Perl, 181
/ /;use String::CRC32;use Compress::Zlib;sub k{$_=pop;pack'Na*N',y///c-4,$_,crc32$_}$_="\x89PNG\r\n\cZ\n".k(IHDR.pack NNCV,$',$',8,6).k(IDAT.compress pack('CH*',0,$`x$')x$').k IEND
La dimensione è di 180 byte ed -p
è necessaria l' opzione (+1). Il punteggio è quindi 181.
Gli argomenti vengono forniti tramite STDIN in una riga, separati da uno spazio, il colore come valore esadecimale (16 caratteri) e il numero di pixel per larghezza / altezza, ad esempio:
echo "FFFF00FF 200" | perl -p solidpng.pl >yellow200.png
La dimensione del file è 832 byte. L'immagine di dimensioni massime (n = 999) con lo stesso colore ha 6834 byte (molto al di sotto di 10 MB).
La soluzione utilizza due librerie:
use Digest::CRC crc32;
per i valori CRC32 alle estremità del blocco.
use IO::Compress::Deflate deflate;
per comprimere i dati dell'immagine.
Entrambe le librerie non sono correlate alle immagini.
Ungolfed:
# Perl option "-p" adds the following around the program:
# LINE:
# while (<>) {
# ... # the program goes here
# } continue {
# print or die "-p destination: $!\n";
/ /; # match the separator of the arguments in the input line
# first argument, color in hex: $`
# second argument, width/height: $' #'
# load the libraries for the CRC32 fields and the data compression
use String::CRC32;
use Compress::Zlib;
# function that generates a PNG chunk:
# N (4 bytes, big-endian: data length
# N: chunk type
# a* (binary data): data
# N: CRC32 of chunk type and data
sub k {
$_ = pop; # chunk data including chunk type and
# excluding length and CRC32 fields
pack 'Na*N',
y///c - 4, # chunk length #/
# netto length without length, type, and CRC32 fields
$_, # chunk type and data
crc32($_) # checksum field
}
$_ = # $_ is printed by option "-p".
"\x89PNG\r\n\cZ\n" # PNG header
# IHDR chunk: image header with
# width, height,
# bit depth (8), color type (6),
# compresson method (0), filter method (0), interlace method (0)
. k('IHDR' . pack NNCV, $', $', 8, 6)
# IDAT chunk: image data
. k('IDAT' .
compress # compress/deflate data
pack('CH*', # scan line with filter byte
0, # filter byte: None
($` x $') # pixel data for one scan line #'`
) x $' # n lines #'
)
# IHDR chunk: image end
. k('IEND');
Le modifiche
use IO::Compress::Deflate':all';
è sostituito da use Compress::Zlib;
. Quest'ultimo esporta la funzione deflate compress
per impostazione predefinita. La funzione non necessita di riferimenti come argomenti e restituisce direttamente il risultato. Ciò consente di eliminare la variabile $o
.
Grazie per la risposta di Michael :
Grazie per il commento di VadimR con molti consigli:
use String::CRC32;
è più corto di use Digest::CRC crc32;
.
y///c-4
è più corto di -4+y///c
.
- La linea di scansione è ora costruita dal modello
CH*
con la ripetizione nel valore.
- Rimozione
$i
utilizzando un riferimento di valore.
- Parole nude invece di stringhe per i tipi di blocchi.
- Le opzioni ora vengono lette abbinando una riga di input STDIN (opzione
-p
) con la corrispondenza del separatore di spazio / /
. Quindi viene inserita la prima opzione e viene inserito $`
il secondo argomento $'
.
- L'opzione
-p
stampa anche automaticamente $_
.
"\cZ"
è più corto di "\x1a"
.
Migliore compressione
A scapito della dimensione del codice, i dati dell'immagine possono essere ulteriormente compressi, se viene applicato il filtro.
Dimensione file non filtrata per FFFF0FF
200
: 832 byte
Filtro Sub
(differenze di pixel orizzontali): 560 byte
$i = ( # scan line:
"\1" # filter "Sub"
. pack('H*',$c) # first pixel in scan line
. ("\0" x (4 * $n - 4)) # fill rest of line with zeros
) x $n; # $n scan lines
Filtro Sub
per la prima riga e Up
per le restanti righe: 590 byte
$i = # first scan line
"\1" # filter "Sub"
. pack('H*',$c) # first pixel in scan line
. ("\0" x (4 * $n - 4)) # fill rest of line with zeros
# remaining scan lines
. (
"\2" # filter "Up"
. "\0" x (4 * $n) # fill rest of line with zeros
) x ($n - 1);
Prima riga non filtrata, quindi filtro Up
: 586 byte
$i = # first scan line
pack('H*', ("00" . ($c x $n))) # scan line with filter byte: none
# remaining scan lines
. (
"\2" # filter "Up"
. "\0" x (4 * $n) # fill rest of line with zeros
) x ($n - 1);
Inoltre Compress::Zlib
può essere sintonizzato; il livello di compressione più alto può essere impostato da un'opzione aggiuntiva per il livello di compressione in funzione compress
al costo di due byte:
compress ..., 9;
La dimensione del file di esempio yellow200.png
senza filtro diminuisce da 832 byte a 472 byte. Applicata all'esempio con Sub
filtro, la dimensione del file si riduce da 560 byte a 445 byte ( pngcrush -brute
non è possibile comprimere ulteriormente).
999x999
file ha più di 30720 pixel, quindi sembra contraddittorio.