2021-08-24 12:22:12 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
map: make caches smaller by using VLPs VLP = variable-length pair Each byte in a VLP uses high nibble to store X and low nibble to store Y. Signed is encoded as unsigned by using odd numbers for positive and even numbers for negative. Usually 95+% of points are compressed to 1 byte per point. This is not as optimal as Rice Coding, but probably simpler, since we don't have to deal with bit streams.
diff --git a/bio.lua b/bio.lua index 73f3c5c..c2c12f6 100644 --- a/bio.lua +++ b/bio.lua @@ -49,6 +49,26 @@ local function read_led64(fp) return ffi.cast("double *", ffi.new("char[8]", fp:read(8)))[0] end +local function read_leuvlp(fp) + local x, y = 0, 0 + local s = 0 + repeat + local byte = fp:read(1):byte(1) + l, r = bit.band(bit.rshift(byte, 4), 0x07), bit.band(byte, 0x07) + x = bit.bor(bit.lshift(l, s), x) + y = bit.bor(bit.lshift(r, s), y) + s = s + 3 + until bit.band(byte, 0x88) == 0 + return x, y +end + +local function read_leivlp(fp) + local x, y = read_leuvlp(fp) + if bit.band(x, 1) == 1 then x = (x+1)/2 elseif x ~= 0 then x = -(x/2) end + if bit.band(y, 1) == 1 then y = (y+1)/2 elseif y ~= 0 then y = -(y/2) end + return x, y +end + local function write_beu16(fp, n) fp:write(string.char(bit.rshift(n, 8), bit.band(n, 0xFF))) end @@ -65,10 +85,28 @@ local function write_bei16(fp, n) write_beu16(fp, n) end +local function write_leuvlp(fp, x, y) + repeat + local byte = bit.bor(bit.lshift(bit.band(x, 0x07), 4), bit.band(y, 0x07)) + x, y = bit.rshift(x, 3), bit.rshift(y, 3) + if x ~= 0 or y ~= 0 then + byte = bit.bor(byte, 0x88) + end + fp:write(string.char(byte)) + until x == 0 and y == 0 +end + +local function write_leivlp(fp, x, y) + if x < 0 then x = -2*x elseif x > 0 then x = x*2-1 end + if y < 0 then y = -2*y elseif y > 0 then y = y*2-1 end + write_leuvlp(fp, x, y) +end + return { read_byte=read_byte, read_leu16=read_leu16, read_leu32=read_leu32, read_beu16=read_beu16, read_beu32=read_beu32, read_lei16=read_lei16, read_lei32=read_lei32, read_bei16=read_bei16, read_bei32=read_bei32, - read_led64=read_led64, write_beu16=write_beu16, write_beu32=write_beu32, - write_bei16=write_bei16 + read_led64=read_led64, read_leuvlp=read_leuvlp, read_leivlp=read_leivlp, + write_beu16=write_beu16, write_beu32=write_beu32, write_bei16=write_bei16, + write_leuvlp=write_leuvlp, write_leivlp=write_leivlp } diff --git a/map.lua b/map.lua index 4619421..f04fd09 100644 --- a/map.lua +++ b/map.lua @@ -245,24 +245,22 @@ function Frame:save_cache(fname, sf, filter) bio.write_beu32(cache, offset) cache:seek("set", offset) local bb, lens, polys = sf:read_polygons(index) + bio.write_beu16(cache, #lens) for poly in self:mapped(polys) do local ox, oy = unpack(poly()) ox, oy = math.floor(ox + 0.5), math.floor(oy + 0.5) - bio.write_bei16(cache, ox) - bio.write_bei16(cache, oy) + bio.write_leivlp(cache, ox, oy) for point in poly do local x, y = unpack(point) x, y = math.floor(x + 0.5), math.floor(y + 0.5) local dx, dy = x-ox, y-oy if dx ~= 0 or dy ~= 0 then - bio.write_bei16(cache, dx) - bio.write_bei16(cache, dy) + bio.write_leivlp(cache, dx, dy) ox, oy = x, y end end - bio.write_beu32(cache, 0) -- end list of points + bio.write_leivlp(cache, 0, 0) end - bio.write_beu32(cache, 0x80008000) -- end list of polys end cache:close() end @@ -360,14 +358,16 @@ function Cache:get_polys(key) end assert(offset > 0, ("key '%s' not found in cache '%s'"):format(key, fname)) cache:seek("set", offset) + local npolys = bio.read_beu16(cache) return function() - local ox, oy = bio.read_bei16(cache), bio.read_bei16(cache) - if ox ~= -0x8000 or oy ~= -0x8000 then + if npolys > 0 then + npolys = npolys - 1 + local ox, oy = bio.read_leivlp(cache) local x, y = 0, 0 return function() if x ~= ox or y ~= oy then x, y = ox, oy - local dx, dy = bio.read_bei16(cache), bio.read_bei16(cache) + local dx, dy = bio.read_leivlp(cache) ox, oy = ox+dx, oy+dy return {x, y} end