-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlzw.lua
215 lines (197 loc) · 6.39 KB
/
lzw.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
local ffi = require "ffi"
local bit = require "bit"
local bnot = bit.bnot
local bor, band = bit.bor, bit.band
local lshift, rshift = bit.lshift, bit.rshift
-- == Encoder ==
local BUFout = {}
BUFout.__index = BUFout
local function new_buffer_out(f)
local self = setmetatable({f=f, offset=0, partial=0}, BUFout)
self.buf = ffi.new("char[255]")
return self
end
function BUFout:put_key(key, size)
local offset, partial = self.offset, self.partial
local f, buf = self.f, self.buf
local byte_offset, bit_offset = math.floor(offset / 8), offset % 8
partial = bor(partial, lshift(key, bit_offset))
local bits_to_write = bit_offset + size
while bits_to_write >= 8 do
buf[byte_offset] = band(partial, 0xFF)
byte_offset = byte_offset + 1
if byte_offset == 0xFF then -- flush
f:write(string.char(0xFF))
f:write(ffi.string(buf, 0xFF))
byte_offset = 0
end
partial = rshift(partial, 8)
bits_to_write = bits_to_write - 8
end
self.offset = (offset + size) % (0xFF * 8)
self.partial = partial
end
function BUFout:end_key()
local offset, partial = self.offset, self.partial
local f, buf = self.f, self.buf
local byte_offset = math.floor(offset / 8)
if offset % 8 ~= 0 then
buf[byte_offset] = band(partial, 0xFF)
byte_offset = byte_offset + 1
end
if byte_offset > 0 then
f:write(string.char(byte_offset))
f:write(ffi.string(buf, byte_offset))
end
f:write(string.char(0))
self.offset, self.partial = 0, 0
end
local function new_trie(degree)
local children = {}
for key = 0, degree-1 do
children[key] = {key=key, children={}}
end
return {children=children}
end
local function encode(f, d, s, x, y, w, h)
local buf = new_buffer_out(f)
local code_size = math.max(d, 2)
f:write(string.char(code_size))
local degree = 2 ^ code_size
local root = new_trie(degree)
local clear, stop = degree, degree + 1
local nkeys = degree + 2 -- skip clear code and stop code
local node = root
local key_size = code_size + 1
buf:put_key(clear, key_size)
for j = y, y+h-1 do
for i = x, x+w-1 do
local index = band(s:pget(i, j), degree-1)
local child = node.children[index]
if child ~= nil then
node = child
else
buf:put_key(node.key, key_size)
if nkeys < 0x1000 then
if nkeys == 2 ^ key_size then
key_size = key_size + 1
end
node.children[index] = {key=nkeys, children={}}
nkeys = nkeys + 1
else
buf:put_key(clear, key_size)
root = new_trie(degree)
node = root
nkeys = degree + 2
key_size = code_size + 1
end
node = root.children[index]
end
end
end
buf:put_key(node.key, key_size)
buf:put_key(stop, key_size)
buf:end_key()
end
-- == Decoder ==
local BUFin = {}
BUFin.__index = BUFin
local function new_buffer_in(f)
local self = setmetatable({}, BUFin)
self.f = f -- already opened file, read mode
self.s = 0 -- number of bytes available in block
self.b = 0 -- value of last byte read
self.n = 0 -- number of bits available in self.b
return self
end
function BUFin:get_key(size)
local key = 0
for i = 1, size do
if self.s == 0 then
self.s = self.f:read(1):byte(1)
assert(self.s > 0, "unexpected end-of-block")
end
if self.n == 0 then
self.b = self.f:read(1):byte(1)
self.n = 8
self.s = self.s - 1
end
key = bor(key, lshift(band(rshift(self.b, 8-self.n), 1), i-1))
self.n = self.n - 1
end
return key
end
local CodeTable = {}
CodeTable.__index = CodeTable
local function new_code_table(key_size)
local self = setmetatable({}, CodeTable)
self.len = lshift(1, key_size)
self.tab = {}
for key = 0, self.len+1 do
self.tab[key] = {length=1, prefix=0xFFF, suffix=key}
end
self.len = self.len + 2 -- clear & stop
return self
end
function CodeTable:add_entry(length, prefix, suffix)
self.tab[self.len] = {length=length, prefix=prefix, suffix=suffix}
self.len = self.len + 1
if band(self.len, self.len-1) == 0 then
return 1
end
return 0
end
local function decode(f, d, s, fx, fy, w, h)
local key_size = f:read(1):byte(1)
assert(key_size == math.max(d, 2), "invalid code size")
local buf = new_buffer_in(f)
local clear = lshift(key_size, 1)
local stop = clear + 1
key_size = key_size + 1
local init_key_size = key_size
local key = buf:get_key(key_size)
assert(key == clear, "expected clear code, got "..key)
local code_table, table_is_full, entry, str_len, ret
local frm_off = 0 -- pixels read
local frm_size = w * h
while frm_off < frm_size do
if key == clear then
key_size = init_key_size
code_table = new_code_table(key_size-1)
table_is_full = false
elseif not table_is_full then
ret = code_table:add_entry(str_len+1, key, entry.suffix)
if code_table.len == 0x1000 then
ret = 0
table_is_full = true
end
end
key = buf:get_key(key_size)
if key ~= clear then
if key == stop or key == 0x1000 then break end
if ret == 1 then key_size = key_size + 1 end
entry = code_table.tab[key]
str_len = entry.length
for i = 1, str_len do
local p = frm_off + entry.length - 1
local x = p % w
local y = math.floor(p / w)
s:pset(fx+x, fy+y, entry.suffix)
if entry.prefix == 0xFFF then
break
else
entry = code_table.tab[entry.prefix]
end
end
frm_off = frm_off + str_len
if key < code_table.len-1 and not table_is_full then
code_table.tab[code_table.len-1].suffix = entry.suffix
end
end
end
while buf.s > 0 do
f:seek("cur", buf.s)
buf.s = f:read(1):byte(1)
end
end
return {encode=encode, decode=decode}