Added a cache to the page image viewer. This makes pdf viewing more convenient since you can flip back and forth between pages that are stored in the cache. The new command-line option -c allows the user to specify the maximum number of bitmaps to store. Beyond pdf documents I tried opening several images at once, and it seems to work as expected: the rendered images are saved to the cache and once a few are loaded flipping between them is instant! It would probably be nice if we start rendering the next page in the background, but I haven't attempted this yet -- let's see if this patch is satisfactory first. For the manual page we can add something like: As pages are rendered resulting bitmaps are cached to allow for fast page flipping. The -c option is used to specify the maximum number of frames for the cache to keep around (default 15). The page number and rotation angle uniquely identify a rendered page in the cache. This means that a given page can exist in the cache up to four times (once for each angle). Zack Folkerts zfolkerts@flowerbed.dyndns.org Notes: Tue Sep 18 01:22:09 EDT 2007 geoff replaced by patch page-cra Reference: /n/sources/patch/sorry/page-bitmapcache Date: Thu May 24 14:36:37 CES 2007 Signed-off-by: zfolkerts@flowerbed.dyndns.org Reviewed-by: geoff --- /sys/src/cmd/page/view.c Thu May 24 14:28:36 2007 +++ /sys/src/cmd/page/view.c Thu May 24 14:28:33 2007 @@ -15,6 +15,16 @@ Document *doc; Image *im; + +typedef struct Imcache { /* list to cache rendered pages */ + int page, angle; + Image *im; + struct Imcache *next; +} Imcache; +Imcache *cache = nil; +int cachesz; /* current number of cache elements */ +extern int cachemax; /* set with -c option */ + int page; int angle = 0; int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */ @@ -115,6 +125,69 @@ return menustr; } +Image* +cachefind(int page, int angle) +{ + Imcache *c; + Image *tmp; + for(c = cache; c; c = c->next) + if(c->page == page && c->angle == angle) { /* Return a copy of the rendered image from cache */ + tmp = xallocimage(display, Rect(0, 0, Dx(c->im->r), Dy(c->im->r)), c->im->chan, 0, DNofill); + drawop(tmp, tmp->r, c->im, nil, c->im->r.min, S); + return tmp; + } + return nil; +} + +/* recursive helper to free list elements from here on */ +void +cachefree(Imcache *c) +{ + if(c == nil) return; + if(c->next) + cachefree(c->next); + if(c->im) + freeimage(c->im); + free(c); +} + +/* ensure the cache contains at most cachemax elements */ +void +cachetrim(void) +{ + int i; + Imcache *c; + for(c = cache, i=0; c && i < cachemax; c = c->next, i++); + if(i == cachemax) { + cachefree(c->next); + c->next = nil; + cachesz = cachemax; + } +} + +/* add image to linked list of cached bitmaps along with page number and rotation angle */ +void +cacheadd(int page, int angle, Image *im) +{ + Imcache *c; + if(im == nil) return; + if(cachefind(page, angle) == nil){ /* don't add it if it's already there */ + c = malloc(sizeof(Imcache)); + c->page = page; + c->angle = angle; + c->im = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill); + if(c->im == nil){ + free(c); + return; + } + drawop(c->im, c->im->r, im, nil, im->r.min, S); + c->next = cache; /* this element becomes the new head of the list */ + cache = c; + if(++cachesz > cachemax) + cachetrim(); + } +} + void showpage(int page, Menu *m) { @@ -131,41 +204,45 @@ im = nil; return; } - im = doc->drawpage(doc, page); - if(im == nil) { - if(doc->fwdonly) /* this is how we know we're out of pages */ - wexits(0); - im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack); + if((im = cachefind(page, angle)) == nil) { /* use the cached copy if available */ + im = doc->drawpage(doc, page); if(im == nil) { - fprint(2, "out of memory: %r\n"); - wexits("memory"); + if(doc->fwdonly) /* this is how we know we're out of pages */ + wexits(0); + + im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack); + if(im == nil) { + fprint(2, "out of memory: %r\n"); + wexits("memory"); + } + string(im, ZP, display->white, ZP, display->defaultfont, "?"); + }else if(resizing){ + resize(Dx(im->r), Dy(im->r)); } - string(im, ZP, display->white, ZP, display->defaultfont, "?"); - }else if(resizing){ - resize(Dx(im->r), Dy(im->r)); - } - if(im->r.min.x > 0 || im->r.min.y > 0) { - tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill); - if(tmp == nil) { - fprint(2, "out of memory during showpage: %r\n"); - wexits("memory"); + if(im->r.min.x > 0 || im->r.min.y > 0) { + tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill); + if(tmp == nil) { + fprint(2, "out of memory during showpage: %r\n"); + wexits("memory"); + } + drawop(tmp, tmp->r, im, nil, im->r.min, S); + freeimage(im); + im = tmp; } - drawop(tmp, tmp->r, im, nil, im->r.min, S); - freeimage(im); - im = tmp; - } - - switch(angle){ - case 90: - im = rot90(im); - break; - case 180: - rot180(im); - break; - case 270: - im = rot270(im); - break; + + switch(angle){ + case 90: + im = rot90(im); + break; + case 180: + rot180(im); + break; + case 270: + im = rot270(im); + break; + } + cacheadd(page, angle, im); /* add a copy of the rendered page to the cache */ } esetcursor(nil); @@ -397,9 +474,15 @@ if(im==nil) break; esetcursor(&reading); - rot180(im); - esetcursor(nil); angle = (angle+180) % 360; + if((tmp = cachefind(page, angle)) == nil) { + rot180(im); + cacheadd(page, angle, im); + } else { + freeimage(im); + im = tmp; + } + esetcursor(nil); redraw(screen); flushimage(display, 1); break; @@ -596,9 +679,15 @@ } case Rot: /* rotate 90 */ esetcursor(&reading); - im = rot90(im); - esetcursor(nil); angle = (angle+90) % 360; + if((tmp = cachefind(page, angle)) == nil) { + im = rot90(im); + cacheadd(page, angle, im); + } else { + freeimage(im); + im = tmp; + } + esetcursor(nil); redraw(screen); flushimage(display, 1); break; @@ -606,9 +695,15 @@ if(im==nil) break; esetcursor(&reading); - rot180(im); - esetcursor(nil); angle = (angle+180) % 360; + if((tmp = cachefind(page, angle)) == nil) { + rot180(im); + cacheadd(page, angle, im); + } else { + freeimage(im); + im = tmp; + } + esetcursor(nil); redraw(screen); flushimage(display, 1); break; --- /sys/src/cmd/page/page.c Thu May 24 14:28:47 2007 +++ /sys/src/cmd/page/page.c Thu May 24 14:28:45 2007 @@ -21,6 +21,7 @@ int imagemode; int notewatcher; int notegp; +int cachemax = 15; int watcher(void*, char *x) @@ -70,7 +71,7 @@ void usage(void) { - fprint(2, "usage: page [-biRrw] [-p ppi] file...\n"); + fprint(2, "usage: page [-biRrw] [-p ppi] [-c numcachedpages] file...\n"); exits("usage"); } @@ -121,6 +122,9 @@ break; case 'i': imagemode = 1; + break; + case 'c': + cachemax = atoi(EARGF(usage())); break; default: usage();