1 //========================================================================
2 //
3 // TileCompositor.cc
4 //
5 // Copyright 2014 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 #include <aconf.h>
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include <string.h>
16 #include <math.h>
17 #include "gmempp.h"
18 #include "GString.h"
19 #include "GList.h"
20 #include "SplashBitmap.h"
21 #include "GlobalParams.h"
22 #include "DisplayState.h"
23 #include "TileMap.h"
24 #include "TileCache.h"
25 #include "TileCompositor.h"
26 
27 //------------------------------------------------------------------------
28 
29 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)30 static inline Guchar div255(int x) {
31   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
32 }
33 
34 //------------------------------------------------------------------------
35 
TileCompositor(DisplayState * stateA,TileMap * tileMapA,TileCache * tileCacheA)36 TileCompositor::TileCompositor(DisplayState *stateA,
37 			       TileMap *tileMapA,
38 			       TileCache *tileCacheA) {
39   state = stateA;
40   state->setTileCompositor(this);
41   tileMap = tileMapA;
42   tileCache = tileCacheA;
43   bitmap = NULL;
44   bitmapValid = gFalse;
45 }
46 
~TileCompositor()47 TileCompositor::~TileCompositor() {
48   delete bitmap;
49 }
50 
getBitmap(GBool * finished)51 SplashBitmap *TileCompositor::getBitmap(GBool *finished) {
52   GList *tiles;
53   PlacedTileDesc *tile;
54   SplashBitmap *tileBitmap;
55   GBool allTilesFinished, tileFinished;
56   int xSrc, ySrc, xDest, yDest, w, h, i;
57 
58   if (bitmapValid) {
59     *finished = gTrue;
60     return bitmap;
61   }
62   if (!bitmap ||
63       bitmap->getWidth() != state->getWinW() ||
64       bitmap->getHeight() != state->getWinH()) {
65     if (bitmap) {
66       delete bitmap;
67     }
68     bitmap = new SplashBitmap(state->getWinW(), state->getWinH(),
69 			      state->getBitmapRowPad(), state->getColorMode(),
70 			      gFalse, gTrue, NULL);
71   }
72   clearBitmap();
73 
74   //--- PDF content
75   allTilesFinished = gTrue;
76   tiles = tileMap->getTileList();
77   tileCache->setActiveTileList(tiles);
78   for (i = 0; i < tiles->getLength(); ++i) {
79     tile = (PlacedTileDesc *)tiles->get(i);
80     if (tile->px >= 0) {
81       xSrc = 0;
82       xDest = tile->px;
83       w = tile->tw;
84     } else {
85       xSrc = -tile->px;
86       xDest = 0;
87       w = tile->tw + tile->px;
88     }
89     if (xDest + w > state->getWinW()) {
90       w = state->getWinW() - xDest;
91     }
92     if (tile->py >= 0) {
93       ySrc = 0;
94       yDest = tile->py;
95       h = tile->th;
96     } else {
97       ySrc = -tile->py;
98       yDest = 0;
99       h = tile->th + tile->py;
100     }
101     if (yDest + h > state->getWinH()) {
102       h = state->getWinH() - yDest;
103     }
104     if (w > 0 && h > 0) {
105       if ((tileBitmap = tileCache->getTileBitmap(tile, &tileFinished))) {
106 	blit(tileBitmap, xSrc, ySrc,
107 	     bitmap, xDest, yDest, w, h, !tileFinished);
108       } else {
109 	fill(xDest, yDest, w, h, state->getPaperColor());
110       }
111       allTilesFinished &= tileFinished;
112     }
113   }
114 
115 
116   //--- selection
117   if (state->hasSelection()) {
118     drawSelection();
119   }
120 
121   if (finished) {
122     *finished = allTilesFinished;
123   }
124   bitmapValid = allTilesFinished;
125 
126   return bitmap;
127 }
128 
drawSelection()129 void TileCompositor::drawSelection() {
130   SelectRect *rect;
131   int x0, y0, x1, y1, t, i;
132 
133   for (i = 0; i < state->getNumSelectRects(); ++i) {
134     rect = state->getSelectRect(i);
135     tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x0, &y0);
136     tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x1, &y1);
137     if (x0 > x1) {
138       t = x0;  x0 = x1;  x1 = t;
139     }
140     if (y0 > y1) {
141       t = y0;  y0 = y1;  y1 = t;
142     }
143     if (x0 < 0) {
144       x0 = 0;
145     } else if (x0 > state->getWinW()) {
146       x0 = state->getWinW();
147     }
148     if (y0 < 0) {
149       y0 = 0;
150     } else if (y0 > state->getWinH()) {
151       y0 = state->getWinH();
152     }
153     if (x1 < 0) {
154       x1 = 0;
155     } else if (x1 > state->getWinW()) {
156       x1 = state->getWinW();
157     }
158     if (y1 < 0) {
159       y1 = 0;
160     } else if (y1 > state->getWinH()) {
161       y1 = state->getWinH();
162     }
163     // NB: in any of the non-continuous modes, if selectPage isn't
164     // displayed, cvtUserToWindow will return 0,0 -- so we need to
165     // check for x0 < x1 and y0 < y1 here
166     if (x0 < x1 && y0 < y1) {
167       applySelection(x0, y0, x1 - x0, y1 - y0, state->getSelectColor());
168     }
169   }
170 }
171 
172 
paperColorChanged()173 void TileCompositor::paperColorChanged() {
174   bitmapValid = gFalse;
175 }
176 
matteColorChanged()177 void TileCompositor::matteColorChanged() {
178   bitmapValid = gFalse;
179 }
180 
selectColorChanged()181 void TileCompositor::selectColorChanged() {
182   bitmapValid = gFalse;
183 }
184 
reverseVideoChanged()185 void TileCompositor::reverseVideoChanged() {
186   bitmapValid = gFalse;
187 }
188 
docChanged()189 void TileCompositor::docChanged() {
190   bitmapValid = gFalse;
191 }
192 
windowSizeChanged()193 void TileCompositor::windowSizeChanged() {
194   bitmapValid = gFalse;
195 }
196 
displayModeChanged()197 void TileCompositor::displayModeChanged() {
198   bitmapValid = gFalse;
199 }
200 
zoomChanged()201 void TileCompositor::zoomChanged() {
202   bitmapValid = gFalse;
203 }
204 
rotateChanged()205 void TileCompositor::rotateChanged() {
206   bitmapValid = gFalse;
207 }
208 
scrollPositionChanged()209 void TileCompositor::scrollPositionChanged() {
210   bitmapValid = gFalse;
211 }
212 
selectionChanged()213 void TileCompositor::selectionChanged() {
214   bitmapValid = gFalse;
215 }
216 
regionsChanged()217 void TileCompositor::regionsChanged() {
218   bitmapValid = gFalse;
219 }
220 
optionalContentChanged()221 void TileCompositor::optionalContentChanged() {
222   bitmapValid = gFalse;
223 }
224 
225 
forceRedraw()226 void TileCompositor::forceRedraw() {
227   bitmapValid = gFalse;
228 }
229 
230 // Clear the bitmap to matteColor.  This only supports the RGB8 and
231 // BGR8 color modes.
clearBitmap()232 void TileCompositor::clearBitmap() {
233   SplashColorPtr data, row, p;
234   SplashBitmapRowSize rowSize;
235   int w, h, x, y;
236 
237   w = bitmap->getWidth();
238   h = bitmap->getHeight();
239   rowSize = bitmap->getRowSize();
240   data = bitmap->getDataPtr();
241 
242   switch (state->getColorMode()) {
243   case splashModeRGB8:
244     if (state->getMatteColor()[0] == state->getMatteColor()[1] &&
245 	state->getMatteColor()[1] == state->getMatteColor()[2]) {
246       if (rowSize < 0) {
247 	memset(data + rowSize * (h - 1), state->getMatteColor()[0],
248 	       -rowSize * h);
249       } else {
250 	memset(data, state->getMatteColor()[0], rowSize * h);
251       }
252     } else {
253       row = data;
254       for (y = 0; y < h; ++y) {
255 	p = row;
256 	for (x = 0; x < w; ++x) {
257 	  *p++ = state->getMatteColor()[0];
258 	  *p++ = state->getMatteColor()[1];
259 	  *p++ = state->getMatteColor()[2];
260 	}
261 	row += rowSize;
262       }
263     }
264     break;
265   case splashModeBGR8:
266     if (state->getMatteColor()[0] == state->getMatteColor()[1] &&
267 	state->getMatteColor()[1] == state->getMatteColor()[2]) {
268       if (rowSize < 0) {
269 	memset(data + rowSize * (h - 1), state->getMatteColor()[0],
270 	       -rowSize * h);
271       } else {
272 	memset(data, state->getMatteColor()[0], rowSize * h);
273       }
274     } else {
275       row = data;
276       for (y = 0; y < h; ++y) {
277 	p = row;
278 	for (x = 0; x < w; ++x) {
279 	  *p++ = state->getMatteColor()[2];
280 	  *p++ = state->getMatteColor()[1];
281 	  *p++ = state->getMatteColor()[0];
282 	}
283 	row += rowSize;
284       }
285     }
286     break;
287   default:
288     break;
289   }
290 }
291 
292 // Copy [xSrc, ySrc, w, h] from srcBitmap to [xDest, yDest] in
293 // destBitmap.  This only supports the RGB8 and BGR8 color modes.
294 // If [compositeWithPaper] is true, composites the source bitmap
295 // with the paper color (used for incremental updates).
blit(SplashBitmap * srcBitmap,int xSrc,int ySrc,SplashBitmap * destBitmap,int xDest,int yDest,int w,int h,GBool compositeWithPaper)296 void TileCompositor::blit(SplashBitmap *srcBitmap, int xSrc, int ySrc,
297 			  SplashBitmap *destBitmap, int xDest, int yDest,
298 			  int w, int h, GBool compositeWithPaper) {
299   SplashColorPtr srcData, destData, srcP, destP;
300   SplashColorPtr paperColor;
301   Guchar *alphaData, *alphaP;
302   Guchar alpha, alpha1;
303   SplashBitmapRowSize srcRowSize, destRowSize;
304   size_t alphaRowSize;
305   int x, y;
306 
307   srcData = srcBitmap->getDataPtr();
308   srcRowSize = srcBitmap->getRowSize();
309   destData = destBitmap->getDataPtr();
310   destRowSize = destBitmap->getRowSize();
311 
312   if (compositeWithPaper && (alphaData = srcBitmap->getAlphaPtr())) {
313     alphaRowSize = srcBitmap->getAlphaRowSize();
314     paperColor = state->getPaperColor();
315     for (y = 0; y < h; ++y) {
316       destP = &destData[(yDest + y) * destRowSize + 3 * xDest];
317       srcP = &srcData[(ySrc + y) * srcRowSize + 3 * xSrc];
318       alphaP = &alphaData[(ySrc + y) * alphaRowSize + xSrc];
319       for (x = 0; x < w; ++x) {
320 	alpha = *alphaP++;
321 	//~ this needs to swap paperColor 0/1/2 in BGR8 mode (?)
322 	if (alpha == 255) {
323 	  destP[0] = srcP[0];
324 	  destP[1] = srcP[1];
325 	  destP[2] = srcP[2];
326 	} else if (alpha == 0) {
327 	  destP[0] = paperColor[0];
328 	  destP[1] = paperColor[1];
329 	  destP[2] = paperColor[2];
330 	} else {
331 	  alpha1 = (Guchar)(255 - alpha);
332 	  destP[0] = div255(alpha1 * paperColor[0] + alpha * srcP[0]);
333 	  destP[1] = div255(alpha1 * paperColor[1] + alpha * srcP[1]);
334 	  destP[2] = div255(alpha1 * paperColor[2] + alpha * srcP[2]);
335 	}
336 	srcP += 3;
337 	destP += 3;
338       }
339     }
340   } else {
341     for (y = 0; y < h; ++y) {
342       destP = &destData[(yDest + y) * destRowSize + 3 * xDest];
343       srcP = &srcData[(ySrc + y) * srcRowSize + 3 * xSrc];
344       memcpy(destP, srcP, 3 * w);
345     }
346   }
347 }
348 
349 // Fill [xDest, yDest, w, h] with color.  This only supports the RGB8
350 // and BGR8 color modes.
fill(int xDest,int yDest,int w,int h,SplashColorPtr color)351 void TileCompositor::fill(int xDest, int yDest, int w, int h,
352 			  SplashColorPtr color) {
353   SplashColorPtr data, p;
354   Guchar c0, c1, c2;
355   SplashBitmapRowSize rowSize;
356   int x, y;
357 
358   if (xDest < 0) {
359     w += xDest;
360     xDest = 0;
361   }
362   if (xDest + w > state->getWinW()) {
363     w = state->getWinW() - xDest;
364   }
365   if (w <= 0) {
366     return;
367   }
368   if (yDest < 0) {
369     h += yDest;
370     yDest = 0;
371   }
372   if (yDest + h > state->getWinH()) {
373     h = state->getWinH() - yDest;
374   }
375   if (h <= 0) {
376     return;
377   }
378   data = bitmap->getDataPtr();
379   rowSize = bitmap->getRowSize();
380   if (bitmap->getMode() == splashModeRGB8) {
381     c0 = color[0];
382     c1 = color[1];
383     c2 = color[2];
384   } else {
385     c0 = color[2];
386     c1 = color[1];
387     c2 = color[0];
388   }
389   for (y = 0; y < h; ++y) {
390     p = &data[(yDest + y) * rowSize + 3 * xDest];
391     for (x = 0; x < w; ++x) {
392       p[0] = c0;
393       p[1] = c1;
394       p[2] = c2;
395       p += 3;
396     }
397   }
398 }
399 
400 // Apply a selection of <color> to the specified rectangle.  This only
401 // supports the RGB8 and BGR8 color modes.
applySelection(int xDest,int yDest,int w,int h,SplashColorPtr color)402 void TileCompositor::applySelection(int xDest, int yDest, int w, int h,
403 				    SplashColorPtr color) {
404   SplashColorPtr data, p;
405   Guchar c0, c1, c2;
406   SplashBitmapRowSize rowSize;
407   int x, y;
408 
409   if (xDest < 0) {
410     w += xDest;
411     xDest = 0;
412   }
413   if (xDest + w > state->getWinW()) {
414     w = state->getWinW() - xDest;
415   }
416   if (w <= 0) {
417     return;
418   }
419   if (yDest < 0) {
420     h += yDest;
421     yDest = 0;
422   }
423   if (yDest + h > state->getWinH()) {
424     h = state->getWinH() - yDest;
425   }
426   if (h <= 0) {
427     return;
428   }
429   data = bitmap->getDataPtr();
430   rowSize = bitmap->getRowSize();
431   if (bitmap->getMode() == splashModeRGB8) {
432     c0 = color[0];
433     c1 = color[1];
434     c2 = color[2];
435   } else {
436     c0 = color[2];
437     c1 = color[1];
438     c2 = color[0];
439   }
440   for (y = 0; y < h; ++y) {
441     p = &data[(yDest + y) * rowSize + 3 * xDest];
442     for (x = 0; x < w; ++x) {
443       p[0] = (Guchar)((3 * p[0] + c0) / 4);
444       p[1] = (Guchar)((3 * p[1] + c1) / 4);
445       p[2] = (Guchar)((3 * p[2] + c2) / 4);
446       p += 3;
447     }
448   }
449 }
450 
451 //------------------------------------------------------------------------
452 
453