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
Die Größe beträgt 180 Byte und die Option -p
wird benötigt (+1). Punktzahl ist dann 181.
Die Argumente werden über STDIN in einer durch ein Leerzeichen getrennten Zeile, der Farbe als Hex-Wert (16 Zeichen) und der Anzahl der Pixel für Breite / Höhe angegeben, z.
echo "FFFF00FF 200" | perl -p solidpng.pl >yellow200.png
Die Dateigröße beträgt 832 Bytes. Das maximal große Bild (n = 999) mit derselben Farbe hat 6834 Bytes (weit unter 10 MB).
Die Lösung verwendet zwei Bibliotheken:
use Digest::CRC crc32;
für die CRC32-Werte am Block endet.
use IO::Compress::Deflate deflate;
um die Bilddaten zu komprimieren.
Beide Bibliotheken beziehen sich nicht auf Bilder.
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');
Bearbeitungen
use IO::Compress::Deflate':all';
wird ersetzt durch use Compress::Zlib;
. Letzterer exportiert compress
standardmäßig die Deflate-Funktion . Die Funktion benötigt keine Referenzen als Argumente und gibt das Ergebnis auch direkt zurück. Das erlaubt es, Variablen loszuwerden $o
.
Danke für Michaels Antwort :
Vielen Dank für den Kommentar von VadimR mit vielen Tipps:
use String::CRC32;
ist kürzer als use Digest::CRC crc32;
.
y///c-4
ist kürzer als -4+y///c
.
- Die Scanlinie wird jetzt von der Vorlage
CH*
mit der Wiederholung im Wert erstellt.
- Entfernung
$i
unter Verwendung einer Wertreferenz.
- Nackte Wörter anstelle von Zeichenfolgen für die Chunk-Typen.
- Optionen werden jetzt gelesen, indem eine STDIN-Eingabezeile (Option
-p
) mit dem Leerzeichen abgeglichen wird / /
. Dann ist die erste Option in $`
und das zweite Argument geht in $'
.
- Option
-p
druckt auch automatisch $_
.
"\cZ"
ist kürzer als "\x1a"
.
Bessere Komprimierung
Auf Kosten der Codegröße können die Bilddaten weiter komprimiert werden, wenn eine Filterung angewendet wird.
Ungefilterte Dateigröße für FFFF0FF
200
: 832 Bytes
Filter Sub
(horizontale Pixeldifferenzen): 560 Bytes
$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
Filter Sub
für die erste Zeile und Up
für die verbleibenden Zeilen: 590 Bytes
$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);
Erst ungefilterte Zeile, dann Filter Up
: 586 Bytes
$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);
Auch Compress::Zlib
kann abgestimmt werden; Die höchste Komprimierungsstufe kann durch eine zusätzliche Option für die Komprimierungsstufe in Funktion compress
auf Kosten von zwei Bytes festgelegt werden:
compress ..., 9;
Die Dateigröße des Beispiels yellow200.png
ohne Filterung verringert sich von 832 Byte auf 472 Byte. Auf das Beispiel mit Sub
Filter pngcrush -brute
angewendet , wird die Dateigröße von 560 Byte auf 445 Byte verkleinert ( kann nicht weiter komprimiert werden).
999x999
Datei hat mehr als 30720 Pixel, das scheint also widersprüchlich.