1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
-- only going to support binary ones
-- because the textual ones are annoying
-- types:
-- P1 ascii pbm
-- P2 ascii pgm
-- P3 ascii ppm
-- P4 binary pbm
-- P5 binary pgm
-- P6 binary ppm
-- function nextpixel(imgf,cur,depth) -> pixel, ncur
-- where pixel is {r,g,b}, with r,g,b in [0,1]
local pixel_mt = {__tostring=function(s) return string.format("rgb(%f,%f,%f)",s[1],s[2],s[3]) end}
local function np_p1(imgf,cur,depth)
local val,ncur = imgf:match("([01])%s*()",cur)
val = tonumber(val)
assert(val,"bad image format at "..cur)
local p = (1-val) -- 1 is black (0), 0 is white (1)
local pixel = {p,p,p}
return pixel,ncur
end
local function np_p2(imgf,cur,depth)
local val,ncur = imgf:match("(%d+)%s+()",cur)
val = tonumber(val)
assert(val,"bad image format at "..cur)
assert(0<=val and val<=depth, "image value out of range at "..cur)
local p = val/depth
local pixel = {p,p,p}
return pixel,ncur
end
local function np_p3(imgf,cur,depth)
local tr,tg,tb,ncur = imgf:match("(%d+)%s+(%d+)%s+(%d+)%s+()",cur)
local vr,vg,vb = tonumber(tr),tonumber(tg),tonumber(tb)
assert(vr,"bad image format at "..cur)
for _,v in ipairs{vr,vg,vb} do
assert(0<=v and v<=depth,"image value out of range at "..cur)
end
local pr,pg,pb = vr/depth,vg/depth,vb/depth
local pixel = {pr,pg,pb}
return pixel,ncur
end
-- i will figure this one out later
local function np_p4() error("p4 isn't supported yet") end
local function np_p5(imgf,cur,depth)
local val = imgf:byte(cur)
assert(val<=depth,"image value out of range at "..cur)
local p = val/depth
local pixel = {p,p,p}
return pixel,cur+1
end
local function np_p6(imgf,cur,depth)
local vr,vg,vb = imgf:byte(cur,cur+3)
for _,v in ipairs{vr,vg,vb} do
assert(0<=v and v<=depth, "image value out of range at "..cur)
end
local pr,pg,pb = vr/depth,vg/depth,vb/depth
local pixel = {pr,pg,pb}
return pixel,cur+3
end
local nextpixel_fns = {
[1]=np_p1,
[2]=np_p2,
[3]=np_p3,
[4]=np_p4,
[5]=np_p5,
[6]=np_p6,
}
local function parse(imgf)
assert(imgf:sub(1,1) == "P","invalid header")
local typech = tonumber(imgf:sub(2,2))
assert(typech and 1<=typech and typech<=6,"invalid header type")
local has_depth = not(typech == 1 or typech == 4)
local width,height,cur = imgf:match("\x0a(%d+) (%d+)\x0a()",3)
width,height = tonumber(width),tonumber(height)
assert(width,"couldn't find image dimensions")
-- by 'depth' i mean 'maximum value'
local depth = 1
if has_depth then
local ndepth,ncur = imgf:match("(%d+)\x0a()",cur)
ndepth=tonumber(ndepth)
assert(ndepth,"couldn't find image depth (max pixel value), in a format that needs it")
depth=ndepth
cur=ncur
end
assert(depth<=255,"depth too high (shouldn't be above 255)")
-- img[y][x]; 1,1 is top left
local img = setmetatable({},{__index=function(t,k)
if type(k) ~= "number" then return nil end
local r = {}
rawset(t,k,r)
return r
end})
local x,y=1,1
local nextpixel = nextpixel_fns[typech]
while cur <= #imgf do
local npixel,ncur = nextpixel(imgf,cur,depth)
setmetatable(npixel,pixel_mt)
img[y][x] = npixel
cur = ncur
x = x + 1
if x > width then
x = 1
y = y + 1
end
if y > height then
break
end
end
img.width = width
img.height = height
return img
end
return {load=parse}
|