Produire un PNG solide à partir de zéro

11

Entrée : une couleur hexadécimale RGBA c(ex. FFFF00FF) Et un entier> 0 et <1000 n(ex. 200).

Sortie : Raw octets d'un fichier PNG de sorte que lorsque la sortie est enregistrée dans un fichier et ouvert dans une visionneuse d'images, une npar nimage remplie avec la couleur cest affichée.

Spécification : votre programme doit produire exactement :

  • un en-tête PNG ( 89504E470D0A1A0Aen hexadécimal)
  • un IHDRmorceau contenant ces spécifications:
    • largeur: l'entrée précédente n
    • hauteur: l'entrée précédente n
    • profondeur de bits: 8( RGBA)
    • type de couleur: 6(truecolor avec alpha)
    • méthode de compression: 0
    • méthode de filtrage: 0
    • méthode entrelacée: 0
  • un ou plusieurs IDATmorceaux contenant les données d'image (une image solide de couleur entrée précédemment c); peut être compressé ou non compressé
  • un IENDmorceau de fin d'image

Plus de détails disponibles sur Wikipedia , sur le site W3 , ou via une recherche Google.

Restrictions :

  • Vous ne pouvez pas utiliser de bibliothèques d'images ou de fonctions conçues pour fonctionner avec des images de toute nature.
  • Votre programme doit s'exécuter en moins de 3 minutes et générer un fichier de moins de 10 Mo pour toutes les entrées (vérification de l'intégrité).
  • Il s'agit de , donc le code le plus court en octets gagnera!
Poignée de porte
la source
Vous dites que le fichier peut être entièrement décompressé, mais qu'il doit être inférieur à 30 Ko pour toutes les entrées. Un 999x999fichier a plus de 30720 pixels, ce qui semble contradictoire.
Peter Taylor
@PeterTaylor Hm, pour une raison quelconque, je pensais que 30 Ko était plus que suffisant. Je ne sais pas ce que je pensais ... édité. (Et j'ai seulement dit que vous pouviez ou non utiliser la compression; selon ce que vous vouliez.)
Poignée de porte
Largeur: 4 octets Hauteur: 4 octets Profondeur de bits: 1 octet Type de couleur: 1 octet Méthode de compression: 1 octet Méthode de filtrage: 1 octet Méthode entrelacée: 1 octet
technosaurus
@technosaurus ... euh, quoi?
Poignée de porte
1
Votre exemple n'est pas correct: profondeur de bits: 8 (RRGGBBAA). La profondeur de bits 8 est (RGBA) non (RRGGBBAA).
Glenn Randers-Pehrson,

Réponses:

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 taille est de 180 octets et une option -pest nécessaire (+1). Le score est alors de 181.

Les arguments sont donnés via STDIN dans une ligne, séparés par un espace, la couleur en valeur hexadécimale (16 caractères) et le nombre de pixels pour la largeur / hauteur, par exemple:

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

yellow200.png

La taille du fichier est de 832 octets. L'image de taille maximale (n = 999) avec la même couleur a 6834 octets (bien en dessous de 10 Mo).

La solution utilise deux bibliothèques:

  • use Digest::CRC crc32; pour les valeurs CRC32 aux extrémités du bloc.
  • use IO::Compress::Deflate deflate; pour compresser les données d'image.

Les deux bibliothèques ne sont pas liées aux images.

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

Modifications

  • use IO::Compress::Deflate':all';est remplacé par use Compress::Zlib;. Ce dernier exporte la fonction de dégonflage compresspar défaut. La fonction n'a pas besoin de références comme arguments et renvoie également le résultat directement. Cela permet de se débarrasser des variables $o.

Merci pour la réponse de Michael :

  • Fonction k: Un appel de packpourrait être supprimé en utilisant un modèle Na*Npour le premier packde la fonction.

  • packmodèle NNCVavec quatre valeurs optimise NNC3navec six valeurs.

Merci pour le commentaire de VadimR avec beaucoup de conseils:

  • use String::CRC32;est plus court que use Digest::CRC crc32;.
  • y///c-4est plus court que -4+y///c.
  • La ligne de balayage est maintenant construite par le modèle CH*avec la répétition dans la valeur.
  • Suppression de $ien utilisant une référence de valeur.
  • Mots nus au lieu de chaînes pour les types de blocs.
  • Les options sont désormais lues en faisant correspondre une ligne d'entrée STDIN (option -p) avec une correspondance avec le séparateur d'espace / /. Ensuite, la première option est entrée $`et le deuxième argument est entré $'.
  • L'option -pimprime également automatiquement $_.
  • "\cZ"est plus court que "\x1a".

Meilleure compression

Au prix de la taille du code, les données d'image peuvent être encore compressées si un filtrage est appliqué.

  • Taille de fichier non filtrée pour FFFF0FF 200: 832 octets

  • Filtre Sub(différences de pixels horizontales): 560 octets

    $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
    
  • Filtre Subpour la première ligne et Uppour les lignes restantes: 590 octets

    $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);
    
  • Première ligne non filtrée, puis filtre Up: 586 octets

    $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);
    
  • Compress::ZlibPeut également être réglé; le niveau de compression le plus élevé peut être défini par une option supplémentaire pour le niveau de compression en fonction compressau prix de deux octets:

    compress ..., 9;

    La taille de fichier de l'exemple yellow200.pngsans filtrage passe de 832 octets à 472 octets. Appliquée à l'exemple avec Subfiltre, la taille du fichier passe de 560 octets à 445 octets ( pngcrush -bruteimpossible de compresser davantage).

Heiko Oberdiek
la source
Excellente réponse (comme toujours), mais le golf peut aller plus loin - j'obtiens 202, + 1 pour -p. En plus des informations contenues dans la réponse de Michael ( NA*Net les NNCVmodèles), - les String::CRC32exportations par défaut y///c-4sont OK, le CH*modèle $iest parti \cZ, les mots nus sont OK -pet / /;placent les arguments en prématch et postmatch. Je me demande si j'ai raté quelque chose et que le score peut descendre en dessous de 200 :)
user2846289
1
@VadimR: Merci beaucoup pour les conseils utiles. Je pourrais même le use Compress::Zlib;
jouer
5

PHP 214

Je ne suis pas un expert en PHP, il y a de la place pour le golf. Les pourboires sont les bienvenus.

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

Générez un fichier PNG:

php png.php 20 FFFF00FF > output.png

Générez un flux base64 (collez le résultat dans la barre d'adresse de votre navigateur)

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

Version non golfée:

<?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.
la source
C'est 214 maintenant, n'est-ce pas? Et, je ne peux obtenir aucune image correcte des versions golfées et non golfées, mais je n'ai aucune expérience PHP, donc si cela fonctionne pour tout le monde, c'est moi qui le fais mal.
user2846289
1
@VadimR, oui 214, vous avez raison. J'ai vérifié, l'image générée est valable pour moi.
Michael M.
Pour moi (je teste avec PHP 5.4.27.0), l'image est courte de 4 octets - ne devrait-on pas ajouter adler-32 aux données dégonflées? IE et Chrome sont heureux d'afficher l'image telle quelle, FF ne l'est pas. Différentes applications se comportent également différemment avec cette image.
user2846289
4

Python, 252 octets

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

Ce script prend l'entrée d'argv. Exécutez ce script à partir de la ligne de commande, commepython 27086.py deadbeef 999

Casse-croûte
la source