ゼロからソリッドPNGを出力する


11

入力:RGBA 16進数の色c(例FFFF00FF)と0以上1000以下の整数n(例200)。

出力:PNGファイルの未加工バイト。出力がファイルに保存され、イメージビューアーで開かれると、色で塗りつぶされたnby nイメージcが表示されます。

仕様:プログラムは正確に出力する必要があります:

  • PNGヘッダー(89504E470D0A1A0A16進数)
  • IHDRこれらの仕様を含むチャンク:
    • 幅:前の入力 n
    • 高さ:前の入力 n
    • ビット深度:8RGBA
    • カラータイプ:(6アルファ付きトゥルーカラー)
    • 圧縮方法: 0
    • フィルター方法: 0
    • インターレース方式: 0
  • IDAT画像データ(以前に入力された色のベタ画像c)を含む1つ以上のチャンク。圧縮または非圧縮の場合があります
  • IEND画像終了チャンク

詳細は、ウィキペディアW3サイト、またはGoogle検索で入手できます。

制限事項

  • いかなる種類の画像でも動作するように設計された画像ライブラリまたは関数を使用することはできません。
  • プログラムは3分以内に実行され、すべての入力に対して10 MB未満のファイルを出力する必要があります(健全性チェック)。
  • これはなので、バイト単位の最短コードが勝ちます!

ファイルは完全に圧縮されていないかもしれませんが、すべての入力に対して30kB未満である必要があると言います。999x999それは自己矛盾しているようだように、ファイルには、以上の30720個の画素を有します。
ピーターテイラー

@PeterTaylor Hm、何らかの理由で30 KBで十分だと思いました。私が何を考えていたのか分からない...編集。(そして、圧縮を使用してもしなくてもかまいません;どちらでもいいです。)
ドアノブ

幅:4バイト高さ:4バイトビット深度:1バイトカラータイプ:1バイト圧縮方式:1バイトフィルター方式:1バイトインターレース方式:1バイト
テクノサウルス

@technosaurus ...えーと、何?
ドアノブ

1
あなたの例は正しくありません:ビット深度:8(RRGGBBAA)。ビット深度8は(RRGGBBAA)ではなく(RGBA)です。
グレンランダース-パーソン

回答:


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

サイズは180バイトで、オプション-pが必要です(+1)。スコアは181です。

引数は、スペース、16進数値としての色(16文字)、および幅/高さのピクセル数で区切られた行でSTDINを介して与えられます。例:

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

yellow200.png

ファイルサイズは832バイトです。同じ色の最大サイズの画像(n = 999)は、6834バイト(10 MB未満)です。

ソリューションは2つのライブラリを使用します。

  • use Digest::CRC crc32; チャンクの終わりのCRC32値の場合。
  • use IO::Compress::Deflate deflate; 画像データを圧縮します。

両方のライブラリは画像に関連していません。

ゴルフをしていない:

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

編集

  • use IO::Compress::Deflate':all';はに置き換えられuse Compress::Zlib;ます。後者はcompressデフォルトでdeflate関数をエクスポートします。関数は引数として参照を必要とせず、結果を直接返します。それは変数を取り除くことを可能にします$o

マイケルの答えをありがとう:

  • 機能k:の呼び出しは、機能の最初のpackテンプレートNa*Nを使用して削除できますpack

  • packNNCV4つの値を持つテンプレートはNNC3n、6 つの値で最適化します。

多くのヒントを備えたVadimRのコメントをありがとう:

  • use String::CRC32;はより短いuse Digest::CRC crc32;
  • y///c-4はより短い-4+y///c
  • スキャンラインはCH*、値に繰り返しを含むテンプレートによって構築されます。
  • $i値参照を使用した削除。
  • チャンクタイプの文字列の代わりに裸の単語。
  • STDIN入力行(オプション-p)とスペース区切り文字を一致させることで、オプションが読み取られるようになりました/ /。次に、最初のオプションが入力され$`、2番目の引数が入力され$'ます。
  • オプション-pも自動的に印刷し$_ます。
  • "\cZ"はより短い"\x1a"

より良い圧縮

フィルタリングが適用される場合、コードサイズを犠牲にして画像データをさらに圧縮できます。

  • フィルタリングされていないファイルサイズFFFF0FF 200:832バイト

  • フィルターSub(水平ピクセル差):560バイト

    $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
  • Sub最初の行とUp残りの行のフィルター:590バイト

    $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);
  • 最初にフィルタリングされていない行、次にフィルタリングUp:586バイト

    $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::Zlib調整することもできます。最高の圧縮レベルはcompress、2バイトのコストで機能の圧縮レベルの追加オプションによって設定できます。

    compress ..., 9;

    yellow200.pngフィルタリングなしの例のファイルサイズは、832バイトから472バイトに減少します。Subフィルタを使用した例に適用すると、ファイルサイズは560バイトから445バイトに縮小します(pngcrush -bruteこれ以上圧縮できません)。


(いつものように)すばらしい答えですが、ゴルフはさらに進むことができ-pます。Michaelの答え(NA*NおよびNNCVテンプレート)の洞察に加えて、- String::CRC32デフォルトでエクスポート、y///c-4OK、CH*テンプレート、$i削除、ベアワード\cZはOKで-p/ /;引数を事前一致および事後一致に配置します。私は何かを逃し、スコアが200未満になる可能性があるのだろうか?)
user2846289

1
@VadimR:役に立つヒントをありがとう。私もゴルフ、それがさらに使用してできたuse Compress::Zlib;と≈10%200未満になった
ハイコOberdiek

5

PHP 214

私はPHPの専門家ではありません。ゴルフをする場所があります。ヒントを歓迎します。

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

PNGファイルを生成します。

php png.php 20 FFFF00FF > output.png

base64ストリームを生成します(結果をブラウザのアドレスバーに貼り付けます)

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

非ゴルフバージョン:

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

今は214ですよね。そして、ゴルフバージョンと非ゴルフバージョンの両方から正しい画像を取得することはできませんが、PHPの経験はありません。
user2846289

1
@VadimR、はい214、あなたは正しい。確認したところ、生成された画像は私にとって有効です。
マイケルM.

私にとって(PHP 5.4.27.0でテストします)、イメージは4バイト短いです-adler-32を収縮したデータに追加すべきではありませんか?IEとChromeは画像をそのまま表示できますが、FFはそうではありません。この画像では、アプリごとに動作が異なります。
user2846289

4

Python、252バイト

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

このスクリプトは、argvから入力を受け取ります。次のようなコマンドラインからこのスクリプトを実行しますpython 27086.py deadbeef 999

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.