|  |  | @@ -1,519 +0,0 @@ | 
		
	
		
			
			|  |  |  | --[===================================================================[-- | 
		
	
		
			
			|  |  |  | Copyright © 2016, 2018 Pedro Gimeno Fortea. All rights reserved. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Permission is hereby granted to everyone to copy and use this file, | 
		
	
		
			
			|  |  |  | for any purpose, in whole or in part, free of charge, provided this | 
		
	
		
			
			|  |  |  | single condition is met: The above copyright notice, together with | 
		
	
		
			
			|  |  |  | this permission grant and the disclaimer below, should be included | 
		
	
		
			
			|  |  |  | in all copies of this software or of a substantial portion of it. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | THIS SOFTWARE COMES WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. | 
		
	
		
			
			|  |  |  | --]===================================================================]-- | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local ffi = require 'ffi' | 
		
	
		
			
			|  |  |  | local bit = require 'bit' | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local bytearray = ffi.typeof('uint8_t[?]') | 
		
	
		
			
			|  |  |  | local intarray = ffi.typeof('int[?]') | 
		
	
		
			
			|  |  |  | local int32ptr = ffi.typeof('int32_t *') | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local intertable = {0, 8, 4, 8, 2, 4, 1, 2, false} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function coresume(co, ...) | 
		
	
		
			
			|  |  |  | local ok, err = coroutine.resume(co, ...) | 
		
	
		
			
			|  |  |  | if not ok then | 
		
	
		
			
			|  |  |  | error(err) | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifread(self, length) | 
		
	
		
			
			|  |  |  | while self.ptr + length >= self.buflen do | 
		
	
		
			
			|  |  |  | coroutine.yield() -- wait for more input | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | local tmp = self.ptr | 
		
	
		
			
			|  |  |  | self.ptr = self.ptr + length | 
		
	
		
			
			|  |  |  | if tmp >= 24576 then -- this leaves 8192 as max read length (768 would probably suffice) | 
		
	
		
			
			|  |  |  | ffi.copy(self.buffer, self.buffer + tmp, self.buflen - tmp) | 
		
	
		
			
			|  |  |  | self.buflen = self.buflen - tmp | 
		
	
		
			
			|  |  |  | self.ptr = self.ptr - tmp | 
		
	
		
			
			|  |  |  | tmp = 0 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | return tmp, length | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifupdate(self, s) | 
		
	
		
			
			|  |  |  | if #s > 32768 then | 
		
	
		
			
			|  |  |  | -- Creating a Lua string object is an expensive operation. | 
		
	
		
			
			|  |  |  | -- Do it as seldom as possible. We split the input data | 
		
	
		
			
			|  |  |  | -- into 32K chunks. | 
		
	
		
			
			|  |  |  | for i = 1, #s, 32768 do | 
		
	
		
			
			|  |  |  | gifupdate(self, s:sub(i, i + 32767)) | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | return | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if coroutine.status(self.decoder) == "dead" then | 
		
	
		
			
			|  |  |  | -- feeding data after the decoding is finished, ignore | 
		
	
		
			
			|  |  |  | return | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | assert(self.buflen <= 32768, "Buffer overflow") | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | ffi.copy(self.buffer + self.buflen, s, #s) | 
		
	
		
			
			|  |  |  | self.buflen = self.buflen + #s | 
		
	
		
			
			|  |  |  | coresume(self.decoder) | 
		
	
		
			
			|  |  |  | return self | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifdone(self) | 
		
	
		
			
			|  |  |  | -- free C memory immediately | 
		
	
		
			
			|  |  |  | self.buffer = false | 
		
	
		
			
			|  |  |  | return self | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function giferr(self, msg) | 
		
	
		
			
			|  |  |  | print(msg) | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifpalette(palette, source, psize) | 
		
	
		
			
			|  |  |  | -- Read a palette, inserting alpha | 
		
	
		
			
			|  |  |  | for i = 0, psize - 1 do | 
		
	
		
			
			|  |  |  | palette[i*4]     = source[i*3] | 
		
	
		
			
			|  |  |  | palette[i*4 + 1] = source[i*3 + 1] | 
		
	
		
			
			|  |  |  | palette[i*4 + 2] = source[i*3 + 2] | 
		
	
		
			
			|  |  |  | palette[i*4 + 3] = 255 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifdecoder(self) | 
		
	
		
			
			|  |  |  | -- Read file ID and header | 
		
	
		
			
			|  |  |  | local buffer = self.buffer | 
		
	
		
			
			|  |  |  | gifread(self, 13) | 
		
	
		
			
			|  |  |  | if ffi.string(self.buffer, 6) ~= 'GIF87a' | 
		
	
		
			
			|  |  |  | and ffi.string(self.buffer, 6) ~= 'GIF89a' | 
		
	
		
			
			|  |  |  | then | 
		
	
		
			
			|  |  |  | self:err('Invalid GIF file format') | 
		
	
		
			
			|  |  |  | return | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | self.width = buffer[6] + 256*buffer[7] | 
		
	
		
			
			|  |  |  | self.height = buffer[8] + 256*buffer[9] | 
		
	
		
			
			|  |  |  | local gpalettesize = buffer[10] >= 128 and bit.lshift(1, bit.band(buffer[10], 7) + 1) or 0 | 
		
	
		
			
			|  |  |  | local background = buffer[11] | 
		
	
		
			
			|  |  |  | self.aspect = ((buffer[12] == 0 and 49 or 0) + 15) / 64 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local gpalette = bytearray(256*4) | 
		
	
		
			
			|  |  |  | local lpalette = bytearray(256*4) | 
		
	
		
			
			|  |  |  | local lpalettesize | 
		
	
		
			
			|  |  |  | -- Read palette and set background | 
		
	
		
			
			|  |  |  | self.background = background -- default value | 
		
	
		
			
			|  |  |  | if gpalettesize > 0 then | 
		
	
		
			
			|  |  |  | gifread(self, gpalettesize * 3) | 
		
	
		
			
			|  |  |  | gifpalette(gpalette, buffer + 13, gpalettesize) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if background < gpalettesize then | 
		
	
		
			
			|  |  |  | self.background = {gpalette[background*4], gpalette[background*4+1], gpalette[background*4+2]} | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local p | 
		
	
		
			
			|  |  |  | local GCE_trans = false | 
		
	
		
			
			|  |  |  | local GCE_dispose = 0 | 
		
	
		
			
			|  |  |  | local GCE_delay = 0 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | -- Allocate the buffers in advance, to reuse them for every frame | 
		
	
		
			
			|  |  |  | local dict = bytearray(4096) | 
		
	
		
			
			|  |  |  | local dictptrs = intarray(4096) | 
		
	
		
			
			|  |  |  | local reversebuf = bytearray(4096) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | repeat | 
		
	
		
			
			|  |  |  | -- Get block type | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) | 
		
	
		
			
			|  |  |  | local blocktype = 0x3B | 
		
	
		
			
			|  |  |  | local blocklen | 
		
	
		
			
			|  |  |  | -- for simplicity (?), we fuse the block type and the extension type into | 
		
	
		
			
			|  |  |  | -- 'blocktype' | 
		
	
		
			
			|  |  |  | if buffer[p] == 0x2C then | 
		
	
		
			
			|  |  |  | -- Image block | 
		
	
		
			
			|  |  |  | blocktype = 0x2C | 
		
	
		
			
			|  |  |  | elseif buffer[p] == 0x21 then | 
		
	
		
			
			|  |  |  | -- Extension block | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) | 
		
	
		
			
			|  |  |  | blocktype = buffer[p] | 
		
	
		
			
			|  |  |  | if blocktype == 0x2C then | 
		
	
		
			
			|  |  |  | -- there's no extension 2C - terminate | 
		
	
		
			
			|  |  |  | -- (avoids ambiguity with block type 2C) | 
		
	
		
			
			|  |  |  | blocktype = 0x3B | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | elseif buffer[p] ~= 0x3B then | 
		
	
		
			
			|  |  |  | self:err(string.format("Unknown block type: 0x%02X", buffer[p])) | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if blocktype == 0x3B then | 
		
	
		
			
			|  |  |  | -- Trailer block or invalid block - terminate | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | elseif blocktype == 0xFF then | 
		
	
		
			
			|  |  |  | -- Application extension - may be loop, otherwise skip | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) | 
		
	
		
			
			|  |  |  | blocklen = buffer[p] | 
		
	
		
			
			|  |  |  | p = gifread(self, blocklen + 1) | 
		
	
		
			
			|  |  |  | if blocklen >= 11 and ffi.string(buffer + p, 11) == 'NETSCAPE2.0' then | 
		
	
		
			
			|  |  |  | -- these *are* the androids we're looking for | 
		
	
		
			
			|  |  |  | p = p + blocklen | 
		
	
		
			
			|  |  |  | while buffer[p] ~= 0 do | 
		
	
		
			
			|  |  |  | local sblen = buffer[p] | 
		
	
		
			
			|  |  |  | p = gifread(self, sblen + 1) -- read also the next block length | 
		
	
		
			
			|  |  |  | if buffer[p] == 1 and sblen >= 3 then | 
		
	
		
			
			|  |  |  | -- looping subblock - that's for us | 
		
	
		
			
			|  |  |  | self.loop = buffer[p + 1] + 256 * buffer[p + 2] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | p = p + sblen -- advance to next block | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- skip entire block | 
		
	
		
			
			|  |  |  | p = p + blocklen | 
		
	
		
			
			|  |  |  | while buffer[p] ~= 0 do | 
		
	
		
			
			|  |  |  | gifread(self, buffer[p]) | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | elseif blocktype == 0x01 or blocktype == 0xFE then | 
		
	
		
			
			|  |  |  | -- Text or Comment Extension - not processed by us, skip | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) -- read length | 
		
	
		
			
			|  |  |  | if blocktype < 0x01 then | 
		
	
		
			
			|  |  |  | -- skip the block header (contains a length field) | 
		
	
		
			
			|  |  |  | p = gifread(self, buffer[p] + 1) + buffer[p] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | -- the text extension "consumes" the GCE, so we clear it | 
		
	
		
			
			|  |  |  | GCE_trans = false | 
		
	
		
			
			|  |  |  | GCE_dispose = 0 | 
		
	
		
			
			|  |  |  | GCE_delay = 0 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | while buffer[p] ~= 0 do | 
		
	
		
			
			|  |  |  | p = gifread(self, buffer[p] + 1) + buffer[p] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | elseif blocktype == 0xF9 then | 
		
	
		
			
			|  |  |  | -- Graphic Control Extension | 
		
	
		
			
			|  |  |  | p = gifread(self, 1) | 
		
	
		
			
			|  |  |  | blocklen = buffer[p] | 
		
	
		
			
			|  |  |  | p = gifread(self, blocklen + 1) | 
		
	
		
			
			|  |  |  | if blocklen >= 4 then | 
		
	
		
			
			|  |  |  | GCE_delay = (buffer[p+1] + 256 * buffer[p+2]) / 100 | 
		
	
		
			
			|  |  |  | GCE_trans = bit.band(buffer[p], 1) ~= 0 and buffer[p + 3] | 
		
	
		
			
			|  |  |  | GCE_dispose = bit.rshift(bit.band(buffer[p], 0x1C), 2) | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | p = p + blocklen | 
		
	
		
			
			|  |  |  | while buffer[p] ~= 0 do | 
		
	
		
			
			|  |  |  | p = gifread(self, buffer[p] + 1) + buffer[p] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | elseif blocktype == 0x2C then | 
		
	
		
			
			|  |  |  | -- Here be dragons | 
		
	
		
			
			|  |  |  | p = gifread(self, 9) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local x, y = buffer[p] + 256*buffer[p+1], buffer[p+2] + 256*buffer[p+3] | 
		
	
		
			
			|  |  |  | local w, h = buffer[p+4] + 256*buffer[p+5], buffer[p+6] + 256*buffer[p+7] | 
		
	
		
			
			|  |  |  | if w == 0 or h == 0 then | 
		
	
		
			
			|  |  |  | self:err('Zero size image') | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | local img = love.image.newImageData(w, h) | 
		
	
		
			
			|  |  |  | local dataptr = ffi.cast(int32ptr, img:getPointer()) | 
		
	
		
			
			|  |  |  | self.imgs[#self.imgs + 1] = GCE_dispose | 
		
	
		
			
			|  |  |  | self.imgs[#self.imgs + 1] = GCE_delay | 
		
	
		
			
			|  |  |  | self.imgs[#self.imgs + 1] = img | 
		
	
		
			
			|  |  |  | self.imgs[#self.imgs + 1] = x | 
		
	
		
			
			|  |  |  | self.imgs[#self.imgs + 1] = y | 
		
	
		
			
			|  |  |  | self.nimages = self.nimages + 1 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local flags = buffer[p+8] | 
		
	
		
			
			|  |  |  | if flags >= 128 then | 
		
	
		
			
			|  |  |  | -- Has local palette | 
		
	
		
			
			|  |  |  | lpalettesize = bit.lshift(1, bit.band(flags, 7) + 1) | 
		
	
		
			
			|  |  |  | p = gifread(self, lpalettesize*3) | 
		
	
		
			
			|  |  |  | gifpalette(lpalette, buffer + p, lpalettesize) | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- No local palette - copy the global palette to the local one | 
		
	
		
			
			|  |  |  | ffi.copy(lpalette, gpalette, gpalettesize*4) | 
		
	
		
			
			|  |  |  | lpalettesize = gpalettesize | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | if GCE_trans and GCE_trans < lpalettesize then | 
		
	
		
			
			|  |  |  | -- Clear alpha | 
		
	
		
			
			|  |  |  | lpalette[GCE_trans*4 + 3] = 0 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | local interlace = bit.band(flags, 64) ~= 0 and 1 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | -- LZW decoder. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | -- This could really use another coroutine for | 
		
	
		
			
			|  |  |  | -- simplicity, as there's another producer/consumer, | 
		
	
		
			
			|  |  |  | -- but we won't go there. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | p = gifread(self, 2) | 
		
	
		
			
			|  |  |  | local LZWsize = buffer[p] | 
		
	
		
			
			|  |  |  | p = p + 1 | 
		
	
		
			
			|  |  |  | if LZWsize == 0 or LZWsize > 11 then | 
		
	
		
			
			|  |  |  | self:err("Invalid code size") | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | local codebits = LZWsize + 1 | 
		
	
		
			
			|  |  |  | local clearcode = bit.lshift(1, LZWsize) -- End-of-stream is always clearcode+1 | 
		
	
		
			
			|  |  |  | local dictlen = clearcode + 2 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local bitstream, bitlen = 0, 0 | 
		
	
		
			
			|  |  |  | x, y = 0, 0 | 
		
	
		
			
			|  |  |  | local nextlenptr = p | 
		
	
		
			
			|  |  |  | local oldcode | 
		
	
		
			
			|  |  |  | local walkcode | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local nrows = 0 -- counts vertical rows, used because interlacing makes the last y invalid | 
		
	
		
			
			|  |  |  | local row = 0 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | repeat | 
		
	
		
			
			|  |  |  | -- Are there enough bits in curcode? Do we need to read more data? | 
		
	
		
			
			|  |  |  | if bitlen >= codebits and y then | 
		
	
		
			
			|  |  |  | -- Extract next code | 
		
	
		
			
			|  |  |  | local code = bit.band(bitstream, bit.lshift(1, codebits) - 1) | 
		
	
		
			
			|  |  |  | bitstream = bit.rshift(bitstream, codebits) | 
		
	
		
			
			|  |  |  | bitlen = bitlen - codebits | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if code == clearcode then | 
		
	
		
			
			|  |  |  | codebits = LZWsize + 1 | 
		
	
		
			
			|  |  |  | dictlen = clearcode + 2 | 
		
	
		
			
			|  |  |  | oldcode = false | 
		
	
		
			
			|  |  |  | elseif code == clearcode + 1 then | 
		
	
		
			
			|  |  |  | if x ~= 0 or nrows ~= h then | 
		
	
		
			
			|  |  |  | self:err("Soft EOD before all rows were output") | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | -- signal end of processing | 
		
	
		
			
			|  |  |  | -- (further data won't be read, but we need to follow the blocks) | 
		
	
		
			
			|  |  |  | y = false | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- The dictionary is stored as a list of back pointers. | 
		
	
		
			
			|  |  |  | -- We need to reverse the order to output the entries. | 
		
	
		
			
			|  |  |  | -- We use a reverse buffer for that. | 
		
	
		
			
			|  |  |  | local reverseptr = 4095 | 
		
	
		
			
			|  |  |  | -- Is this code already in the table? | 
		
	
		
			
			|  |  |  | if code < dictlen then | 
		
	
		
			
			|  |  |  | -- Already in the table - get the string from the table | 
		
	
		
			
			|  |  |  | walkcode = code | 
		
	
		
			
			|  |  |  | while walkcode >= clearcode do | 
		
	
		
			
			|  |  |  | reversebuf[reverseptr] = dict[walkcode] | 
		
	
		
			
			|  |  |  | reverseptr = reverseptr - 1 | 
		
	
		
			
			|  |  |  | walkcode = dictptrs[walkcode] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | reversebuf[reverseptr] = walkcode | 
		
	
		
			
			|  |  |  | -- Add to the table | 
		
	
		
			
			|  |  |  | if oldcode then | 
		
	
		
			
			|  |  |  | if dictlen < 4096 then | 
		
	
		
			
			|  |  |  | dictptrs[dictlen] = oldcode | 
		
	
		
			
			|  |  |  | dict[dictlen] = walkcode | 
		
	
		
			
			|  |  |  | dictlen = dictlen + 1 | 
		
	
		
			
			|  |  |  | if dictlen ~= 4096 and bit.band(dictlen, dictlen - 1) == 0 then | 
		
	
		
			
			|  |  |  | -- perfect power of two - increase code size | 
		
	
		
			
			|  |  |  | codebits = codebits + 1 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | oldcode = code | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- Not in the table - deal with the special case | 
		
	
		
			
			|  |  |  | -- The compressor has created a new code, which must be the next | 
		
	
		
			
			|  |  |  | -- in sequence. We know what it must contain. | 
		
	
		
			
			|  |  |  | -- It must contain oldcode + first character of oldcode. | 
		
	
		
			
			|  |  |  | if code > dictlen or not oldcode or not walkcode then | 
		
	
		
			
			|  |  |  | self:err("Broken LZW") | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | -- Add to the table | 
		
	
		
			
			|  |  |  | if oldcode then | 
		
	
		
			
			|  |  |  | if dictlen < 4096 then | 
		
	
		
			
			|  |  |  | dictptrs[dictlen] = oldcode | 
		
	
		
			
			|  |  |  | dict[dictlen] = walkcode | 
		
	
		
			
			|  |  |  | dictlen = dictlen + 1 | 
		
	
		
			
			|  |  |  | if dictlen ~= 4096 and bit.band(dictlen, dictlen - 1) == 0 then | 
		
	
		
			
			|  |  |  | -- perfect power of two - increase code size | 
		
	
		
			
			|  |  |  | codebits = codebits + 1 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | oldcode = code | 
		
	
		
			
			|  |  |  | walkcode = oldcode | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | while walkcode >= clearcode do | 
		
	
		
			
			|  |  |  | reversebuf[reverseptr] = dict[walkcode] | 
		
	
		
			
			|  |  |  | reverseptr = reverseptr - 1 | 
		
	
		
			
			|  |  |  | walkcode = dictptrs[walkcode] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | reversebuf[reverseptr] = walkcode | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if y then | 
		
	
		
			
			|  |  |  | for i = reverseptr, 4095 do | 
		
	
		
			
			|  |  |  | local c = reversebuf[i] | 
		
	
		
			
			|  |  |  | if c >= lpalettesize then c = 0 end | 
		
	
		
			
			|  |  |  | c = ffi.cast(int32ptr, lpalette)[c] | 
		
	
		
			
			|  |  |  | dataptr[x + row] = c | 
		
	
		
			
			|  |  |  | if interlace then | 
		
	
		
			
			|  |  |  | -- The passes 1, 2, 3, 4 correspond to the | 
		
	
		
			
			|  |  |  | -- values 1, 3, 5, 7 of 'interlace'. | 
		
	
		
			
			|  |  |  | if self.progressive and interlace < 7 and y + 1 < h then | 
		
	
		
			
			|  |  |  | -- In any pass but the last, there are at least 2 lines. | 
		
	
		
			
			|  |  |  | dataptr[x + row + w] = c | 
		
	
		
			
			|  |  |  | if interlace < 5 and y + 2 < h then | 
		
	
		
			
			|  |  |  | -- In the first two passes, there are at least 4 lines. | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*2] = c | 
		
	
		
			
			|  |  |  | if y + 3 < h then | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*3] = c | 
		
	
		
			
			|  |  |  | if interlace < 3 and y + 4 < h then | 
		
	
		
			
			|  |  |  | -- In the first pass there are 8 lines. | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*4] = c | 
		
	
		
			
			|  |  |  | if y + 5 < h then | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*5] = c | 
		
	
		
			
			|  |  |  | if y + 6 < h then | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*6] = c | 
		
	
		
			
			|  |  |  | if y + 7 < h then | 
		
	
		
			
			|  |  |  | dataptr[x + row + w*7] = c | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | -- Advance pixel | 
		
	
		
			
			|  |  |  | x = x + 1 | 
		
	
		
			
			|  |  |  | if x >= w then | 
		
	
		
			
			|  |  |  | -- Skip to next interlaced row | 
		
	
		
			
			|  |  |  | x = 0 | 
		
	
		
			
			|  |  |  | nrows = nrows + 1 | 
		
	
		
			
			|  |  |  | y = y + intertable[interlace + 1] | 
		
	
		
			
			|  |  |  | if y >= h then | 
		
	
		
			
			|  |  |  | interlace = interlace + 2 | 
		
	
		
			
			|  |  |  | if interlace > 7 then | 
		
	
		
			
			|  |  |  | y = false | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | y = intertable[interlace] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | if y then | 
		
	
		
			
			|  |  |  | row = y * w | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- No interlace, just increment y | 
		
	
		
			
			|  |  |  | x = x + 1 | 
		
	
		
			
			|  |  |  | if x >= w then | 
		
	
		
			
			|  |  |  | x = 0 | 
		
	
		
			
			|  |  |  | y = y + 1 | 
		
	
		
			
			|  |  |  | nrows = y | 
		
	
		
			
			|  |  |  | if y >= h then | 
		
	
		
			
			|  |  |  | y = false | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | row = y * w | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- This should not happen. | 
		
	
		
			
			|  |  |  | self:err('Data past the end of the image') | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- Not enough bits, grab 8 more | 
		
	
		
			
			|  |  |  | if p >= nextlenptr then | 
		
	
		
			
			|  |  |  | -- End of this subblock - read next subblock | 
		
	
		
			
			|  |  |  | assert(p == nextlenptr) | 
		
	
		
			
			|  |  |  | local sblen = buffer[nextlenptr] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if sblen == 0 then | 
		
	
		
			
			|  |  |  | -- no more data | 
		
	
		
			
			|  |  |  | if y then | 
		
	
		
			
			|  |  |  | self:err("Hard EOD before the end of the image") | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | p = gifread(self, sblen + 1) | 
		
	
		
			
			|  |  |  | nextlenptr = p + sblen | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | if y then | 
		
	
		
			
			|  |  |  | bitstream = bitstream + bit.lshift(buffer[p], bitlen) | 
		
	
		
			
			|  |  |  | bitlen = bitlen + 8 | 
		
	
		
			
			|  |  |  | p = p + 1 | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | -- end of data - fast forward to end of block | 
		
	
		
			
			|  |  |  | p = nextlenptr | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | until false | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | GCE_trans = false | 
		
	
		
			
			|  |  |  | GCE_dispose = 0 | 
		
	
		
			
			|  |  |  | GCE_delay = 0 | 
		
	
		
			
			|  |  |  | self.ncomplete = self.nimages | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | else | 
		
	
		
			
			|  |  |  | break | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | until false | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifframe(self, n) | 
		
	
		
			
			|  |  |  | n = (n-1) % self.nimages + 1 | 
		
	
		
			
			|  |  |  | return self.imgs[n*5-2], self.imgs[n*5-1], self.imgs[n*5], self.imgs[n*5-3], self.imgs[n*5-4] | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local function gifnew(retver) | 
		
	
		
			
			|  |  |  | if retver == "version" then | 
		
	
		
			
			|  |  |  | return 0x010002 | 
		
	
		
			
			|  |  |  | -- else just ignore it and create the object | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | local self = { | 
		
	
		
			
			|  |  |  | update = gifupdate; | 
		
	
		
			
			|  |  |  | done = gifdone; | 
		
	
		
			
			|  |  |  | frame = gifframe; | 
		
	
		
			
			|  |  |  | err = giferr; | 
		
	
		
			
			|  |  |  | background = false; | 
		
	
		
			
			|  |  |  | width = false; | 
		
	
		
			
			|  |  |  | height = false; | 
		
	
		
			
			|  |  |  | imgs = {}; | 
		
	
		
			
			|  |  |  | nimages = 0; | 
		
	
		
			
			|  |  |  | ncomplete = 0; | 
		
	
		
			
			|  |  |  | buffer = bytearray(65536); | 
		
	
		
			
			|  |  |  | buflen = 0; | 
		
	
		
			
			|  |  |  | ptr = 0; | 
		
	
		
			
			|  |  |  | progressive = false; | 
		
	
		
			
			|  |  |  | loop = false; | 
		
	
		
			
			|  |  |  | aspect = false; | 
		
	
		
			
			|  |  |  | decoder = coroutine.create(gifdecoder); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | -- pass self to the coroutine (will return immediately for lack of data) | 
		
	
		
			
			|  |  |  | coresume(self.decoder, self) | 
		
	
		
			
			|  |  |  | return self | 
		
	
		
			
			|  |  |  | end | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return gifnew |