Stampa da zero un PNG solido


11

Input : un colore esadecimale RGBA c(es. FFFF00FF) E un numero intero> 0 e <1000 n(es. 200).

Output : byte grezzi di un file PNG in modo tale che quando l'output viene salvato in un file e aperto in un visualizzatore di immagini, viene visualizzata un'immagine nper nimmagine riempita con il colore c.

Specifica : il tuo programma dovrebbe produrre esattamente :

  • un'intestazione PNG ( 89504E470D0A1A0Ain esadecimale)
  • un IHDRpezzo contenente queste specifiche:
    • larghezza: l'input precedente n
    • altezza: l'input precedente n
    • profondità bit: 8( RGBA)
    • tipo di colore: 6(truecolor con alfa)
    • metodo di compressione: 0
    • metodo di filtro: 0
    • metodo di interlacciamento: 0
  • uno o più IDATblocchi contenenti i dati dell'immagine (un'immagine solida di colore precedentemente inserita c); può essere compresso o non compresso
  • un IENDpezzo fine di immagine

Ulteriori dettagli disponibili su Wikipedia , sul sito W3 o tramite una ricerca su Google.

Restrizioni :

  • Non è possibile utilizzare librerie di immagini o funzioni progettate per funzionare con immagini di qualsiasi tipo.
  • Il programma deve essere eseguito in meno di 3 minuti e generare un file inferiore a 10 MB per tutti gli input (controllo di integrità).
  • Questo è , quindi vincerà il codice più breve in byte!

Dici che il file può essere completamente non compresso, ma che deve essere inferiore a 30 kB per tutti gli input. Un 999x999file ha più di 30720 pixel, quindi sembra contraddittorio.
Peter Taylor,

@PeterTaylor Hm, per qualche ragione ho pensato che 30 KB fosse più che sufficiente. Non so cosa stavo pensando ... modificato. (E ho solo detto che puoi o meno usare la compressione; qualunque cosa tu voglia.)
Maniglia della porta

Larghezza: 4 byte Altezza: 4 byte Profondità bit: 1 byte Tipo di colore: 1 byte Metodo di compressione: 1 byte Metodo di filtro: 1 byte Metodo di interlacciamento: 1 byte
technosaurus

@technosaurus ... um, cosa?
Maniglia della porta

1
Il tuo esempio non è corretto: profondità bit: 8 (RRGGBBAA). La profondità di bit 8 è (RGBA) non (RRGGBBAA).
Glenn Randers-Pehrson,

Risposte:


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

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

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 compressper 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 :

  • Funzione k: è packpossibile rimuovere una chiamata di utilizzando il modello Na*Nper il primo packnella funzione.

  • packmodello NNCVcon quattro valori ottimizza NNC3ncon sei valori.

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 $iutilizzando 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 -pstampa 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 Subper la prima riga e Upper 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::Zlibpuò essere sintonizzato; il livello di compressione più alto può essere impostato da un'opzione aggiuntiva per il livello di compressione in funzione compressal costo di due byte:

    compress ..., 9;

    La dimensione del file di esempio yellow200.pngsenza filtro diminuisce da 832 byte a 472 byte. Applicata all'esempio con Subfiltro, la dimensione del file si riduce da 560 byte a 445 byte ( pngcrush -brutenon è possibile comprimere ulteriormente).


Ottima risposta (come sempre), ma il golf può andare oltre - ottengo 202, + 1 per -p. Oltre alle intuizioni in risposta di Michael ( NA*Ne NNCVmodelli), - String::CRC32esportazioni per impostazione predefinita, y///c-4è OK, CH*modello, $iessere andato, \cZ, bareword sono OK, -pe / /;luoghi argomenti in Prematch e postmatch. Mi chiedo se ho perso qualcosa e il punteggio può scendere al di sotto di 200 :)
user2846289,

1
@VadimR: Mille grazie per i suggerimenti utili. Potrei anche giocare a golf ulteriormente usando use Compress::Zlib;e ottenuto ≈ 10% inferiore a 200.
Heiko Oberdiek

5

PHP 214

Non sono un esperto di PHP, c'è posto per il golf. I suggerimenti sono benvenuti.

<?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");

Genera un file PNG:

php png.php 20 FFFF00FF > output.png

Genera flusso base64 (incolla il risultato nella barra degli indirizzi del browser)

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

Versione non golfata:

<?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");

Sono le 214 adesso, no? E non riesco a ottenere un'immagine corretta da entrambe le versioni golf e non golf, ma non ho esperienza PHP, quindi se funziona per tutti gli altri, sono io che sto sbagliando.
user2846289,

1
@VadimR, sì 214, hai ragione. Ho controllato, l'immagine generata è valida per me.
Michael M.,

Per me (ho testato con PHP 5.4.27.0), l'immagine è corta di 4 byte - non si dovrebbe aggiungere adler-32 a dati sgonfiati? IE e Chrome sono felici di visualizzare l'immagine così com'è, FF no. Anche diverse app si comportano diversamente con questa immagine.
user2846289,

4

Python, 252 byte

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")

Questo script prende input da argv. Esegui questo script dalla riga di comando, ad esempiopython 27086.py deadbeef 999

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.