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