ãpng ã®ãã©ã¼ããã㯠gif ã«æ¯ã¹ã¦é£ããããªã©ã¨èãããã¨ãããã¾ããããzlib ã使ããå¦çç³»ãªããç°¡å㪠png ç»åã¯ããããç°¡åã« (ãããæ¼ç®ãªã©ä¸è¦ã§) ä½ããã¿ããã§ãã
2015/03/12追è¨ï¼ãã®è¨äºã¯åãªãç¥ç好å¥å¿ã§èªå png çæãã¦ããã«éãã¾ããã以ä¸ã®ãµã³ãã«ã³ã¼ãããã¢ã«éããªãã®ã§ãå®ç¨ç®çã§ä½¿ããã¨ã¯ãããããã¾ãããRuby ã§ãã¿ãã¼ã¿ã® png åãããã人㯠chunky_png ãã綺éºãªçµµãæ¸ããã人㯠rcairo ã使ãã¨ããã¨æãã¾ãã
ã¾ãã¯ãµã³ãã«ã³ã¼ã
é»ãã赤ã¸ã®ã°ã©ãã¼ã·ã§ã³ç»åãä½ãããã°ã©ã ã
# coding: UTF-8 require "zlib" width, height = 100, 20 depth, color_type = 8, 2 # ã°ã©ãã¼ã·ã§ã³ã®ãã¿ãã¼ã¿ line = (0...width).map {|x| [x * 255 / width, 0, 0] } raw_data = [line] * height # ãã£ã³ã¯ã®ãã¤ãåçæé¢æ° def chunk(type, data) [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N") end # ãã¡ã¤ã«ã·ã°ãã㣠print "\x89PNG\r\n\x1a\n" # ããã print chunk("IHDR", [width, height, 8, 2, 0, 0, 0].pack("NNCCCCC")) # ç»åãã¼ã¿ img_data = raw_data.map {|line| ([0] + line.flatten).pack("C*") }.join print chunk("IDAT", Zlib::Deflate.deflate(img_data)) # çµç«¯ print chunk("IEND", "")
çæçµæã
$ ruby19 gradation.rb > gradation.png
ãã¡ã¤ã«ã·ã°ããã£
"\x89PNG\r\n\x1a\n" ã® 8 æåãè¼NG ã¯ã¿ããªè¦ããã¨ããã¯ããRuby ãªãããããã
print "\x89PNG\r\n\x1a\n"
ãã£ã³ã¯æ§é
ãã¡ã¤ã«ã·ã°ããã£ã®å¾ã¯ãã£ã³ã¯ãè¤æ°åç¹°ãè¿ãã1 ã¤ã®ãã£ã³ã¯ã¯
- ãã£ã³ã¯é· (4 ãã¤ã)
- ãã£ã³ã¯ã¿ã¤ã (4 ãã¤ã)
- ãã¼ã¿ (ãã£ã³ã¯é·ã®ç¤ºããã¤ãæ°)
- CRC32 (4 ãã¤ã)
ã¨ããæ§é ã«ãªã£ã¦ãããæ´æ°ã¯å
¨é¨ããã°ã¨ã³ãã£ã¢ã³ããã£ã³ã¯é·ã¯ãã¼ã¿é¨åã ãã®é·ãã表ããCRC32 ã¯ãã£ã³ã¯ã¿ã¤ãã¨ãã¼ã¿ã¾ã§ã®ãã¤ãåã® CRC ã表ãã¨ããã«æ³¨æã
以ä¸ã¯ãã£ã³ã¯ã¿ã¤ãã¨ãã¼ã¿ãåãåã£ã¦ãã£ã³ã¯ã®ãã¤ãåãè¿ãé¢æ°ã
require "zlib" def chunk(type, data) [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N") end
Array#pack ã使ãã¨ç°¡åãpack ãã©ã¼ãããã® N 㯠32 ãããæ´æ°ã®ããã°ã¨ã³ãã£ã¢ã³è¡¨ç¾ãA 㯠ASCII æååãCRC32 ã®è¨ç®ã¯ zlib ã«ããããé¢æ°ãããã¾ãã
1 ã¤ã® png ãã¡ã¤ã«ã¯ãIHDR (ããã) ãIDAT (ç»åãã¼ã¿) ãIEND (çµç«¯) ãã® 3 ã¤ã®ãã£ã³ã¯ã¿ã¤ãã®ãã£ã³ã¯ãå¿
ãæã¤ããã以ä¸ã®ãã£ã³ã¯ãæã¤ãã¨ãããã
IHDR ãã£ã³ã¯
png ã®ãããæ å ±ã以ä¸ã® 13 ãã¤ãã®ãã¤ãåã
- ç»åã®æ¨ªå¹ (4 ãã¤ã)
- ç»åã®ç¸¦å¹ (4 ãã¤ã)
- ããã深度 (1 ãã¤ã)
- ã«ã©ã¼ã»ã¿ã¤ã (1 ãã¤ã)
- å§ç¸®æ¹å¼ (1 ãã¤ã)
- ãã£ã«ã¿æ¹å¼ (1 ãã¤ã)
- ã¤ã³ã¿ã¼ã¬ã¼ã¹æ¹å¼ (1 ãã¤ã)
ããã深度㯠1 ã2 ã4 ã8 ã16 ã®ã©ãããæ®é㯠8 ã 16 ã ã¨æãã
ã«ã©ã¼ã¿ã¤ãã¯ããã深度ã¨é¢ä¿ããããã©ãç½é»ãªã 0 ãã«ã©ã¼ãªã 2 ã¨æãã°ãããããã¢ã«ãã¡å¤ãå¿
è¦ãªããããã 4 㨠6 ã¿ããã ãã©è©¦ãããã¨ã¯ãªãã詳細ã¯仕様書ãåç
§ã®ãã¨ã
å§ç¸®æ¹å¼ã¯ 0 (deflate/inflate) ããå®ç¾©ããã¦ããªãããã£ã«ã¿æ¹å¼ã 0 (ããã©ã«ã) ã®ã¿ãã¤ã³ã¿ã¼ã¬ã¼ã¹æ¹å¼ã¯ 0 (éã¤ã³ã¿ã¼ã¬ã¼ã¹) ãã試ãã¦ãªãããç¥ããªããç¥ããã人ã¯èªåã§èª¿ã¹ã¦ã
ããã§ã¯ 100 x 20 ã® 8 ããã深度ã«ã©ã¼ç»åãä½ãã¨ããã
print chunk("IHDR", [100, 20, 8, 2, 0, 0, 0].pack("NNCCCCC"))
IDAT ãã£ã³ã¯
ç»åãã¼ã¿é¨åãã¾ãã¯å§ç¸®åã®ãã¼ã¿ãä½ãã
- åè¡ã®å é ã« 1 ãã¤ãã® 0 ãã¤ããã*1
- åè¡ã® RGB ãæå®ããããã深度ã§ä¸¦ã¹ãã
- å ¨è¡ã®ãã¼ã¿ãçµåãããå§ç¸®åãã¼ã¿ã®å®æã
è¨èããããã°ã©ã ã§è¦ãã»ããæ©ãã§ããããã§ã¯ãåãã¯ã»ã«ã 0 ã 255 ã® 3 ã¤ã®æ´æ° (RGB) ã§ããããè¡åãããã¨ãã¾ãããªãããããªæãã®ã
raw_data = [ [ [0, 0, 0], [0, 0, 1], [0, 0, 2], ... ], [ [0, 1, 0], [0, 1, 1], [0, 1, 2], ... ], [ [0, 2, 0], [0, 2, 1], [0, 2, 2], ... ], ... ]
ãããããããå½¢ã«ããã
img_data = [ 0, 0,0,0, 0,0,1, 0,0,2, ..., 0, 0,1,0, 0,1,1, 0,1,2, ..., 0, 0,2,0, 0,2,1, 0,2,2, ..., ... ]
ããããã¤ããªåã«ãããä»å㯠8 ãããç»åãªã®ã§ C* 㧠pack ã16 ãããç»åãªã n* ã«ãªãã¨æãã4 ããã以ä¸ã¯ãããæ¼ç®ãå¿ è¦ã«ãªãã
img_data = raw_data.map {|line| ([0] + line.flatten).pack("C*") }.join
ããã§å¾ãããå§ç¸®åãã¼ã¿ã Zlib::Deflate.deflate ã§å§ç¸®ããã°å®æã
print chunk("IDAT", Zlib::Deflate.deflate(img_data))
IEND ãã£ã³ã¯
çµç«¯æ å ±ã ãããã¼ã¿é¨ã¯ç©ºã
print chunk("IEND", "")
以ä¸ãç°¡åã§ãããã
ã¾ã¨ã
ç°¡å㪠png ç»åãèªåã§çæãã Ruby ããã°ã©ã ã®è§£èª¬ã§ããã
ç»åçæãªãã¦æ®éã¯ã©ã¤ãã©ãªã使ãã®ã§ãèªåã§çæããªãã¨ãããªãã·ãã¥ã¨ã¼ã·ã§ã³ã¯å°ãªãã¨æãã¾ããã§ã (é常ã«æ®å¿µãªãã) Ruby ã«ã¯æ¨æºçãªç»åã©ã¤ãã©ãªããªãã®ã§ãããããã®ããã¾ã«ã¯ããããããã¾ããã
ã¡ãªã¿ã« Q(uine)R(uby) code ã§ããã®æ¹æ³ã§ png ãèªåçæãã¦ãã¾ã (ã¨ããã Q(uine)R(uby) code ã®ããã«èª¿ã¹ã) ã
ãã¾ã
å
ã®æ³¢é·ã RGB ã§è¿ä¼¼ãã¦ã¹ãã¯ãã«ã£ã½ãç»åãä½ã£ã¦ã¿ãã
# coding: US-ASCII # http://www.physics.sfasu.edu/astro/color/spectra.html def wavelength_to_rgb(n) rgb = case when n < 380 then [0.0 , 0.0 , 0.0 ] when n < 440 then [(440.0 - n) / 60.0, 0.0 , 1.0 ] when n < 440 then [(440.0 - n) / 60.0, 0.0 , 1.0 ] when n < 490 then [0.0 , (n - 440.0) / 50.0, 1.0 ] when n < 510 then [0.0 , 1.0 , (510.0 - n) / 20.0] when n < 580 then [(n - 510.0) / 70.0, 1.0 , 0.0 ] when n < 645 then [1.0 , (645 - n) / 65.0 , 0.0 ] when n < 780 then [1.0 , 0.0 , 0.0 ] else [0.0 , 0.0 , 0.0 ] end factor = case when n < 380 then 0.0 when n < 420 then 0.3 + 0.7 * (n - 380.0) / 40.0 when n < 700 then 1.0 when n < 780 then 0.3 + 0.7 * (780.0 - n) / 80.0 else 0.0 end rgb.map {|c| c == 0.0 ? 0 : 255 * ((c * factor) ** 0.8) } end # generate png require "zlib" width, height = 100, 20 depth, color_type = 8, 2 line = (0...width).map {|x| wavelength_to_rgb(380 + x * 400 / width) } raw_data = [line] * height def chunk(type, data) [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N") end print "\x89PNG\r\n\x1a\n" print chunk("IHDR", [width, height, 8, 2, 0, 0, 0].pack("NNCCCCC")) img_data = raw_data.map {|line| ([0] + line.flatten).pack("C*") }.join print chunk("IDAT", Zlib::Deflate.deflate(img_data)) print chunk("IEND", "")
*1:ãã®è¡ã®ãã¼ã¿ã®å½¢å¼ (ãã£ã«ã¿) ãããããæ å ±ããããã© 0 以å¤ã試ãããã¨ã¯ãªãã0 ã¯ã¹ããã¼ã¿ (ãã£ã«ã¿ãªã) ã¨ããæå³ãã¡ããã¨ä½ããªããã®ã¸ããã¡ããã¨ããå¿ è¦ãããã¨æãã