Salida de un PNG sólido desde cero

11

Entrada : un color hexadecimal RGBA c(ej. FFFF00FF) Y un entero> 0 y <1000 n(ej. 200).

Salida : Raw bytes de un archivo PNG de tal manera que cuando la salida se guarda en un archivo y se abre en un visor de imágenes, una npor nimagen llena de color cen la pantalla.

Especificación : Su programa debería generar exactamente :

  • un encabezado PNG ( 89504E470D0A1A0Aen hexadecimal)
  • un IHDRfragmento que contiene estas especificaciones:
    • ancho: la entrada anterior n
    • altura: la entrada anterior n
    • profundidad de bits: 8( RGBA)
    • tipo de color: 6(truecolor con alfa)
    • método de compresión: 0
    • método de filtro: 0
    • método entrelazado: 0
  • uno o más IDATfragmentos que contienen los datos de la imagen (una imagen sólida de color ingresada previamente c); puede estar comprimido o sin comprimir
  • un IENDfragmento de imagen final

Más detalles disponibles en Wikipedia , en el sitio W3 , o mediante una búsqueda en Google.

Restricciones :

  • No puede utilizar ninguna biblioteca de imágenes o funciones diseñadas para trabajar con imágenes de ningún tipo.
  • Su programa debe ejecutarse en menos de 3 minutos y generar un archivo de menos de 10 MB para todas las entradas (verificación de sanidad).
  • Este es el , ¡así que el código más corto en bytes ganará!
Pomo de la puerta
fuente
Usted dice que el archivo puede estar completamente descomprimido, pero que debe tener menos de 30kB para todas las entradas. Un 999x999archivo tiene más de 30720 píxeles, por lo que parece contradictorio.
Peter Taylor
@PeterTaylor Hm, por alguna razón pensé que 30 KB era más que suficiente. No sé lo que estaba pensando ... editado. (Y solo dije que puede o no usar compresión; lo que quiera).
Pomo de la puerta
Ancho: 4 bytes Altura: 4 bytes Profundidad de bits: 1 byte Tipo de color: 1 byte Método de compresión: 1 byte Método de filtro: 1 byte Método de entrelazado: 1 byte
technosaurus
@technosaurus ... um, ¿qué?
Pomo de la puerta
1
Su ejemplo no es correcto: profundidad de bits: 8 (RRGGBBAA). La profundidad de bits 8 es (RGBA) no (RRGGBBAA).
Glenn Randers-Pehrson

Respuestas:

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

El tamaño es de 180 bytes y -pse necesita la opción (+1). La puntuación es entonces 181.

Los argumentos se proporcionan a través de STDIN en una línea, separados por un espacio, el color como valor hexadecimal (16 caracteres) y el número de píxeles para ancho / alto, por ejemplo:

 echo "FFFF00FF 200" | perl -p solidpng.pl >yellow200.png

yellow200.png

El tamaño del archivo es de 832 bytes. La imagen de tamaño máximo (n = 999) con el mismo color tiene 6834 bytes (muy por debajo de 10 MB).

La solución usa dos bibliotecas:

  • use Digest::CRC crc32; para los valores CRC32 en el fragmento finaliza.
  • use IO::Compress::Deflate deflate; para comprimir los datos de la imagen.

Ambas bibliotecas no están relacionadas con las imágenes.

Sin golf:

# 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');

Ediciones

  • use IO::Compress::Deflate':all';se sustituye por use Compress::Zlib;. Este último exporta la función desinflar compresspor defecto. La función no necesita referencias como argumentos y también devuelve el resultado directamente. Eso permite deshacerse de la variable $o.

Gracias por la respuesta de Michael :

  • Función k: packse puede eliminar una llamada de mediante el uso de la plantilla Na*Npara el primero packen la función.

  • packLa plantilla NNCVcon cuatro valores se optimiza NNC3ncon seis valores.

Gracias por el comentario de VadimR con muchos consejos:

  • use String::CRC32;es más corto que use Digest::CRC crc32;.
  • y///c-4es más corto que -4+y///c.
  • La línea de exploración ahora está construida por la plantilla CH*con la repetición en el valor.
  • Eliminación de $imediante el uso de una referencia de valor.
  • Palabras desnudas en lugar de cadenas para los tipos de fragmentos.
  • Las opciones ahora se leen haciendo coincidir una línea de entrada STDIN (opción -p) con la coincidencia del separador de espacio / /. Entonces $`entra la primera opción y entra el segundo argumento $'.
  • La opción -ptambién imprime automáticamente $_.
  • "\cZ"es más corto que "\x1a".

Mejor compresión

A costa del tamaño del código, los datos de la imagen pueden comprimirse aún más, si se aplica el filtrado.

  • Tamaño de archivo sin filtrar para FFFF0FF 200: 832 bytes

  • Filtro Sub(diferencias de píxeles horizontales): 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
    
  • Filtro Subpara la primera línea y Uppara las líneas restantes: 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);
    
  • Primera línea sin filtrar, luego filtro 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);
    
  • También Compress::Zlibse puede sintonizar; El nivel de compresión más alto se puede establecer mediante una opción adicional para el nivel de compresión en función compressa un costo de dos bytes:

    compress ..., 9;

    El tamaño del archivo de ejemplo yellow200.pngsin filtrado disminuye de 832 bytes a 472 bytes. Aplicado al ejemplo con Subfiltro, el tamaño del archivo se reduce de 560 bytes a 445 bytes ( pngcrush -bruteno se puede comprimir más).

Heiko Oberdiek
fuente
Gran respuesta (como siempre), pero el golf puede ir más allá: obtengo 202, + 1 por -p. Además de las ideas en la respuesta de Michael ( NA*Ny las NNCVplantillas), - las String::CRC32exportaciones de forma predeterminada, y///c-4está bien, la CH*plantilla, $idesaparece \cZ, las palabras vacías están bien -py / /;coloca los argumentos en prematch y postmatch. Me pregunto si me perdí algo y la puntuación puede ser inferior a 200 :)
user2846289
1
@VadimR: Muchas gracias por los consejos útiles. Incluso podría jugar más golf usando use Compress::Zlib;y obtuve ≈ 10% por debajo de 200.
Heiko Oberdiek
5

PHP 214

No soy un experto en PHP, hay lugar para jugar al golf. Las propinas son bienvenidas.

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

Generar un archivo PNG:

php png.php 20 FFFF00FF > output.png

Genere flujo base64 (pegue el resultado en la barra de direcciones de su navegador)

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

Versión sin golf:

<?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.
fuente
Ahora son las 214, ¿no? Y no puedo obtener una imagen correcta de las versiones con y sin golf, pero no tengo experiencia en PHP, así que si funciona para todos los demás, soy yo quien lo hace mal.
usuario2846289
1
@VadimR, sí 214, tienes razón. Lo he comprobado, la imagen generada es válida para mí.
Michael M.
Para mí (pruebo con PHP 5.4.27.0), la imagen tiene 4 bytes cortos, ¿no debería agregarse adler-32 a los datos desinflados? IE y Chrome están felices de mostrar la imagen como está, FF no lo está. Las diferentes aplicaciones también se comportan de manera diferente con esta imagen.
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")

Este script toma entrada de argv. Ejecute este script desde la línea de comando, comopython 27086.py deadbeef 999

Bocadillo
fuente