Portable Network Graphics Parser


This is a simple example of parsing portable network graphics (PNG) files [1] [2] using pure Lua code (no C code like libpng [3] or zlib [4] [5]). In its current form, it does not fully decode PNG files but rather only the top-level structure, including metadata. A full implementation would need to implement zlib [4] too (see ModuleCompressDeflateLua). Extend if you wish.

-- pngparse.lua
-- Simple example of parsing the main sections of a PNG file.
--
-- This is mostly just an example. Not intended to be complete,
-- robust, modular, or well tested.
--
-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license).
-- Unpack 32-bit unsigned integer (most-significant-byte, MSB, first)
-- from byte string.
local function unpack_msb_uint32(s)
 local a,b,c,d = s:byte(1,#s)
 local num = (((a*256) + b) * 256 + c) * 256 + d
 return num
end
-- Read 32-bit unsigned integer (most-significant-byte, MSB, first) from file.
local function read_msb_uint32(fh)
 return unpack_msb_uint32(fh:read(4))
end
-- Read unsigned byte (integer) from file
local function read_byte(fh)
 return fh:read(1):byte()
end
local function parse_zlib(fh, len)
 local byte1 = read_byte(fh)
 local byte2 = read_byte(fh)
 local compression_method = byte1 % 16
 local compression_info = math.floor(byte1 / 16)
 local fcheck = byte2 % 32
 local fdict = math.floor(byte2 / 32) % 1
 local flevel = math.floor(byte2 / 64)
 print("compression_method=", compression_method)
 print("compression_info=", compression_info)
 print("fcheck=", fcheck)
 print("fdict=", fdict)
 print("flevel=", flevel)
 fh:read(len - 6)
 print("(deflate data not displayed)")
 local checksum = read_msb_uint32(fh)
 print("checksum=", checksum)
end
local function parse_IHDR(fh, len)
 assert(len == 13, 'format error')
 local width = read_msb_uint32(fh)
 local height = read_msb_uint32(fh)
 local bit_depth = read_byte(fh)
 local color_type = read_byte(fh)
 local compression_method = read_byte(fh)
 local filter_method = read_byte(fh)
 local interlace_method = read_byte(fh)
 print("width=", width)
 print("height=", height)
 print("bit_depth=", bit_depth)
 print("color_type=", color_type)
 print("compression_method=", compression_method)
 print("filter_method=", filter_method)
 print("interlace_method=", interlace_method)
 return compression_method
end
local function parse_sRGB(fh, len)
 assert(len == 1, 'format error')
 local rendering_intent = read_byte(fh)
 print("rendering_intent=", rendering_intent)
end
local function parse_gAMA(fh, len)
 assert(len == 4, 'format error')
 local rendering_intent = read_msb_uint32(fh)
 print("rendering_intent=", rendering_intent)
end
local function parse_cHRM(fh, len)
 assert(len == 32, 'format error')
 local white_x = read_msb_uint32(fh)
 local white_y = read_msb_uint32(fh)
 local red_x = read_msb_uint32(fh)
 local red_y = read_msb_uint32(fh)
 local green_x = read_msb_uint32(fh)
 local green_y = read_msb_uint32(fh)
 local blue_x = read_msb_uint32(fh)
 local blue_y = read_msb_uint32(fh)
 print('white_x=', white_x)
 print('white_y=', white_y)
 print('red_x=', red_x)
 print('red_y=', red_y)
 print('green_x=', green_x)
 print('green_y=', green_y)
 print('blue_x=', blue_x)
 print('blue_y=', blue_y)
end
local function parse_IDAT(fh, len, compression_method)
 if compression_method == 0 then
 -- fh:read(len)
 parse_zlib(fh, len)
 else
 print('(unrecognized compression method)')
 end 
end
local function parse_png(fh)
 -- parse PNG header
 local bytes = fh:read(8)
 local expect = "137円080円078円071円013円010円026円010円"
 if bytes ~= expect then
 error 'not a PNG file'
 end
 -- parse chunks
 local compression_method
 while 1 do
 local len = read_msb_uint32(fh)
 local stype = fh:read(4)
 print("chunk:", "type=", stype, "len=", len)
 if stype == 'IHDR' then
 compression_method = parse_IHDR(fh, len)
 elseif stype == 'sRGB' then
 parse_sRGB(fh, len)
 elseif stype == 'gAMA' then
 parse_gAMA(fh, len)
 elseif stype == 'cHRM' then
 parse_cHRM(fh, len)
 elseif stype == 'IDAT' then
 parse_IDAT(fh, len, compression_method)
 else
 local data = fh:read(len)
 print("data=", len == 0 and "(empty)" or "(not displayed)")
 end
 local crc = read_msb_uint32(fh)
 print("crc=", crc)
 if stype == 'IEND' then
 break
 end
 end
end
local filename = arg[1]
if not filename then
 io.stderr:write("usage: lua pngparse.lua <filename>")
 os.exit(1)
end
local fh = assert(io.open(filename, 'rb'))
parse_png(fh)

Example output:

$ lua pngparse.lua myfile.png
chunk: type= IHDR len= 13
width= 676
height= 647
bit_depth= 8
color_type= 2
compression_method= 0
filter_method= 0
interlace_method= 0
crc= 3625766246
chunk: type= sRGB len= 1
rendering_intent= 0
crc= 2932743401
chunk: type= gAMA len= 4
rendering_intent= 45455
crc= 201089285
chunk: type= cHRM len= 32
white_x= 31270
white_y= 32900
red_x= 64000
red_y= 33000
green_x= 30000
green_y= 60000
blue_x= 15000
blue_y= 6000
crc= 2629456188
chunk: type= IDAT len= 60975
compression_method= 8
compression_info= 7
fcheck= 30
fdict= 0
flevel= 1
(deflate data not displayed)
checksum= 2411816191
crc= 1804717936
chunk: type= IEND len= 0
data= (empty)
crc= 2923585666

--DavidManura


RecentChanges · preferences
edit · history
Last edited May 1, 2009 8:32 pm GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /