Xuất một PNG chắc chắn từ đầu


11

Đầu vào : Màu lục giác RGBA c(ví dụ FFFF00FF) và số nguyên> 0 và <1000 n(ví dụ 200).

Output : Raw byte của một tập tin PNG như vậy mà khi đầu ra sẽ được lưu vào một tập tin và mở trong một trình xem ảnh, một nbằng nhình ảnh đầy màu sắc cđược hiển thị.

Đặc điểm kỹ thuật : Chương trình của bạn sẽ xuất chính xác :

  • một tiêu đề PNG ( 89504E470D0A1A0Aở dạng hex)
  • một IHDRđoạn chứa các thông số kỹ thuật:
    • chiều rộng: đầu vào trước n
    • chiều cao: đầu vào trước n
    • độ sâu bit: 8( RGBA)
    • loại màu: 6(truecolor với alpha)
    • phương pháp nén: 0
    • phương pháp lọc: 0
    • phương pháp xen kẽ: 0
  • một hoặc nhiều IDATkhối chứa dữ liệu hình ảnh (một hình ảnh rắn màu đầu vào trước đó c); có thể bị nén hoặc không nén
  • một IENDđoạn kết thúc hình ảnh

Thông tin chi tiết có sẵn trên Wikipedia , trên trang web W3 hoặc thông qua tìm kiếm của Google.

Hạn chế :

  • Bạn không được sử dụng bất kỳ thư viện hình ảnh hoặc chức năng nào được thiết kế để hoạt động với bất kỳ hình ảnh nào.
  • Chương trình của bạn phải chạy trong vòng dưới 3 phút và xuất một tệp dưới 10 MB cho tất cả các đầu vào (kiểm tra độ tỉnh táo).
  • Đây là , vì vậy mã ngắn nhất tính bằng byte sẽ giành chiến thắng!

Bạn nói rằng tệp có thể hoàn toàn không bị nén, nhưng sau đó nó phải dưới 30kB cho tất cả các đầu vào. Một 999x999tệp có hơn 30720 pixel, do đó có vẻ như tự mâu thuẫn.
Peter Taylor

@PeterTaylor Hừm, vì một số lý do tôi nghĩ rằng 30 KB là quá đủ. Không biết tôi đang nghĩ gì ... đã chỉnh sửa. (Và tôi chỉ nói rằng bạn có thể hoặc không thể sử dụng nén; bất cứ điều gì bạn muốn.)
Doorknob

Chiều rộng: 4 byte Chiều cao: 4 byte Độ sâu bit: 1 byte Loại màu: 1 byte Phương pháp nén: 1 byte Phương pháp lọc: 1 byte Phương pháp
xen kẽ

@technosaurus ... ừ, cái gì?
Doorknob

1
Ví dụ của bạn không đúng: độ sâu bit: 8 (RRGGBBAA). Độ sâu bit 8 là (RGBA) không (RRGGBBAA).
Glenn Randers-Pehrson

Câu trả lời:


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

Kích thước là 180 byte và tùy chọn -plà cần thiết (+1). Điểm số sau đó là 181.

Các đối số được đưa ra thông qua STDIN trong một dòng, được phân tách bằng khoảng trắng, màu dưới dạng giá trị hex (16 ký tự) và số pixel cho chiều rộng / chiều cao, ví dụ:

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

vàng200.png

Kích thước tệp là 832 byte. Hình ảnh có kích thước tối đa (n = 999) có cùng màu có 6834 byte (cách dưới 10 MB).

Giải pháp sử dụng hai thư viện:

  • use Digest::CRC crc32; cho các giá trị CRC32 ở cuối chunk.
  • use IO::Compress::Deflate deflate; để nén dữ liệu hình ảnh.

Cả hai thư viện không liên quan đến hình ảnh.

Ung dung:

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

Chỉnh sửa

  • use IO::Compress::Deflate':all';được thay thế bởi use Compress::Zlib;. Cái sau không xuất chức năng def def compresstheo mặc định. Hàm không cần tham chiếu làm đối số và cũng trả về kết quả trực tiếp. Điều đó cho phép thoát khỏi biến $o.

Cảm ơn câu trả lời của Michael :

  • Chức năng k: Có packthể xóa cuộc gọi bằng cách sử dụng mẫu Na*Ncho lần đầu tiên packtrong chức năng.

  • packmẫu NNCVvới bốn giá trị tối ưu hóa NNC3nvới sáu giá trị.

Cảm ơn bình luận của VadimR với rất nhiều lời khuyên:

  • use String::CRC32;ngắn hơn use Digest::CRC crc32;.
  • y///c-4ngắn hơn -4+y///c.
  • Dòng quét hiện được xây dựng bởi mẫu CH*với sự lặp lại trong giá trị.
  • Loại bỏ $ibằng cách sử dụng một tham chiếu giá trị.
  • Từ trần thay vì chuỗi cho các loại chunk.
  • Bây giờ các tùy chọn được đọc bằng cách khớp dòng đầu vào STDIN (tùy chọn -p) với khớp dấu cách / /. Sau đó, tùy chọn đầu tiên là trong $`và đối số thứ hai đi vào $'.
  • Tùy chọn -pcũng tự động in $_.
  • "\cZ"ngắn hơn "\x1a".

Nén tốt hơn

Với chi phí kích thước mã, dữ liệu hình ảnh có thể được nén thêm, nếu lọc được áp dụng.

  • Kích thước tệp chưa được lọc cho FFFF0FF 200: 832 byte

  • Bộ lọc Sub(chênh lệch pixel ngang): 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
  • Bộ lọc Subcho dòng đầu tiên và Upcho các dòng còn lại: 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);
  • Dòng chưa được lọc đầu tiên, sau đó lọc Up: 586 byte

    $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);
    
  • Cũng Compress::Zlibcó thể được điều chỉnh; mức nén cao nhất có thể được đặt bằng tùy chọn bổ sung cho mức nén trong hàm compressvới chi phí là hai byte:

    compress ..., 9;

    Kích thước tệp của ví dụ yellow200.pngmà không lọc giảm từ 832 byte xuống 472 byte. Áp dụng cho ví dụ với Subbộ lọc, kích thước tệp thu nhỏ từ 560 byte thành 445 byte ( pngcrush -brutekhông thể nén thêm).


Câu trả lời tuyệt vời (như mọi khi), nhưng chơi gôn có thể đi xa hơn - tôi nhận được 202, + 1 cho -p. Ngoài những hiểu biết sâu sắc về câu trả lời của Michael ( NA*Nvà các NNCVmẫu), - String::CRC32xuất theo mặc định, y///c-4vẫn ổn, CH*mẫu $isẽ biến mất \cZ, các barewords đều ổn, -p/ /;đặt các đối số thành tiền tố và postmatch. Tôi tự hỏi nếu tôi bỏ lỡ điều gì và điểm có thể đạt dưới 200 :)
user2846289

1
@VadimR: Rất cám ơn những lời khuyên hữu ích. Tôi thậm chí có thể chơi gôn hơn nữa bằng cách sử dụng use Compress::Zlib;và nhận được ≈ 10% dưới 200.
Heiko Oberdiek

5

PHP 214

Tôi không phải là một chuyên gia về PHP, có chỗ để chơi gôn. Lời khuyên được hoan nghênh.

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

Tạo tệp PNG:

php png.php 20 FFFF00FF > output.png

Tạo luồng base64 (dán kết quả vào thanh địa chỉ trình duyệt của bạn)

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

Phiên bản bị đánh cắp:

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

Bây giờ là 214, phải không? Và, tôi không thể có được hình ảnh chính xác từ cả hai phiên bản golf và không chơi gôn, nhưng tôi không có kinh nghiệm về PHP, vì vậy nếu nó hoạt động cho mọi người khác, thì tôi đã làm sai.
dùng2846289

1
@VadimR, có 214, bạn đúng. Tôi đã kiểm tra, hình ảnh được tạo ra là hợp lệ đối với tôi.
Michael M.

Đối với tôi (tôi kiểm tra với PHP 5.4.27.0), hình ảnh có 4 byte ngắn - không nên adler-32 được thêm vào dữ liệu xì hơi? IE và Chrome rất vui khi hiển thị hình ảnh như hiện tại, FF thì không. Các ứng dụng khác nhau cũng hoạt động khác nhau, với hình ảnh này.
dùng2846289

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

Kịch bản này lấy đầu vào từ argv. Chạy đoạn script này từ dòng lệnh, nhưpython 27086.py deadbeef 999

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.