2021-08-27 14:24:20 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
gif: add GIF decoder
diff --git a/gif.lua b/gif.lua index 06fc021..69b18a1 100644 --- a/gif.lua +++ b/gif.lua @@ -217,9 +217,7 @@ function GIFin:get_frame() end sep = self.f:read(1) end - if self:read_image() == -1 then - return -1 - end + self:read_image() return 1 end diff --git a/lzw.lua b/lzw.lua index 2415b58..377d23a 100644 --- a/lzw.lua +++ b/lzw.lua @@ -10,7 +10,7 @@ local lshift, rshift = bit.lshift, bit.rshift local BUFout = {} BUFout.__index = BUFout -local function new_buffer(f) +local function new_buffer_out(f) local self = setmetatable({f=f, offset=0, partial=0}, BUFout) self.buf = ffi.new("char[255]") return self @@ -63,7 +63,7 @@ local function new_trie(degree) end local function encode(f, d, s, x, y, w, h) - local buf = new_buffer(f) + local buf = new_buffer_out(f) local code_size = math.max(d, 2) f:write(string.char(code_size)) local degree = 2 ^ code_size @@ -105,13 +105,111 @@ end -- == Decoder == -local function decode(f, d, s, x, y, w, h) - local code_size = f:read(1):byte(1) - assert(code_size == math.max(d, 2), "invalid code size") - repeat - local size = f:read(1):byte(1) - f:seek("cur", size) - until size == 0 +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}