Fix page caching

Fixed page caching. Cache strategy is the following:
prio 1: cache/render requested area
prio 2: cache/render as much of the current page as possible
prio 3: cache/render full page
prio 4: render next page

This is still a bit buggy when in fit-to-content mode.
pull/2/merge
HW 12 years ago
parent 86ff74acd6
commit aab05c3002

@ -67,6 +67,9 @@ UniReader = {
-- tile cache state:
cache_current_memsize = 0,
cache = {},
pagehash = nil,
jump_stack = {},
toc = nil,
@ -168,34 +171,97 @@ function UniReader:cacheclaim(size)
return true
end
function UniReader:draworcache(no, zoom, offset_x, offset_y, width, height, gamma, rotate)
-- hash draw state
local hash = self:cachehash(no, zoom, offset_x, offset_y, width, height, gamma, rotate)
if self.cache[hash] == nil then
-- not in cache, so prepare cache slot...
self:cacheclaim(width * height / 2);
self.cache[hash] = {
ttl = self.cache_max_ttl,
size = width * height / 2,
bb = Blitbuffer.new(width, height)
}
-- and draw the page
local page = self.doc:openPage(no)
local dc = self:setzoom(page, hash)
page:draw(dc, self.cache[hash].bb, 0, 0)
page:close()
else
-- we have the page in our cache,
-- so give it more ttl.
self.cache[hash].ttl = self.cache_max_ttl
function UniReader:draworcache(no, preCache)
-- our general caching strategy is as follows:
-- #1 goal: we must render the needed area.
-- #2 goal: we render as much of the requested page as we can
-- #3 goal: we render the full page
-- #4 goal: we render next page, too. (TODO)
-- ideally, this should be factored out and only be called when needed (TODO)
local page = self.doc:openPage(no)
local dc = self:setzoom(page)
-- check if we have relevant cache contents
local pagehash = no..'_'..self.globalzoom..'_'..self.globalrotate..'_'..self.globalgamma
if self.cache[pagehash] ~= nil then
-- we have something in cache, check if it contains the requested part
if self.cache[pagehash].x <= (-self.offset_x)
and self.cache[pagehash].y <= (-self.offset_y)
and self.cache[pagehash].x + self.cache[pagehash].w >= (-self.offset_x) + width
and self.cache[pagehash].y + self.cache[pagehash].h >= (-self.offset_y) + height
then
-- requested part is within cached tile
-- ...so properly clean page
page:close()
-- ...and give it more time to live (ttl), except if we're precaching
if not preCache then
self.cache[pagehash].ttl = self.cache_max_ttl
end
-- ...and return blitbuffer plus offset into it
return pagehash,
(-self.offset_x) - self.cache[pagehash].x,
(-self.offset_y) - self.cache[pagehash].y
end
end
return hash, 0, 0
end
-- calculate a hash for our current state
function UniReader:cachehash(no, zoom, offset_x, offset_y, width, height, gamma, rotate)
-- TODO (?): make this a "real" hash...
return no..'_'..zoom..'_'..offset_x..','..offset_y..'-'..width..'x'..height..'_'..gamma..'_'..rotate
-- okay, we do not have it in cache yet.
-- so render now.
-- start off with the requested area
local tile = { x = (-self.offset_x), y = (-self.offset_y), w = width, h = heigth }
-- can we cache the full page?
local max_cache = self.cache_max_memsize
if preCache then
max_cache = max_cache - self.cache[self.pagehash].size
end
if (self.fullwidth * self.fullheight / 2) <= max_cache then
-- yes we can, so do this with offset 0, 0
tile.x = 0
tile.y = 0
tile.w = self.fullwidth
tile.h = self.fullheight
elseif (tile.w*tile.h / 2) > max_cache then
-- no, we can't. so generate a tile as big as we can go
-- grow area in steps of 10px
while ((tile.w+10) * (tile.h+10) / 2) < max_cache do
if tile.x > 0 then
tile.x = tile.x - 5
tile.w = tile.w + 5
end
if tile.x + tile.w < self.fullwidth then
tile.w = tile.w + 5
end
if tile.y > 0 then
tile.y = tile.y - 5
tile.h = tile.h + 5
end
if tile.y + tile.h < self.fullheigth then
tile.h = tile.h + 5
end
end
else
if not preCache then
print("E: not enough memory in cache left, probably a bug.")
end
return nil
end
self:cacheclaim(tile.w * tile.h / 2);
self.cache[pagehash] = {
x = tile.x,
y = tile.y,
w = tile.w,
h = tile.h,
ttl = self.cache_max_ttl,
size = tile.w * tile.h / 2,
bb = Blitbuffer.new(tile.w, tile.h)
}
dc:setOffset(-tile.x, -tile.y)
print("# rendering: page="..no)
page:draw(dc, self.cache[pagehash].bb, 0, 0)
page:close()
return pagehash,
(-self.offset_x) - tile.x,
(-self.offset_y) - tile.y
end
-- blank the cache
@ -334,7 +400,6 @@ function UniReader:setzoom(page)
self.globalzoom_orig = self.globalzoom
dc:setRotate(self.globalrotate);
dc:setOffset(self.offset_x, self.offset_y)
self.fullwidth, self.fullheight = page:getSize(dc)
self.min_offset_x = fb.bb:getWidth() - self.fullwidth
self.min_offset_y = fb.bb:getHeight() - self.fullheight
@ -357,15 +422,23 @@ end
-- render and blit a page
function UniReader:show(no)
local slot
local offset_x -- resulting display offset
local offset_y
if self.globalzoommode ~= self.ZOOM_BY_VALUE then
slot, offset_x, offset_y = self:draworcache(no,self.globalzoommode,self.offset_x,self.offset_y,width,height,self.globalgamma,self.globalrotate)
else
slot, offset_x, offset_y = self:draworcache(no,self.globalzoom,self.offset_x,self.offset_y,width,height,self.globalgamma,self.globalrotate)
end
fb.bb:blitFrom(self.cache[slot].bb, 0, 0, offset_x, offset_y, width, height)
local pagehash, offset_x, offset_y = self:draworcache(no)
self.pagehash = pagehash
local bb = self.cache[pagehash].bb
local dest_x = 0
local dest_y = 0
if bb:getWidth() - offset_x < width then
-- we can't fill the whole output width
dest_x = (width - (bb:getWidth() - offset_x)) / 2
end
if bb:getHeight() - offset_y < height then
-- we can't fill the whole output heigth
dest_y = (height - (bb:getHeight() - offset_y)) / 2
end
if dest_x or dest_y then
fb.bb:paintRect(0, 0, width, height, 8)
end
fb.bb:blitFrom(bb, dest_x, dest_y, offset_x, offset_y, width, height)
if self.rcount == self.rcountmax then
print("full refresh")
self.rcount = 1
@ -446,14 +519,12 @@ function UniReader:goto(no)
self.pageno = no
self:show(no)
-- TODO: move the following to a more appropriate place
-- into the caching section
if no < self.doc:getPages() then
if self.globalzoommode ~= self.ZOOM_BY_VALUE then
if #self.bbox == 0 or not self.bbox.enabled then
-- pre-cache next page, but if we will modify bbox don't!
self:draworcache(no+1,self.globalzoommode,self.offset_x,self.offset_y,width,height,self.globalgamma,self.globalrotate)
end
else
self:draworcache(no,self.globalzoom,self.offset_x,self.offset_y,width,height,self.globalgamma,self.globalrotate)
if #self.bbox == 0 or not self.bbox.enabled then
-- pre-cache next page, but if we will modify bbox don't!
self:draworcache(no+1, true)
end
end
end

Loading…
Cancel
Save