-
Notifications
You must be signed in to change notification settings - Fork 114
/
Copy pathpng.lua
120 lines (104 loc) · 4.39 KB
/
png.lua
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
--[[--
Module for PNG decoding/encoding.
Currently, this is a LuaJIT FFI wrapper for lodepng lib.
@module ffi.png
]]
local ffi = require("ffi")
require("ffi/lodepng_h")
local lodepng = ffi.loadlib("lodepng")
local Png = {}
function Png.encodeToFile(filename, mem, w, h, n)
-- We'll always want 8-bits per component
local bitdepth = 8
-- Devise the output color type based on the number of components passed
local colortype
if n == 1 then
colortype = lodepng.LCT_GREY
elseif n == 2 then
colortype = lodepng.LCT_GREY_ALPHA
elseif n == 3 then
colortype = lodepng.LCT_RGB
elseif n == 4 then
colortype = lodepng.LCT_RGBA
else
return false, "passed an invalid number of color components"
end
local err = lodepng.lodepng_encode_file(filename, mem, w, h, colortype, bitdepth)
if err ~= 0 then
local err_msg = lodepng.lodepng_error_text(err)
return false, err_msg
else
return true
end
end
function Png.decodeFromFile(filename, req_n)
-- Read the file
local fh = io.open(filename, "rb")
if not fh then
return false, "couldn't open PNG file"
end
local fdata = fh:read("*a")
fh:close()
local ptr = ffi.new("unsigned char*[1]")
local width = ffi.new("int[1]")
local height = ffi.new("int[1]")
local state = ffi.new("LodePNGState[1]")
local out_n = req_n
-- Init the state
lodepng.lodepng_state_init(state);
-- We'll always want 8-bits per component
state[0].info_raw.bitdepth = 8
-- Inspect the PNG data first, to see if we can avoid a color-type conversion
local err = lodepng.lodepng_inspect(width, height, state, ffi.cast("const unsigned char*", fdata), #fdata);
if err ~= 0 then
return false, ffi.string(lodepng.lodepng_error_text(err))
end
-- Try to keep grayscale PNGs as-is if we requested so...
if req_n == 1 then
if state[0].info_png.color.colortype == lodepng.LCT_GREY or state[0].info_png.color.colortype == lodepng.LCT_GREY_ALPHA then
state[0].info_raw.colortype = lodepng.LCT_GREY
elseif state[0].info_png.color.colortype == lodepng.LCT_PALETTE and state[0].info_png.color.palettesize <= 16 then
-- If input is sRGB, but paletted to 16c or less, assume it's the eInk palette, and honor it.
-- Just expand it to grayscale so BB knows what to do with it ;).
-- NOTE: A properly encoded image targeting eInk should actually be both dithered down to the 16c eInk palette,
-- AND flagged color-type 0 (Grayscale) too! Those already fall under the first branch ;).
-- As such, this only affects stuff explicitly encoded color-type 3 (Paletted sRGB).
state[0].info_raw.colortype = lodepng.LCT_GREY
else
state[0].info_raw.colortype = lodepng.LCT_RGB
-- Don't forget to update out_n so the caller is aware of the conversion
out_n = 3
end
elseif req_n == 2 then
if state[0].info_png.color.colortype == lodepng.LCT_GREY or state[0].info_png.color.colortype == lodepng.LCT_GREY_ALPHA then
state[0].info_raw.colortype = lodepng.LCT_GREY_ALPHA
elseif state[0].info_png.color.colortype == lodepng.LCT_PALETTE and state[0].info_png.color.palettesize <= 16 then
-- If input is sRGB, but paletted to 16c or less, assume it's the eInk palette, and honor it.
-- Just expand it to grayscale w/ alpha so BB knows what to do with it ;).
state[0].info_raw.colortype = lodepng.LCT_GREY_ALPHA
else
state[0].info_raw.colortype = lodepng.LCT_RGBA
-- Don't forget to update out_n so the caller is aware of the conversion
out_n = 4
end
elseif req_n == 3 then
state[0].info_raw.colortype = lodepng.LCT_RGB
elseif req_n == 4 then
state[0].info_raw.colortype = lodepng.LCT_RGBA
else
return false, "requested an invalid number of color components"
end
err = lodepng.lodepng_decode(ptr, width, height, state, ffi.cast("const unsigned char*", fdata), #fdata)
lodepng.lodepng_state_cleanup(state)
if err ~= 0 then
return false, ffi.string(lodepng.lodepng_error_text(err))
else
return true, {
width = width[0],
height = height[0],
data = ptr[0],
ncomp = out_n,
}
end
end
return Png