Geben Sie ein solides PNG von Grund auf neu aus

11

Eingabe : Eine RGBA-Hex-Farbe c(z. B. FFFF00FF) und eine Ganzzahl> 0 und <1000 n(z. B. 200).

Ausgabe : Rohes Bytes einer PNG - Datei , so dass , wenn die Ausgabe in eine Datei und öffnen in einem Bildbetrachter gespeichert wird, ein ndurch nBild mit der Farbe gefüllt cwird angezeigt.

Spezifikation : Ihr Programm sollte genau Folgendes ausgeben :

  • ein PNG-Header ( 89504E470D0A1A0Ain hex)
  • ein IHDRStück mit folgenden Angaben:
    • width: die vorherige Eingabe n
    • Höhe: die vorherige Eingabe n
    • Bittiefe: 8( RGBA)
    • 6Farbtyp : (Echtfarbe mit Alpha)
    • Kompressionsverfahren: 0
    • Filtermethode: 0
    • Interlace-Methode: 0
  • einen oder mehrere IDATBlöcke, die die Bilddaten enthalten (ein festes Bild der zuvor eingegebenen Farbe c); kann komprimiert oder unkomprimiert sein
  • ein IENDBildende Chunk

Weitere Details finden Sie auf Wikipedia , auf der W3-Website oder über eine Google-Suche.

Einschränkungen :

  • Sie dürfen keine Bildbibliotheken oder Funktionen verwenden, die für die Arbeit mit Bildern jeglicher Art entwickelt wurden.
  • Ihr Programm muss in weniger als 3 Minuten ausgeführt werden und für alle Eingaben eine Datei unter 10 MB ausgeben (Sanity Check).
  • Dies ist , also gewinnt der kürzeste Code in Bytes!
Türknauf
quelle
Sie sagen, dass die Datei möglicherweise vollständig dekomprimiert ist, dann aber für alle Eingaben unter 30 KB liegen muss. Eine 999x999Datei hat mehr als 30720 Pixel, das scheint also widersprüchlich.
Peter Taylor
@ PeterTaylor Hm, aus irgendeinem Grund dachte ich, dass 30 KB mehr als genug sind. Ich weiß nicht was ich dachte ... bearbeitet. (Und ich sagte nur, dass Sie Komprimierung verwenden können oder nicht; was auch immer Sie wollen.)
Türknauf
Breite: 4 Bytes Höhe: 4 Bytes Bittiefe: 1 Byte Farbtyp: 1 Byte Komprimierungsmethode: 1 Byte
Filtermethode
@technosaurus ... ähm, was?
Türknauf
1
Ihr Beispiel ist nicht korrekt: Bittiefe: 8 (RRGGBBAA). Die Bittiefe 8 ist (RGBA) nicht (RRGGBBAA).
Glenn Randers-Pehrson

Antworten:

6

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 -pwird 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

gelb200.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 compressstandardmäß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 :

  • Funktion k: Ein Aufruf von packkann mithilfe der Vorlage Na*Nfür den ersten packin der Funktion entfernt werden.

  • packVorlage NNCVmit vier Werten optimiert NNC3nmit sechs Werten.

Vielen Dank für den Kommentar von VadimR mit vielen Tipps:

  • use String::CRC32;ist kürzer als use Digest::CRC crc32;.
  • y///c-4ist kürzer als -4+y///c.
  • Die Scanlinie wird jetzt von der Vorlage CH*mit der Wiederholung im Wert erstellt.
  • Entfernung $iunter 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 -pdruckt 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 Subfür die erste Zeile und Upfü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::Zlibkann abgestimmt werden; Die höchste Komprimierungsstufe kann durch eine zusätzliche Option für die Komprimierungsstufe in Funktion compressauf Kosten von zwei Bytes festgelegt werden:

    compress ..., 9;

    Die Dateigröße des Beispiels yellow200.pngohne Filterung verringert sich von 832 Byte auf 472 Byte. Auf das Beispiel mit SubFilter pngcrush -bruteangewendet , wird die Dateigröße von 560 Byte auf 445 Byte verkleinert ( kann nicht weiter komprimiert werden).

Heiko Oberdiek
quelle
Tolle Antwort (wie immer), aber Golfen kann noch weiter gehen - ich bekomme 202, + 1 für -p. Zusätzlich zu den Einsichten in Michaels Antwort ( NA*Nund NNCVVorlagen) ist - String::CRC32Exportieren standardmäßig y///c-4OK, CH*Vorlage, $iweg sein \cZ, Barewords sind OK -pund / /;Argumente in Prematch und Postmatch. Ich frage mich, ob ich etwas verpasst habe und die Punktzahl unter 200 fallen kann :)
user2846289
1
@VadimR: Vielen Dank für die nützlichen Tipps. Ich könnte es sogar weiter spielen, indem ich use Compress::Zlib;≈ 10% unter 200
bekomme
5

PHP 214

Ich bin kein PHP-Experte, es gibt Platz zum Golfen. Tipps sind willkommen.

<?function c($d){echo pack("Na*N",strlen($d)-4,$d,crc32($d));}echo"\x89PNG\r\n\x1a\n";c("IHDR".pack("NNCV",$n=$argv[1],$n,8,6));c("IDATx^".gzdeflate(str_repeat("\0".str_repeat(hex2bin($argv[2]),$n),$n)));c("IEND");

Generieren Sie eine PNG-Datei:

php png.php 20 FFFF00FF > output.png

Generieren Sie einen base64-Stream (fügen Sie das Ergebnis in die Adressleiste Ihres Browsers ein)

echo "data:image/png;base64,`php png.php 200 0000FFFF | base64`"

Ungolfed Version:

<?php 

//function used to create a PNG chunck
function chunck($data) {
  return pack("Na*N", //write a big-endian integer, a string and another integer
    strlen($data)-4,     //size of data minus the 4 char of the type
    $data,               //data
    crc32($data));       //compute CRC of data
}

//png header
echo "\x89PNG\r\n\x1a\n"; 

//IHDR chunck
echo chunck("IHDR".pack("NNCV", //2 big-endian integer, a single byte and a little-endian integer
                   $n=$argv[1], $n,
                   8, 6)); //6 also write 3 zeros (little endian integer)

//IDAT chunck
//create a binary string of the raw image, each line begin with 0 (none filter)
$d = str_repeat("\0".str_repeat(hex2bin($argv[2]),$n),$n);
echo chunck("IDATx^".
       gzdeflate($d)); //compress raw data

//IEND chunck
echo chunck("IEND");
Michael M.
quelle
Es ist jetzt 214, nicht wahr? Ich kann kein korrektes Bild sowohl von Golf- als auch von Nicht-Golf-Versionen erhalten, aber ich habe keine PHP-Erfahrung. Wenn es also für alle anderen funktioniert, mache ich es falsch.
user2846289
1
@VadimR, ja 214, du hast recht. Ich habe überprüft, das erzeugte Bild ist für mich gültig.
Michael M.
Für mich (ich teste mit PHP 5.4.27.0) ist das Bild 4 Byte kurz - sollte adler-32 nicht an entleerte Daten angehängt werden? IE und Chrome zeigen das Bild gerne so an, wie es ist, FF nicht. Auch verschiedene Apps verhalten sich bei diesem Bild unterschiedlich.
user2846289
4

Python, 252 Bytes

import struct,sys,zlib as Z
P=struct.pack
A=sys.argv
I=lambda i:P(">I",i)
K=lambda d:I(len(d)-4)+d+I(Z.crc32(d)&(2<<31)-1)
j=int(A[2])
print "\x89PNG\r\n\x1A\n"+K("IHDR"+P(">IIBI",j,j,8,6<<24))+K("IDAT"+Z.compress(("\0"+I(int(A[1],16))*j)*j))+K("IEND")

Dieses Skript nimmt Eingaben von argv entgegen. Führen Sie dieses Skript über die Befehlszeile auspython 27086.py deadbeef 999

Snack
quelle