Wyjście pełnego PNG od zera


11

Dane wejściowe : kolor szesnastkowy RGBA c(np. FFFF00FF) I liczba całkowita> 0 i <1000 n(np. 200).

Dane wyjściowe : surowe bajty pliku PNG, tak że gdy dane wyjściowe są zapisywane w pliku i otwierane w przeglądarce zdjęć, wyświetlany njest nobraz wypełniony kolorem c.

Specyfikacja : Twój program powinien wypisywać dokładnie :

  • nagłówek PNG ( 89504E470D0A1A0Aszesnastkowo)
  • IHDRfragment zawierający następujące dane:
    • szerokość: poprzednie wejście n
    • wysokość: poprzednie wejście n
    • głębia bitowa: 8( RGBA)
    • rodzaj koloru: 6(truecolor z alfa)
    • metoda kompresji: 0
    • metoda filtrowania: 0
    • metoda z przeplotem: 0
  • jeden lub więcej IDATfragmentów zawierających dane obrazu (wcześniej wprowadzono jednolity obraz koloru c); może być skompresowany lub nieskompresowany
  • IENDfragment końcowy obraz

Dalsze szczegóły dostępne są w Wikipedii , na stronie W3 lub w wyszukiwarce Google.

Ograniczenia :

  • Nie wolno używać bibliotek obrazów ani funkcji zaprojektowanych do pracy z obrazami dowolnego rodzaju.
  • Twój program musi zostać uruchomiony w czasie krótszym niż 3 minuty i wygenerować plik poniżej 10 MB dla wszystkich danych wejściowych (kontrola poprawności).
  • To jest , więc wygra najkrótszy kod w bajtach!

Mówisz, że plik może być całkowicie nieskompresowany, ale wtedy musi mieć mniej niż 30 kB dla wszystkich danych wejściowych. 999x999Plik ma więcej niż 30720 pikseli, dzięki czemu wydaje się wewnętrznie sprzeczne.
— Peter Taylor

@PeterTaylor Hm, z jakiegoś powodu myślałem, że 30 KB to więcej niż wystarcza. Nie wiem o czym myślałem ... edytowałem. (I powiedziałem tylko, że możesz, ale nie musisz, stosować kompresję; cokolwiek chcesz.)
— Klamka

Szerokość: 4 bajty Wysokość: 4 bajty Głębokość bitów: 1 bajt Rodzaj koloru: 1 bajt Metoda kompresji: 1 bajt Metoda filtrowania: 1 bajt Metoda z przeplotem: 1 bajt
— technozaur

@technosaurus ... um, co?
— Klamka

1
Twój przykład jest niepoprawny: głębia bitowa: 8 (RRGGBBAA). Głębia bitowa 8 to (RGBA) nie (RRGGBBAA).
— Glenn Randers-Pehrson

Odpowiedzi:


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

Rozmiar to 180 bajtów i -ppotrzebna jest opcja (+1). Wynik to wtedy 181.

Argumenty podawane są przez STDIN w linii oddzielonej spacją, kolorem jako wartością szesnastkową (16 znaków) i liczbą pikseli dla szerokości / wysokości, np .:

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

yellow200.png

Rozmiar pliku to 832 bajty. Maksymalny rozmiar obrazu (n = 999) tego samego koloru ma 6834 bajty (znacznie poniżej 10 MB).

Rozwiązanie wykorzystuje dwie biblioteki:

  • use Digest::CRC crc32; dla wartości CRC32 na końcach porcji.
  • use IO::Compress::Deflate deflate; kompresować dane obrazu.

Obie biblioteki nie są powiązane z obrazami.

Nie golfowany:

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

Edycje

  • use IO::Compress::Deflate':all';zastępuje się przez use Compress::Zlib;. Ten ostatni compressdomyślnie eksportuje funkcję deflacji . Funkcja nie potrzebuje referencji jako argumentów, a także zwraca wynik bezpośrednio. To pozwala pozbyć się zmiennej $o.

Dzięki za odpowiedź Michaela :

  • Funkcja k: Wywołanie packmoże zostać usunięte przy użyciu szablonu Na*Ndla pierwszej packfunkcji.

  • packszablon NNCVz czterema wartościami optymalizuje się NNC3nz sześcioma wartościami.

Dzięki za komentarz VadimR z dużą ilością wskazówek:

  • use String::CRC32;jest krótszy niż use Digest::CRC crc32;.
  • y///c-4jest krótszy niż -4+y///c.
  • Linia skanowania jest teraz konstruowana przez szablon CH*z powtórzeniem wartości.
  • Usunięcie $iza pomocą odwołania do wartości.
  • Nagie słowa zamiast ciągów dla typów porcji.
  • Opcje są teraz odczytywane poprzez dopasowanie wiersza wejściowego STDIN (opcja -p) z dopasowaniem separatora spacji / /. Potem jest pierwsza opcja $`i wchodzi drugi argument $'.
  • Opcja -pdrukuje również automatycznie $_.
  • "\cZ"jest krótszy niż "\x1a".

Lepsza kompresja

Kosztem rozmiaru kodu dane obrazu mogą być dalej kompresowane, jeśli zastosowane jest filtrowanie.

  • Rozmiar niefiltrowanego pliku dla FFFF0FF 200: 832 bajtów

  • Filtr Sub(różnice poziomych pikseli): 560 bajtów

    $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
  • Filtruj Subdla pierwszego wiersza i Updla pozostałych wierszy: 590 bajtów

    $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);
  • Najpierw niefiltrowana linia, a następnie filtruj Up: 586 bajtów

    $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);
    
  • Również Compress::Zlibmożna dostroić; najwyższy poziom kompresji można ustawić za pomocą dodatkowej opcji dla poziomu kompresji działającego compresskosztem dwóch bajtów:

    compress ..., 9;

    Rozmiar pliku yellow200.pngbez filtrowania zmniejsza się z 832 bajtów do 472 bajtów. W przypadku przykładu z Subfiltrem rozmiar pliku zmniejsza się z 560 bajtów do 445 bajtów ( pngcrush -brutenie można dalej kompresować).


Świetna odpowiedź (jak zawsze), ale golf może iść dalej - dostaję 202, + 1 za -p. Oprócz wglądu w odpowiedź Michaela ( NA*Ni NNCVszablony): - String::CRC32domyślnie eksport y///c-4jest OK, CH*szablon $izniknął \cZ, słowa bez słów są w porządku -pi / /;umieszcza argumenty w przedmeczu i postmatchu. Zastanawiam się, czy coś przeoczyłem, a wynik może
— spaść

1
@VadimR: Wielkie dzięki za przydatne wskazówki. Mogę nawet use Compress::Zlib;
— zagrać w

5

PHP 214

Nie jestem ekspertem od PHP, jest miejsce na golfa. Wskazówki są mile widziane.

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

Wygeneruj plik PNG:

php png.php 20 FFFF00FF > output.png

Wygeneruj strumień base64 (wklej wynik w pasku adresu przeglądarki)

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

Wersja bez golfa:

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

Teraz jest 214, prawda? I nie mogę uzyskać poprawnego obrazu zarówno z wersji golfowej, jak i nie golfowej, ale nie mam doświadczenia w PHP, więc jeśli działa dla wszystkich innych, to robię to źle.
— user2846289

1
@VadimR, tak 214, masz rację. Sprawdziłem, wygenerowany obraz jest dla mnie ważny.
— Michael M.

Dla mnie (testuję w PHP 5.4.27.0) obraz ma 4 bajty długości - czy adler-32 nie powinien być dołączany do danych deflowanych? IE i Chrome chętnie wyświetlają obraz takim, jaki jest, FF nie. Z tym obrazem różne aplikacje również zachowują się inaczej.
— user2846289

4

Python, 252 bajty

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

Ten skrypt pobiera dane wejściowe z argv. Uruchom ten skrypt z wiersza poleceń, nppython 27086.py deadbeef 999

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.