Keluarkan PNG padat dari awal

11

Input : Warna hex RGBA c(mis. FFFF00FF) Dan integer> 0 dan <1000 n(mis. 200).

Output : Baku byte dari file PNG sehingga ketika output disimpan ke file dan dibuka di penampil gambar, seorang ndengan ngambar penuh dengan warna cditampilkan.

Spesifikasi : Program Anda harus menampilkan dengan tepat :

  • header PNG ( 89504E470D0A1A0Adalam hex)
  • sebuah IHDRpotongan yang berisi spesifikasi ini:
    • Lebar: input sebelumnya n
    • tinggi: input sebelumnya n
    • kedalaman bit: 8( RGBA)
    • jenis warna: 6(truecolor dengan alpha)
    • metode kompresi: 0
    • metode filter: 0
    • metode interlace: 0
  • satu atau lebih IDATpotongan yang berisi data gambar (gambar padat warna yang sebelumnya diinput c); dapat dikompresi atau tidak dikompresi
  • sebuah IENDpotongan gambar akhir

Rincian lebih lanjut tersedia di Wikipedia , di situs W3 , atau melalui pencarian Google.

Pembatasan :

  • Anda tidak boleh menggunakan perpustakaan gambar atau fungsi apa pun yang dirancang untuk bekerja dengan gambar apa pun.
  • Program Anda harus berjalan dalam waktu kurang dari 3 menit dan menghasilkan file di bawah 10 MB untuk semua input (cek kewarasan).
  • Ini adalah , jadi kode terpendek dalam byte akan menang!
Gagang pintu
sumber
Anda mengatakan bahwa file tersebut mungkin sepenuhnya tidak terkompresi, tetapi kemudian harus di bawah 30kB untuk semua input. Sebuah 999x999file memiliki lebih dari 30720 piksel, sehingga tampaknya saling bertentangan.
Peter Taylor
@ PeterTaylor Hm, untuk beberapa alasan saya pikir 30 KB sudah lebih dari cukup. Tidak tahu apa yang saya pikirkan ... diedit. (Dan saya hanya mengatakan bahwa Anda mungkin atau mungkin tidak menggunakan kompresi; mana yang Anda inginkan.)
Gagang Pintu
Lebar: 4 byte Tinggi: 4 byte Kedalaman bit: 1 byte Jenis warna: 1 byte Metode kompresi: 1 byte Metode filter: 1 byte Metode interlace: 1 byte
technosaurus
@ technosaurus ... um, apa?
Gagang Pintu
1
Contoh Anda tidak benar: kedalaman bit: 8 (RRGGBBAA). Kedalaman bit 8 adalah (RGBA) bukan (RRGGBBAA).
Glenn Randers-Pehrson

Jawaban:

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

Ukurannya 180 byte dan opsi -pdiperlukan (+1). Skornya kemudian 181.

Argumen diberikan melalui STDIN dalam garis, dipisahkan oleh spasi, warna sebagai nilai hex (16 karakter) dan jumlah piksel untuk lebar / tinggi, misalnya:

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

yellow200.png

Ukuran file adalah 832 byte. Gambar berukuran maksimal (n = 999) dengan warna yang sama memiliki 6834 byte (jauh di bawah 10 MB).

Solusinya menggunakan dua perpustakaan:

  • use Digest::CRC crc32; untuk nilai CRC32 di ujung chunk.
  • use IO::Compress::Deflate deflate; untuk mengompres data gambar.

Kedua perpustakaan tidak terkait dengan gambar.

Tidak Disatukan:

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

Suntingan

  • use IO::Compress::Deflate':all';diganti oleh use Compress::Zlib;. Yang terakhir memang mengekspor fungsi deflate compresssecara default. Fungsi tidak memerlukan referensi sebagai argumen dan juga mengembalikan hasilnya secara langsung. Itu memungkinkan untuk menyingkirkan variabel $o.

Terima kasih atas jawaban Michael :

  • Fungsi k: Panggilan packdapat dihapus dengan menggunakan templat Na*Nuntuk yang pertama packdalam fungsi.

  • packtemplate NNCVdengan empat nilai dioptimalkan NNC3ndengan enam nilai.

Terima kasih atas komentar VadimR dengan banyak tips:

  • use String::CRC32;lebih pendek dari use Digest::CRC crc32;.
  • y///c-4lebih pendek dari -4+y///c.
  • Baris pemindaian sekarang dibangun oleh templat CH*dengan pengulangan dalam nilai.
  • Penghapusan $idengan menggunakan referensi nilai.
  • Kata-kata telanjang alih-alih string untuk tipe chunk.
  • Opsi sekarang dibaca dengan mencocokkan jalur input STDIN (opsi -p) dengan mencocokkan pemisah ruang / /. Kemudian opsi pertama ada $`dan argumen kedua masuk $'.
  • Pilihan -pjuga mencetak secara otomatis $_.
  • "\cZ"lebih pendek dari "\x1a".

Kompresi yang lebih baik

Dengan mengorbankan ukuran kode, data gambar dapat dikompresi lebih lanjut, jika penyaringan diterapkan.

  • Ukuran file tanpa filter untuk FFFF0FF 200: 832 bytes

  • Filter Sub(perbedaan piksel horizontal): 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
  • Saring Subuntuk baris pertama dan Upuntuk baris lainnya: 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);
  • Pertama tanpa filter baris, lalu filter 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);
    
  • Juga Compress::Zlibbisa disetel; tingkat kompresi tertinggi dapat diatur dengan opsi tambahan untuk tingkat kompresi berfungsi compressdengan biaya dua byte:

    compress ..., 9;

    Ukuran file misalnya yellow200.pngtanpa penyaringan menurun dari 832 byte menjadi 472 byte. Diterapkan pada contoh dengan Subfilter, ukuran file menyusut dari 560 byte menjadi 445 byte ( pngcrush -brutetidak dapat memampatkan lebih lanjut).

Heiko Oberdiek
sumber
Jawaban bagus (seperti biasa), tapi bermain golf bisa lebih jauh - saya mendapat 202, +1 untuk -p. Selain wawasan dalam jawaban Michael ( NA*Ndan NNCVtemplat), - String::CRC32ekspor secara default, y///c-4tidak apa-apa, CH*templat, $ihilang \cZ,, kata kunci bar OK, -pdan / /;menempatkan argumen ke dalam pra-tanding dan pascas-tanding . Saya ingin tahu apakah saya melewatkan sesuatu dan skornya bisa di bawah 200 :)
user2846289
1
@VadimR: Terima kasih banyak atas tips yang bermanfaat. Saya bahkan bisa bermain golf lebih jauh dengan menggunakan use Compress::Zlib;dan mendapat ≈ 10% di bawah 200.
Heiko Oberdiek
5

PHP 214

Saya bukan ahli PHP, ada tempat untuk bermain golf. Tips disambut.

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

Buat file PNG:

php png.php 20 FFFF00FF > output.png

Hasilkan aliran base64 (rekatkan hasilnya di bilah alamat browser Anda)

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

Versi tidak disatukan:

<?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.
sumber
Sekarang sudah 214, bukan? Dan, saya tidak bisa mendapatkan gambar yang benar dari versi golf dan un-golf, tapi saya tidak punya pengalaman PHP, jadi jika itu bekerja untuk semua orang, itu salah saya.
user2846289
1
@ Sadim, ya 214, Anda benar. Saya sudah memeriksa, gambar yang dihasilkan valid untuk saya.
Michael M.
Bagi saya (saya uji dengan PHP 5.4.27.0), gambar adalah 4 byte pendek - bukankah seharusnya adler-32 ditambahkan ke data kempes? IE dan Chrome senang menampilkan gambar apa adanya, FF tidak. Aplikasi yang berbeda berperilaku berbeda pula dengan gambar ini.
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")

Script ini mengambil input dari argv. Jalankan skrip ini dari baris perintah, misalnyapython 27086.py deadbeef 999

Camilan
sumber