1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
7 #include <plumb.h>
8 #include <ctype.h>
9 #include <keyboard.h>
10 #include "page.h"
11
12 typedef struct Cached Cached;
13 struct Cached
14 {
15 Document *doc;
16 int page;
17 int angle;
18 Image *im;
19 };
20
21 static Cached cache[5];
22
23 static Image*
questionmark(void)24 questionmark(void)
25 {
26 static Image *im;
27
28 if(im)
29 return im;
30 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
31 if(im == nil)
32 return nil;
33 string(im, ZP, display->white, ZP, display->defaultfont, "?");
34 return im;
35 }
36
37 void
cacheflush(void)38 cacheflush(void)
39 {
40 int i;
41 Cached *c;
42
43 for(i=0; i<nelem(cache); i++){
44 c = &cache[i];
45 if(c->im)
46 freeimage(c->im);
47 c->im = nil;
48 c->doc = nil;
49 }
50 }
51
52 static Image*
_cachedpage(Document * doc,int angle,int page,char * ra)53 _cachedpage(Document *doc, int angle, int page, char *ra)
54 {
55 int i;
56 Cached *c, old;
57 Image *im, *tmp;
58 static int lastpage = -1;
59
60 if((page < 0 || page >= doc->npage) && !doc->fwdonly)
61 return nil;
62
63 Again:
64 for(i=0; i<nelem(cache); i++){
65 c = &cache[i];
66 if(c->doc == doc && c->angle == angle && c->page == page){
67 if(chatty) fprint(2, "cache%s hit %d\n", ra, page);
68 goto Found;
69 }
70 if(c->doc == nil)
71 break;
72 }
73
74 if(i >= nelem(cache))
75 i = nelem(cache)-1;
76 c = &cache[i];
77 if(c->im)
78 freeimage(c->im);
79 c->im = nil;
80 c->doc = nil;
81 c->page = -1;
82
83 if(chatty) fprint(2, "cache%s load %d\n", ra, page);
84 im = doc->drawpage(doc, page);
85 if(im == nil){
86 if(doc->fwdonly) /* end of file */
87 wexits(0);
88 im = questionmark();
89 if(im == nil){
90 Flush:
91 if(i > 0){
92 cacheflush();
93 goto Again;
94 }
95 fprint(2, "out of memory: %r\n");
96 wexits("memory");
97 }
98 return im;
99 }
100
101 if(im->r.min.x != 0 || im->r.min.y != 0){
102 /* translate to 0,0 */
103 tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
104 if(tmp == nil){
105 freeimage(im);
106 goto Flush;
107 }
108 drawop(tmp, tmp->r, im, nil, im->r.min, S);
109 freeimage(im);
110 im = tmp;
111 }
112
113 switch(angle){
114 case 90:
115 im = rot90(im);
116 break;
117 case 180:
118 rot180(im);
119 break;
120 case 270:
121 im = rot270(im);
122 break;
123 }
124 if(im == nil)
125 goto Flush;
126
127 c->doc = doc;
128 c->page = page;
129 c->angle = angle;
130 c->im = im;
131
132 Found:
133 if(chatty) fprint(2, "cache%s mtf %d @%d:", ra, c->page, i);
134 old = *c;
135 memmove(cache+1, cache, (c-cache)*sizeof cache[0]);
136 cache[0] = old;
137 if(chatty){
138 for(i=0; i<nelem(cache); i++)
139 fprint(2, " %d", cache[i].page);
140 fprint(2, "\n");
141 }
142 if(chatty) fprint(2, "cache%s return %d %p\n", ra, old.page, old.im);
143 return old.im;
144 }
145
146 Image*
cachedpage(Document * doc,int angle,int page)147 cachedpage(Document *doc, int angle, int page)
148 {
149 static int lastpage = -1;
150 static int rabusy;
151 Image *im;
152 int ra;
153
154 if(doc->npage < 1)
155 return display->white;
156
157 im = _cachedpage(doc, angle, page, "");
158 if(im == nil)
159 return nil;
160
161 /* readahead */
162 ra = -1;
163 if(!rabusy){
164 if(page == lastpage+1)
165 ra = page+1;
166 else if(page == lastpage-1)
167 ra = page-1;
168 }
169 lastpage = page;
170 if(ra >= 0){
171 rabusy = 1;
172 switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
173 case -1:
174 rabusy = 0;
175 break;
176 case 0:
177 lockdisplay(display);
178 _cachedpage(doc, angle, ra, "-ra");
179 rabusy = 0;
180 unlockdisplay(display);
181 _exits(nil);
182 default:
183 break;
184 }
185 }
186 return im;
187 }
188