Saída de um PNG sólido a partir do zero


11

Entrada : uma cor hexadecimal RGBA c(ex. FFFF00FF) E um número inteiro> 0 e <1000 n(ex. 200).

Saída : bytes brutos de um arquivo PNG, de modo que, quando a saída é salva em um arquivo e aberta em um visualizador de imagens, uma imagem npor npreenchida com a cor cé exibida.

Especificação : Seu programa deve gerar exatamente :

  • um cabeçalho PNG ( 89504E470D0A1A0Aem hexadecimal)
  • um IHDRpedaço contendo estas especificações:
    • width: a entrada anterior n
    • height: a entrada anterior n
    • profundidade de bits: 8( RGBA)
    • tipo de cor: 6(truecolor com alfa)
    • método de compressão: 0
    • método de filtro: 0
    • método entrelaçado: 0
  • um ou mais IDATpedaços contendo os dados da imagem (uma imagem sólida de cor inserida anteriormente c); pode ser compactado ou descomprimido
  • uma IENDparte final da imagem

Mais detalhes disponíveis na Wikipedia , no site W3 ou através de uma pesquisa no Google.

Restrições :

  • Você não pode usar nenhuma biblioteca de imagens ou funções projetadas para trabalhar com imagens de qualquer tipo.
  • Seu programa deve ser executado em menos de 3 minutos e gerar um arquivo com menos de 10 MB para todas as entradas (verificação de integridade).
  • Isso é , então o código mais curto em bytes vencerá!

Você diz que o arquivo pode ser totalmente descompactado, mas deve estar abaixo de 30kB para todas as entradas. Um 999x999arquivo tem mais de 30720 pixels, o que parece contraditório.
Peter Taylor

@ PeterTaylor Hm, por algum motivo, pensei que 30 KB era mais do que suficiente. Não sei o que eu estava pensando ... editado. (E eu apenas disse que você pode ou não usar a compactação; o que quiser.)
Maçaneta da porta

Largura: 4 bytes Altura: 4 bytes Profundidade do bit: 1 byte Tipo de cor: 1 byte Método de compactação: 1 byte Método de filtro: 1 byte Método de entrelaçamento: 1 byte
technosaurus

@technosaurus ... hum, o que?
Maçaneta

1
Seu exemplo não está correto: profundidade de bits: 8 (RRGGBBAA). A profundidade de bits 8 é (RGBA) não (RRGGBBAA).
Glenn Randers-Pehrson

Respostas:


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

O tamanho é de 180 bytes e a opção -pé necessária (+1). A pontuação é então 181.

Os argumentos são dados via STDIN em uma linha, separados por um espaço, a cor como valor hexadecimal (16 caracteres) e o número de pixels para largura / altura, por exemplo:

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

yellow200.png

O tamanho do arquivo é 832 bytes. A imagem de tamanho máximo (n = 999) da mesma cor possui 6834 bytes (muito abaixo de 10 MB).

A solução usa duas bibliotecas:

  • use Digest::CRC crc32; para os valores de CRC32 nas extremidades dos blocos.
  • use IO::Compress::Deflate deflate; para comprimir os dados da imagem.

Ambas as bibliotecas não estão relacionadas a imagens.

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

Editar% s

  • use IO::Compress::Deflate':all';é substituído por use Compress::Zlib;. O último exporta a função de desinflar compresspor padrão. A função não precisa de referências como argumentos e também retorna o resultado diretamente. Isso permite livrar-se da variável $o.

Obrigado pela resposta de Michael :

  • Função k: uma chamada de packpode ser removida usando o modelo Na*Npara o primeiro packna função.

  • packmodelo NNCVcom quatro valores otimiza NNC3ncom seis valores.

Obrigado pelo comentário do VadimR com muitas dicas:

  • use String::CRC32;é mais curto que use Digest::CRC crc32;.
  • y///c-4é mais curto que -4+y///c.
  • A linha de varredura agora é construída pelo modelo CH*com a repetição no valor.
  • Remoção $iusando uma referência de valor.
  • Palavras nuas em vez de cadeias para os tipos de partes.
  • As opções agora são lidas combinando uma linha de entrada STDIN (opção -p) com o separador de espaço / /. Então a primeira opção está dentro $`e o segundo argumento entra $'.
  • A opção -ptambém imprime automaticamente $_.
  • "\cZ"é mais curto que "\x1a".

Melhor compressão

À custa do tamanho do código, os dados da imagem podem ser ainda mais compactados, se a filtragem for aplicada.

  • Tamanho do arquivo não filtrado para FFFF0FF 200: 832 bytes

  • Filtro Sub(diferenças de pixel horizontal): 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
  • Filtre Suba primeira linha e Upas linhas 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);
  • Primeira linha não filtrada e depois filtrar 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);
    
  • Também Compress::Zlibpode ser ajustado; o nível mais alto de compactação pode ser definido por uma opção adicional para o nível de compactação em função compressao custo de dois bytes:

    compress ..., 9;

    O tamanho do arquivo do exemplo yellow200.pngsem filtragem diminui de 832 bytes para 472 bytes. Aplicado ao exemplo com Subfiltro, o tamanho do arquivo diminui de 560 bytes para 445 bytes ( pngcrush -brutenão é possível compactar mais).


Ótima resposta (como sempre), mas o golfe pode ir além - recebo 202, + 1 por -p. Além das informações da resposta de Michael ( NA*Ne NNCVmodelos), - as String::CRC32exportações por padrão y///c-4estão OK, o CH*modelo $idesapareceu \cZ, as palavras de barras estão OK -pe / /;coloca os argumentos em prematura e pós-correspondência. Pergunto-me se eu perdi alguma coisa e pontuação pode ficar abaixo de 200 :)
user2846289

1
@VadimR: Muito obrigado pelas dicas úteis. Eu poderia até mesmo golf-lo ainda mais usando use Compress::Zlib;e tem ≈ 10% abaixo de 200.
Heiko Oberdiek

5

PHP 214

Eu não sou um especialista em PHP, há lugar para jogar golfe. Dicas são bem-vindas.

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

Gere um arquivo PNG:

php png.php 20 FFFF00FF > output.png

Gere fluxo base64 (cole o resultado na barra de endereços do navegador)

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

Versão não destruída:

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

São 214 agora, não é? E não consigo obter uma imagem correta das versões com e sem golf, mas não tenho experiência com PHP; portanto, se funcionar para todos os outros, sou eu que estou fazendo errado.
User2846289

1
@VadimR, sim 214, você está certo. Eu verifiquei, a imagem gerada é válida para mim.
Michael M.

Para mim (eu testei com o PHP 5.4.27.0), a imagem tem 4 bytes de comprimento - o adler-32 não deve ser anexado a dados desinflados? O IE e o Chrome ficam felizes em exibir a imagem como está, o FF não. Aplicativos diferentes também se comportam de maneira diferente com esta imagem.
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 recebe entrada do argv. Execute esse script na linha de comando, comopython 27086.py deadbeef 999

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.