佩尔(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。
参数通过STDIN在一行中给出,用空格隔开,颜色为十六进制值(16个字符),宽度/高度的像素数,例如:
echo "FFFF00FF 200" | perl -p solidpng.pl >yellow200.png
文件大小为832字节。具有相同颜色的最大尺寸的图像(n = 999)具有6834字节(低于10 MB)。
该解决方案使用两个库:
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函数。该函数不需要引用作为参数,也可以直接返回结果。这样可以摆脱variable $o
。
感谢Michael的回答:
感谢VadimR对许多技巧的评论:
use String::CRC32;
比短use Digest::CRC crc32;
。
y///c-4
比短-4+y///c
。
- 现在,扫描线由模板构造,
CH*
值重复。
$i
通过使用值引用删除。
- 裸词而不是字符串的块类型。
- 现在,通过将STDIN输入行(选项
-p
)与空格分隔符进行匹配来读取选项/ /
。然后第一个选项$`
进入,第二个参数进入$'
。
- 选项
-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
中的压缩级别的附加选项来设置最高压缩级别,但需要两个字节:
compress ..., 9;
yellow200.png
未经过滤的示例文件大小从832字节减少到472字节。应用于带Sub
过滤器的示例,文件大小从560字节缩小到445字节(pngcrush -brute
无法进一步压缩)。
999x999
文件已超过30720个像素,因此这似乎是自相矛盾的。