1 //========================================================================
2 //
3 // TileMap.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 "gmem.h"
16 #include "gmempp.h"
17 #include "GList.h"
18 #include "PDFDoc.h"
19 #include "DisplayState.h"
20 #include "TileMap.h"
21 
22 //------------------------------------------------------------------------
23 
24 // Number of pixels of matte color between pages (above and below each
25 // other) in continuous mode.
26 #define continuousPageSpacing 3
27 
28 // Number of pixels of matte color between facing pages (left and
29 // right of each other) in side-by-side mode.
30 #define sideBySidePageSpacing 3
31 
32 // Number of pixels of matte color between pages (left and right of
33 // each other) in horizontal continuous mode.
34 #define horizContinuousPageSpacing 3
35 
36 //------------------------------------------------------------------------
37 
TileMap(DisplayState * stateA)38 TileMap::TileMap(DisplayState *stateA) {
39   state = stateA;
40   state->setTileMap(this);
41   pageDPI = NULL;
42   pageW = pageH = NULL;
43   tileW = tileH = NULL;
44   pageBoxW = pageBoxH = NULL;
45   pageX = pageY = NULL;
46   tiles = NULL;
47 }
48 
~TileMap()49 TileMap::~TileMap() {
50   clearPageParams();
51   clearContinuousModeParams();
52   gfree(pageBoxW);
53   gfree(pageBoxH);
54   if (tiles) {
55     deleteGList(tiles, PlacedTileDesc);
56   }
57 }
58 
getTileList()59 GList *TileMap::getTileList() {
60   double pageDPI1, pageDPI2;
61   int pageW1, pageH1, tileW1, tileH1, pageW2, pageH2, tileW2, tileH2;
62   int offsetX, offsetY, offsetX2;
63   int x0, y0, x1, y1, x, y, tx, ty, tw, th, page;
64 
65   if (tiles) {
66     return tiles;
67   }
68 
69   tiles = new GList();
70 
71   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
72     return tiles;
73   }
74 
75   updatePageParams();
76   updateContinuousModeParams();
77 
78   switch (state->getDisplayMode()) {
79 
80   case displaySingle:
81     page = state->getScrollPage();
82     pageDPI1 = pageDPI[page - 1];
83     pageW1 = pageW[page - 1];
84     pageH1 = pageH[page - 1];
85     tileW1 = tileW[page - 1];
86     tileH1 = tileH[page - 1];
87     if (pageW1 < state->getWinW()) {
88       offsetX = (state->getWinW() - pageW1) / 2;
89     } else {
90       offsetX = 0;
91     }
92     if (pageH1 < state->getWinH()) {
93       offsetY = (state->getWinH() - pageH1) / 2;
94     } else {
95       offsetY = 0;
96     }
97     if ((x0 = state->getScrollX() - offsetX) < 0) {
98       x0 = 0;
99     }
100     if ((y0 = state->getScrollY() - offsetY) < 0) {
101       y0 = 0;
102     }
103     if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX) >= pageW1) {
104       x1 = pageW1 - 1;
105     }
106     if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY) >= pageH1) {
107       y1 = pageH1 - 1;
108     }
109     for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
110       for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
111 	tx = x * tileW1;
112 	ty = y * tileH1;
113 	tw = tileW1;
114 	if (tx + tw > pageW1) {
115 	  tw = pageW1 - tx;
116 	}
117 	th = tileH1;
118 	if (ty + th > pageH1) {
119 	  th = pageH1 - ty;
120 	}
121 	tiles->append(new PlacedTileDesc(page, state->getRotate(), pageDPI1,
122 					 tx, ty, tw, th,
123 					 tx - state->getScrollX() + offsetX,
124 					 ty - state->getScrollY() + offsetY));
125       }
126     }
127     break;
128 
129   case displayContinuous:
130     if (totalH < state->getWinH()) {
131       offsetY = (state->getWinH() - totalH) / 2;
132     } else {
133       offsetY = 0;
134     }
135     page = findContinuousPage(state->getScrollY());
136     while (page <= state->getDoc()->getNumPages() &&
137 	   pageY[page - 1] < state->getScrollY() + state->getWinH()) {
138       pageDPI1 = pageDPI[page - 1];
139       pageW1 = pageW[page - 1];
140       pageH1 = pageH[page - 1];
141       tileW1 = tileW[page - 1];
142       tileH1 = tileH[page - 1];
143       if (maxW < state->getWinW()) {
144 	offsetX = (state->getWinW() - maxW) / 2;
145       } else {
146 	offsetX = 0;
147       }
148       offsetX += (maxW - pageW1) / 2;
149       if ((x0 = state->getScrollX() - offsetX) < 0) {
150 	x0 = 0;
151       }
152       if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
153 	y0 = 0;
154       }
155       if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX)
156 	  >= pageW1) {
157 	x1 = pageW1 - 1;
158       }
159       if ((y1 = state->getScrollY() - pageY[page - 1]
160 	        + state->getWinH() - 1 - offsetY)
161 	  >= pageH1) {
162 	y1 = pageH1 - 1;
163       }
164       for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
165 	for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
166 	  tx = x * tileW1;
167 	  ty = y * tileH1;
168 	  tw = tileW1;
169 	  if (tx + tw > pageW1) {
170 	    tw = pageW1 - tx;
171 	  }
172 	  th = tileH1;
173 	  if (ty + th > pageH1) {
174 	    th = pageH1 - ty;
175 	  }
176 	  tiles->append(new PlacedTileDesc(
177 				page, state->getRotate(), pageDPI1,
178 				tx, ty, tw, th,
179 				tx - state->getScrollX() + offsetX,
180 				ty - state->getScrollY() + pageY[page - 1]
181 				  + offsetY));
182 	}
183       }
184       ++page;
185     }
186     break;
187 
188   case displaySideBySideSingle:
189     page = state->getScrollPage();
190     pageDPI1 = pageDPI[page - 1];
191     pageW1 = pageW[page - 1];
192     pageH1 = pageH[page - 1];
193     tileW1 = tileW[page - 1];
194     tileH1 = tileH[page - 1];
195     if (page + 1 <= state->getDoc()->getNumPages()) {
196       pageDPI2 = pageDPI[page];
197       pageW2 = pageW[page];
198       pageH2 = pageH[page];
199       tileW2 = tileW[page];
200       tileH2 = tileH[page];
201     } else {
202       // display a single page as though there were a blank facing
203       // page of the same size
204       pageDPI2 = pageDPI1;
205       pageW2 = pageW1;
206       pageH2 = pageH1;
207       tileW2 = tileW1;
208       tileH2 = tileH1;
209     }
210     if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
211       offsetX = (state->getWinW() -
212 		 (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
213     } else {
214       offsetX = 0;
215     }
216     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
217     if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
218       if (pageH1 > pageH2) {
219 	offsetY = (state->getWinH() - pageH1) / 2;
220       } else {
221 	offsetY = (state->getWinH() - pageH2) / 2;
222       }
223     } else {
224       offsetY = 0;
225     }
226     // left page
227     if ((x0 = state->getScrollX() - offsetX) < 0) {
228       x0 = 0;
229     }
230     if ((y0 = state->getScrollY() - offsetY) < 0) {
231       y0 = 0;
232     }
233     if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX) >= pageW1) {
234       x1 = pageW1 - 1;
235     } else if (x1 < 0) {
236       x1 = -tileW2;
237     }
238     if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY) >= pageH1) {
239       y1 = pageH1 - 1;
240     } else if (y1 < 0) {
241       y1 = -tileH2;
242     }
243     for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
244       for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
245 	tx = x * tileW1;
246 	ty = y * tileH1;
247 	tw = tileW1;
248 	if (tx + tw > pageW1) {
249 	  tw = pageW1 - tx;
250 	}
251 	th = tileH1;
252 	if (ty + th > pageH1) {
253 	  th = pageH1 - ty;
254 	}
255 	tiles->append(new PlacedTileDesc(page,
256 					 state->getRotate(), pageDPI1,
257 					 tx, ty, tw, th,
258 					 tx - state->getScrollX() + offsetX,
259 					 ty - state->getScrollY() + offsetY));
260       }
261     }
262     // right page
263     if (page + 1 <= state->getDoc()->getNumPages()) {
264       if ((x0 = state->getScrollX() - offsetX2) < 0) {
265 	x0 = 0;
266       }
267       if ((y0 = state->getScrollY() - offsetY) < 0) {
268 	y0 = 0;
269       }
270       if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX2)
271 	  >= pageW2) {
272 	x1 = pageW2 - 1;
273       } else if (x1 < 0) {
274 	x1 = -tileW2;
275       }
276       if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY)
277 	  >= pageH2) {
278 	y1 = pageH2 - 1;
279       } else if (y1 < 0) {
280 	y1 = -tileH2;
281       }
282       for (y = y0 / tileH2; y <= y1 / tileH2; ++y) {
283 	for (x = x0 / tileW2; x <= x1 / tileW2; ++x) {
284 	  tx = x * tileW2;
285 	  ty = y * tileH2;
286 	  tw = tileW2;
287 	  if (tx + tw > pageW2) {
288 	    tw = pageW2 - tx;
289 	  }
290 	  th = tileH2;
291 	  if (ty + th > pageH2) {
292 	    th = pageH2 - ty;
293 	  }
294 	  tiles->append(new PlacedTileDesc(page + 1,
295 					   state->getRotate(), pageDPI2,
296 					   tx, ty, tw, th,
297 					   tx - state->getScrollX() + offsetX2,
298 					   ty - state->getScrollY() + offsetY));
299 	}
300       }
301     }
302     break;
303 
304   case displaySideBySideContinuous:
305     if (totalH < state->getWinH()) {
306       offsetY = (state->getWinH() - totalH) / 2;
307     } else {
308       offsetY = 0;
309     }
310     page = findSideBySideContinuousPage(state->getScrollY());
311     while (page <= state->getDoc()->getNumPages() &&
312 	   (pageY[page - 1] < state->getScrollY() + state->getWinH() ||
313 	    (page + 1 <= state->getDoc()->getNumPages() &&
314 	     pageY[page] < state->getScrollY() + state->getWinH()))) {
315       pageDPI1 = pageDPI[page - 1];
316       pageW1 = pageW[page - 1];
317       pageH1 = pageH[page - 1];
318       tileW1 = tileW[page - 1];
319       tileH1 = tileH[page - 1];
320       if (page + 1 <= state->getDoc()->getNumPages()) {
321 	pageDPI2 = pageDPI[page];
322 	pageW2 = pageW[page];
323 	pageH2 = pageH[page];
324 	tileW2 = tileW[page];
325 	tileH2 = tileH[page];
326       } else {
327 	// display a single page as though there were a blank facing
328 	// page of the same size
329 	pageDPI2 = pageDPI1;
330 	pageW2 = pageW1;
331 	pageH2 = pageH1;
332 	tileW2 = tileW1;
333 	tileH2 = tileH1;
334       }
335       if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
336 	offsetX = (state->getWinW() -
337 		   (maxW + sideBySidePageSpacing + maxW2)) / 2;
338       } else {
339 	offsetX = 0;
340       }
341       offsetX += maxW - pageW1;
342       offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
343       // left page
344       if ((x0 = state->getScrollX() - offsetX) < 0) {
345 	x0 = 0;
346       }
347       if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
348 	y0 = 0;
349       }
350       if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX)
351 	  >= pageW1) {
352 	x1 = pageW1 - 1;
353       } else if (x1 < 0) {
354 	x1 = -tileW2;
355       }
356       if ((y1 = state->getScrollY() - pageY[page - 1]
357 	        + state->getWinH() - 1 - offsetY)
358 	  >= pageH1) {
359 	y1 = pageH1 - 1;
360       } else if (y1 < 0) {
361 	y1 = -tileH2;
362       }
363       for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
364 	for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
365 	  tx = x * tileW1;
366 	  ty = y * tileH1;
367 	  tw = tileW1;
368 	  if (tx + tw > pageW1) {
369 	    tw = pageW1 - tx;
370 	  }
371 	  th = tileH1;
372 	  if (ty + th > pageH1) {
373 	    th = pageH1 - ty;
374 	  }
375 	  tiles->append(new PlacedTileDesc(
376 				page, state->getRotate(), pageDPI1,
377 				tx, ty, tw, th,
378 				tx - state->getScrollX() + offsetX,
379 				ty - state->getScrollY() + pageY[page - 1]
380 				  + offsetY));
381 	}
382       }
383       ++page;
384       // right page
385       if (page <= state->getDoc()->getNumPages()) {
386 	if ((x0 = state->getScrollX() - offsetX2) < 0) {
387 	  x0 = 0;
388 	}
389 	if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
390 	  y0 = 0;
391 	}
392 	if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX2)
393 	    >= pageW2) {
394 	  x1 = pageW2 - 1;
395 	} else if (x1 < 0) {
396 	  x1 = -tileW2;
397 	}
398 	if ((y1 = state->getScrollY() - pageY[page - 1]
399 	          + state->getWinH() - 1 - offsetY)
400 	    >= pageH2) {
401 	  y1 = pageH2 - 1;
402 	} else if (y1 < 0) {
403 	  y1 = -tileH2;
404 	}
405 	for (y = y0 / tileH2; y <= y1 / tileH2; ++y) {
406 	  for (x = x0 / tileW2; x <= x1 / tileW2; ++x) {
407 	    tx = x * tileW2;
408 	    ty = y * tileH2;
409 	    tw = tileW2;
410 	    if (tx + tw > pageW2) {
411 	      tw = pageW2 - tx;
412 	    }
413 	    th = tileH2;
414 	    if (ty + th > pageH2) {
415 	      th = pageH2 - ty;
416 	    }
417 	    tiles->append(new PlacedTileDesc(
418 				  page, state->getRotate(), pageDPI2,
419 				  tx, ty, tw, th,
420 				  tx - state->getScrollX() + offsetX2,
421 				  ty - state->getScrollY() + pageY[page - 1]
422 				    + offsetY));
423 	  }
424 	}
425       }
426       ++page;
427     }
428     break;
429 
430   case displayHorizontalContinuous:
431     if (totalW < state->getWinW()) {
432       offsetX = (state->getWinW() - totalW) / 2;
433     } else {
434       offsetX = 0;
435     }
436     page = findHorizContinuousPage(state->getScrollX());
437     while (page <= state->getDoc()->getNumPages() &&
438 	   pageX[page - 1] < state->getScrollX() + state->getWinW()) {
439       pageDPI1 = pageDPI[page - 1];
440       pageW1 = pageW[page - 1];
441       pageH1 = pageH[page - 1];
442       tileW1 = tileW[page - 1];
443       tileH1 = tileH[page - 1];
444       if (maxH < state->getWinH()) {
445 	offsetY = (state->getWinH() - maxH) / 2;
446       } else {
447 	offsetY = 0;
448       }
449       if ((x0 = state->getScrollX() - pageX[page - 1] - offsetX) < 0) {
450 	x0 = 0;
451       }
452       if ((y0 = state->getScrollY() - offsetY) < 0) {
453 	y0 = 0;
454       }
455       if ((x1 = state->getScrollX() - pageX[page - 1]
456 	        + state->getWinW() - 1 - offsetX)
457 	  >= pageW1) {
458 	x1 = pageW1 - 1;
459       }
460       if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY)
461 	  >= pageH1) {
462 	y1 = pageH1 - 1;
463       }
464       for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
465 	for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
466 	  tx = x * tileW1;
467 	  ty = y * tileH1;
468 	  tw = tileW1;
469 	  if (tx + tw > pageW1) {
470 	    tw = pageW1 - tx;
471 	  }
472 	  th = tileH1;
473 	  if (ty + th > pageH1) {
474 	    th = pageH1 - ty;
475 	  }
476 	  tiles->append(new PlacedTileDesc(
477 				page, state->getRotate(), pageDPI1,
478 				tx, ty, tw, th,
479 				tx - state->getScrollX() + pageX[page - 1]
480 				  + offsetX,
481 				ty - state->getScrollY() + offsetY));
482 	}
483       }
484       ++page;
485     }
486     break;
487   }
488 
489   return tiles;
490 }
491 
getScrollLimits(int * horizMax,int * vertMax)492 void TileMap::getScrollLimits(int *horizMax, int *vertMax) {
493   int pageW1, pageH1, pageW2, pageH2;
494 
495   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
496     *horizMax = *vertMax = 0;
497     return;
498   }
499 
500   updatePageParams();
501   updateContinuousModeParams();
502 
503   switch (state->getDisplayMode()) {
504   case displaySingle:
505     *horizMax = pageW[state->getScrollPage() - 1];
506     *vertMax = pageH[state->getScrollPage() - 1];
507     break;
508   case displayContinuous:
509     *horizMax = maxW;
510     *vertMax = totalH;
511     break;
512   case displaySideBySideSingle:
513     pageW1 = pageW[state->getScrollPage() - 1];
514     pageH1 = pageH[state->getScrollPage() - 1];
515     if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
516       pageW2 = pageW[state->getScrollPage()];
517       pageH2 = pageH[state->getScrollPage()];
518     } else {
519       pageW2 = pageW1;
520       pageH2 = pageH1;
521     }
522     *horizMax = pageW1 + sideBySidePageSpacing + pageW2;
523     *vertMax = pageH1 > pageH2 ? pageH1 : pageH2;
524     break;
525   case displaySideBySideContinuous:
526     *horizMax = maxW + sideBySidePageSpacing + maxW2;
527     *vertMax = totalH;
528     break;
529   case displayHorizontalContinuous:
530     *horizMax = totalW;
531     *vertMax = maxH;
532     break;
533   default: // should never happen
534     *horizMax = *vertMax = 0;
535     break;
536   }
537 }
538 
cvtWindowToUser(int xw,int yw,int * pg,double * xu,double * yu)539 GBool TileMap::cvtWindowToUser(int xw, int yw,
540 			       int *pg, double *xu, double *yu) {
541   GBool ok;
542   int xd, yd;
543 
544   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
545     *pg = 0;
546     *xu = *yu = 0;
547     return gFalse;
548   }
549 
550   ok = cvtWindowToDev(xw, yw, pg, &xd, &yd);
551   cvtDevToUser(*pg, xd, yd, xu, yu);
552   return ok;
553 }
554 
cvtWindowToDev(int xw,int yw,int * pg,int * xd,int * yd)555 GBool TileMap::cvtWindowToDev(int xw, int yw,
556 			      int *pg, int *xd, int *yd) {
557   int pageW1, pageH1, pageW2, pageH2, offsetX, offsetX2, offsetY;
558 
559   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
560     *pg = 0;
561     *xd = *yd = 0;
562     return gFalse;
563   }
564 
565   updatePageParams();
566   updateContinuousModeParams();
567 
568   switch (state->getDisplayMode()) {
569 
570   case displaySingle:
571     *pg = state->getScrollPage();
572     pageW1 = pageW[*pg - 1];
573     pageH1 = pageH[*pg - 1];
574     if (pageW1 < state->getWinW()) {
575       offsetX = (state->getWinW() - pageW1) / 2;
576     } else {
577       offsetX = 0;
578     }
579     if (pageH1 < state->getWinH()) {
580       offsetY = (state->getWinH() - pageH1) / 2;
581     } else {
582       offsetY = 0;
583     }
584     *xd = xw - offsetX + state->getScrollX();
585     *yd = yw - offsetY + state->getScrollY();
586     return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
587 
588   case displayContinuous:
589     if (totalH < state->getWinH()) {
590       offsetY = (state->getWinH() - totalH) / 2;
591     } else {
592       offsetY = 0;
593     }
594     *pg = findContinuousPage(yw - offsetY + state->getScrollY());
595     if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
596       *pg = 0;
597       *xd = *yd = 0;
598       return gFalse;
599     }
600     pageW1 = pageW[*pg - 1];
601     pageH1 = pageH[*pg - 1];
602     if (maxW < state->getWinW()) {
603       offsetX = (state->getWinW() - maxW) / 2;
604     } else {
605       offsetX = 0;
606     }
607     offsetX += (maxW - pageW1) / 2;
608     *xd = xw - offsetX + state->getScrollX();
609     *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
610     return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
611 
612   case displaySideBySideSingle:
613     pageW1 = pageW[state->getScrollPage() - 1];
614     pageH1 = pageH[state->getScrollPage() - 1];
615     if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
616       pageW2 = pageW[state->getScrollPage()];
617       pageH2 = pageH[state->getScrollPage()];
618     } else {
619       // display a single page as though there were a blank facing
620       // page of the same size
621       pageW2 = pageW1;
622       pageH2 = pageH1;
623     }
624     if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
625       offsetX = (state->getWinW() -
626 		 (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
627     } else {
628       offsetX = 0;
629     }
630     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
631     if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
632       if (pageH1 > pageH2) {
633 	offsetY = (state->getWinH() - pageH1) / 2;
634       } else {
635 	offsetY = (state->getWinH() - pageH2) / 2;
636       }
637     } else {
638       offsetY = 0;
639     }
640     if (xw + state->getScrollX() < offsetX2) {
641       *pg = state->getScrollPage();
642       *xd = xw - offsetX + state->getScrollX();
643       *yd = yw - offsetY + state->getScrollY();
644       return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
645     } else {
646       if (state->getScrollPage() + 1 > state->getDoc()->getNumPages()) {
647 	*pg = *xd = *yd = 0;
648 	return gFalse;
649       }
650       *pg = state->getScrollPage() + 1;
651       *xd = xw - offsetX2 + state->getScrollX();
652       *yd = yw - offsetY + state->getScrollY();
653       return *xd >= 0 && *xd < pageW2 && *yd >= 0 && *yd < pageH2;
654     }
655 
656   case displaySideBySideContinuous:
657     if (totalH < state->getWinH()) {
658       offsetY = (state->getWinH() - totalH) / 2;
659     } else {
660       offsetY = 0;
661     }
662     *pg = findSideBySideContinuousPage(yw - offsetY + state->getScrollY());
663     if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
664       *pg = 0;
665       *xd = *yd = 0;
666       return gFalse;
667     }
668     pageW1 = pageW[*pg - 1];
669     pageH1 = pageH[*pg - 1];
670     if (*pg + 1 <= state->getDoc()->getNumPages()) {
671       pageW2 = pageW[*pg];
672       pageH2 = pageH[*pg];
673     } else {
674       pageW2 = pageH2 = 0;
675     }
676     if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
677       offsetX = (state->getWinW() -
678 		 (maxW + sideBySidePageSpacing + maxW2)) / 2;
679     } else {
680       offsetX = 0;
681     }
682     offsetX += maxW - pageW1;
683     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
684     if (xw + state->getScrollX() < offsetX2) {
685       *xd = xw - offsetX + state->getScrollX();
686       *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
687       return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
688     } else {
689       if (*pg + 1 > state->getDoc()->getNumPages()) {
690 	*pg = *xd = *yd = 0;
691 	return false;
692       }
693       ++*pg;
694       *xd = xw - offsetX2 + state->getScrollX();
695       *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
696       return *xd >= 0 && *xd < pageW2 && *yd >= 0 && *yd < pageH2;
697     }
698 
699   case displayHorizontalContinuous:
700     if (totalW < state->getWinW()) {
701       offsetX = (state->getWinW() - totalW) / 2;
702     } else {
703       offsetX = 0;
704     }
705     *pg = findHorizContinuousPage(xw - offsetX + state->getScrollX());
706     if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
707       *pg = 0;
708       *xd = *yd = 0;
709       return gFalse;
710     }
711     pageW1 = pageW[*pg - 1];
712     pageH1 = pageH[*pg - 1];
713     if (maxH < state->getWinH()) {
714       offsetY = (state->getWinH() - maxH) / 2;
715     } else {
716       offsetY = 0;
717     }
718     *xd = xw - offsetX - pageX[*pg - 1] + state->getScrollX();
719     *yd = yw - offsetY + state->getScrollY();
720     return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
721   }
722 
723   return gFalse;
724 }
725 
cvtUserToWindow(int pg,double xu,double yu,int * xw,int * yw)726 GBool TileMap::cvtUserToWindow(int pg, double xu, double yu,
727 			       int *xw, int *yw) {
728   int xd, yd;
729 
730   cvtUserToDev(pg, xu, yu, &xd, &yd);
731   return cvtDevToWindow(pg, xd, yd, xw, yw);
732 }
733 
cvtDevToWindow(int pg,int xd,int yd,int * xw,int * yw)734 GBool TileMap::cvtDevToWindow(int pg, int xd, int yd,
735 			      int *xw, int *yw) {
736   int leftPg, pageW1, pageH1, pageW2, pageH2, offsetX, offsetX2, offsetY;
737 
738   if (!state->getDoc() ||
739       pg < 1 || pg > state->getDoc()->getNumPages()) {
740     *xw = *yw = 0;
741     return gFalse;
742   }
743 
744   updatePageParams();
745   updateContinuousModeParams();
746 
747   switch (state->getDisplayMode()) {
748 
749   case displaySingle:
750     if (pg != state->getScrollPage()) {
751       *xw = *yw = 0;
752       return gFalse;
753     }
754     pageW1 = pageW[pg - 1];
755     pageH1 = pageH[pg - 1];
756     if (pageW1 < state->getWinW()) {
757       offsetX = (state->getWinW() - pageW1) / 2;
758     } else {
759       offsetX = 0;
760     }
761     if (pageH1 < state->getWinH()) {
762       offsetY = (state->getWinH() - pageH1) / 2;
763     } else {
764       offsetY = 0;
765     }
766     *xw = xd + offsetX - state->getScrollX();
767     *yw = yd + offsetY - state->getScrollY();
768     break;
769 
770   case displayContinuous:
771     pageW1 = pageW[pg - 1];
772     pageH1 = pageH[pg - 1];
773     if (maxW < state->getWinW()) {
774       offsetX = (state->getWinW() - maxW) / 2;
775     } else {
776       offsetX = 0;
777     }
778     offsetX += (maxW - pageW1) / 2;
779     if (totalH < state->getWinH()) {
780       offsetY = (state->getWinH() - totalH) / 2;
781     } else {
782       offsetY = 0;
783     }
784     *xw = xd + offsetX - state->getScrollX();
785     *yw = pageY[pg - 1] + yd + offsetY - state->getScrollY();
786     break;
787 
788   case displaySideBySideSingle:
789     if (!(pg == state->getScrollPage() ||
790 	  (pg == state->getScrollPage() + 1 &&
791 	   state->getScrollPage() + 1 <= state->getDoc()->getNumPages()))) {
792       *xw = *yw = 0;
793       return gFalse;
794     }
795     pageW1 = pageW[state->getScrollPage() - 1];
796     pageH1 = pageH[state->getScrollPage() - 1];
797     if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
798       pageW2 = pageW[state->getScrollPage()];
799       pageH2 = pageH[state->getScrollPage()];
800     } else {
801       // display a single page as though there were a blank facing
802       // page of the same size
803       pageW2 = pageW1;
804       pageH2 = pageH1;
805     }
806     if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
807       offsetX = (state->getWinW() -
808 		 (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
809     } else {
810       offsetX = 0;
811     }
812     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
813     if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
814       if (pageH1 > pageH2) {
815 	offsetY = (state->getWinH() - pageH1) / 2;
816       } else {
817 	offsetY = (state->getWinH() - pageH2) / 2;
818       }
819     } else {
820       offsetY = 0;
821     }
822     if (pg == state->getScrollPage()) {
823       *xw = xd + offsetX - state->getScrollX();
824       *yw = yd + offsetY - state->getScrollY();
825     } else {
826       *xw = xd + offsetX2 - state->getScrollX();
827       *yw = yd + offsetY - state->getScrollY();
828     }
829     break;
830 
831   case displaySideBySideContinuous:
832     leftPg = (pg - 1) | 1;
833     pageW1 = pageW[leftPg - 1];
834     pageH1 = pageH[leftPg - 1];
835     if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
836       offsetX = (state->getWinW() -
837 		 (maxW + sideBySidePageSpacing + maxW2)) / 2;
838     } else {
839       offsetX = 0;
840     }
841     offsetX += maxW - pageW1;
842     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
843     if (totalH < state->getWinH()) {
844       offsetY = (state->getWinH() - totalH) / 2;
845     } else {
846       offsetY = 0;
847     }
848     if (pg == leftPg) {
849       *xw = xd + offsetX - state->getScrollX();
850     } else {
851       *xw = xd + offsetX2 - state->getScrollX();
852     }
853     *yw = pageY[pg - 1] + yd + offsetY - state->getScrollY();
854     break;
855 
856   case displayHorizontalContinuous:
857     if (totalW < state->getWinW()) {
858       offsetX = (state->getWinW() - totalW) / 2;
859     } else {
860       offsetX = 0;
861     }
862     if (maxH < state->getWinH()) {
863       offsetY = (state->getWinH() - maxH) / 2;
864     } else {
865       offsetY = 0;
866     }
867     *xw = pageX[pg - 1] + xd + offsetX - state->getScrollX();
868     *yw = yd + offsetY - state->getScrollY();
869     break;
870   }
871 
872   return gTrue;
873 }
874 
cvtUserToDev(int pg,double xu,double yu,int * xd,int * yd)875 void TileMap::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
876   double m[6];
877 
878   if (!state->getDoc() ||
879       pg < 1 || pg > state->getDoc()->getNumPages()) {
880     *xd = *yd = 0;
881     return;
882   }
883 
884   computePageMatrix(pg, m);
885   *xd = (int)(xu * m[0] + yu * m[2] + m[4] + 0.5);
886   *yd = (int)(xu * m[1] + yu * m[3] + m[5] + 0.5);
887 }
888 
cvtDevToUser(int pg,int xd,int yd,double * xu,double * yu)889 void TileMap::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
890   double m[6], im[6];
891 
892   if (!state->getDoc() ||
893       pg < 1 || pg > state->getDoc()->getNumPages()) {
894     *xu = *yu = 0;
895     return;
896   }
897 
898   computePageMatrix(pg, m);
899   invertMatrix(m, im);
900   *xu = xd * im[0] + yd * im[2] + im[4];
901   *yu = xd * im[1] + yd * im[3] + im[5];
902 }
903 
getWindowPageRange(int x,int y,int w,int h,int * firstPage,int * lastPage)904 void TileMap::getWindowPageRange(int x, int y, int w, int h,
905 				 int *firstPage, int *lastPage) {
906   GList *tiles;
907   PlacedTileDesc *tile;
908   int i;
909 
910   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
911     *firstPage = *lastPage = 0;
912     return;
913   }
914   *firstPage = state->getDoc()->getNumPages();
915   *lastPage = 0;
916   tiles = getTileList();
917   for (i = 0; i < tiles->getLength(); ++i) {
918     tile = (PlacedTileDesc *)tiles->get(i);
919     if (tile->px < x + w &&
920 	tile->px + tile->tw > x &&
921 	tile->py < y + h &&
922 	tile->py + tile->th > y) {
923       if (tile->page < *firstPage) {
924 	*firstPage = tile->page;
925       }
926       if (tile->page > *lastPage) {
927 	*lastPage = tile->page;
928       }
929     }
930   }
931 }
932 
getPageTopY(int page)933 int TileMap::getPageTopY(int page) {
934   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
935     return 0;
936   }
937 
938   updateContinuousModeParams();
939 
940   switch (state->getDisplayMode()) {
941   case displaySingle:
942   case displaySideBySideSingle:
943   case displayHorizontalContinuous:
944   default:
945     return 0;
946   case displayContinuous:
947   case displaySideBySideContinuous:
948     return pageY[page - 1];
949   }
950 }
951 
getPageBottomY(int page)952 int TileMap::getPageBottomY(int page) {
953   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
954     return 0;
955   }
956 
957   updatePageParams();
958   updateContinuousModeParams();
959 
960   switch (state->getDisplayMode()) {
961   case displaySingle:
962   case displaySideBySideSingle:
963   case displayHorizontalContinuous:
964   default:
965     return pageH[page - 1] - state->getWinH();
966   case displayContinuous:
967   case displaySideBySideContinuous:
968     return pageY[page - 1] + pageH[page - 1] - state->getWinH();
969   }
970 }
971 
getPageLeftX(int page)972 int TileMap::getPageLeftX(int page) {
973   int leftPage, rightPage, pageW1, pageW2, offsetX, offsetX2;
974 
975   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
976     return 0;
977   }
978 
979   updatePageParams();
980   updateContinuousModeParams();
981 
982   switch (state->getDisplayMode()) {
983   case displaySingle:
984   default:
985     return 0;
986   case displayContinuous:
987     return (maxW - pageW[page - 1]) / 2;
988   case displaySideBySideSingle:
989     leftPage = ((page - 1) & ~1) + 1;
990     rightPage = leftPage + 1;
991     pageW1 = pageW[leftPage - 1];
992     if (rightPage <= state->getDoc()->getNumPages()) {
993       pageW2 = pageW[rightPage - 1];
994     } else {
995       // display a single page as though there were a blank facing
996       // page of the same size
997       pageW2 = pageW1;
998     }
999     if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
1000       offsetX = (state->getWinW() -
1001 		 (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
1002     } else {
1003       offsetX = 0;
1004     }
1005     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
1006     return (page == leftPage) ? offsetX : offsetX2;
1007   case displaySideBySideContinuous:
1008     leftPage = ((page - 1) & ~1) + 1;
1009     rightPage = leftPage + 1;
1010     pageW1 = pageW[leftPage - 1];
1011     if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
1012       offsetX = (state->getWinW() -
1013 		 (maxW + sideBySidePageSpacing + maxW2)) / 2;
1014     } else {
1015       offsetX = 0;
1016     }
1017     offsetX += maxW - pageW1;
1018     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
1019     return (page == leftPage) ? offsetX : offsetX2;
1020   case displayHorizontalContinuous:
1021     return pageX[page - 1];
1022   }
1023 }
1024 
getPageRightX(int page)1025 int TileMap::getPageRightX(int page) {
1026   int leftPage, rightPage, pageW1, pageW2, offsetX, offsetX2;
1027 
1028   if (!state->getDoc() || !state->getDoc()->getNumPages()) {
1029     return 0;
1030   }
1031 
1032   updatePageParams();
1033   updateContinuousModeParams();
1034 
1035   switch (state->getDisplayMode()) {
1036   case displaySingle:
1037   default:
1038     return pageW[page - 1] - state->getWinW();
1039   case displayContinuous:
1040     return (maxW + pageW[page - 1]) / 2 - state->getWinW();
1041   case displaySideBySideSingle:
1042     leftPage = ((page - 1) & ~1) + 1;
1043     rightPage = leftPage + 1;
1044     pageW1 = pageW[leftPage - 1];
1045     if (rightPage <= state->getDoc()->getNumPages()) {
1046       pageW2 = pageW[rightPage - 1];
1047     } else {
1048       // display a single page as though there were a blank facing
1049       // page of the same size
1050       pageW2 = pageW1;
1051     }
1052     if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
1053       offsetX = (state->getWinW() -
1054 		 (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
1055     } else {
1056       offsetX = 0;
1057     }
1058     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
1059     return (page == leftPage) ? offsetX + pageW1 - state->getWinW()
1060                               : offsetX2 + pageW2 - state->getWinW();
1061   case displaySideBySideContinuous:
1062     leftPage = ((page - 1) & ~1) + 1;
1063     rightPage = leftPage + 1;
1064     pageW1 = pageW[leftPage - 1];
1065     if (rightPage <= state->getDoc()->getNumPages()) {
1066       pageW2 = pageW[rightPage - 1];
1067     } else {
1068       // display a single page as though there were a blank facing
1069       // page of the same size
1070       pageW2 = pageW1;
1071     }
1072     if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
1073       offsetX = (state->getWinW() -
1074 		 (maxW + sideBySidePageSpacing + maxW2)) / 2;
1075     } else {
1076       offsetX = 0;
1077     }
1078     offsetX += maxW - pageW1;
1079     offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
1080     return (page == leftPage) ? offsetX + pageW1 - state->getWinW()
1081                               : offsetX2 + pageW2 - state->getWinW();
1082   case displayHorizontalContinuous:
1083     return pageX[page - 1] + pageW[page - 1] - state->getWinW();
1084   }
1085 }
1086 
getFirstPage()1087 int TileMap::getFirstPage() {
1088   updateContinuousModeParams();
1089   switch (state->getDisplayMode()) {
1090   case displaySingle:
1091   default:
1092     return state->getScrollPage();
1093   case displayContinuous:
1094     return findContinuousPage(state->getScrollY());
1095   case displaySideBySideSingle:
1096     return state->getScrollPage();
1097   case displaySideBySideContinuous:
1098     return findSideBySideContinuousPage(state->getScrollY());
1099   case displayHorizontalContinuous:
1100     return findHorizContinuousPage(state->getScrollX());
1101   }
1102 }
1103 
getFirstPageTop()1104 int TileMap::getFirstPageTop() {
1105   int page;
1106 
1107   updateContinuousModeParams();
1108   switch (state->getDisplayMode()) {
1109   case displaySingle:
1110   default:
1111     return state->getScrollPage();
1112   case displayContinuous:
1113     page = findContinuousPage(state->getScrollY());
1114     if (page < state->getDoc()->getNumPages() &&
1115 	pageY[page - 1] < state->getScrollY()) {
1116       return page + 1;
1117     } else {
1118       return page;
1119     }
1120   case displaySideBySideSingle:
1121     return state->getScrollPage();
1122   case displaySideBySideContinuous:
1123     page = findSideBySideContinuousPage(state->getScrollY());
1124     if (page < state->getDoc()->getNumPages() &&
1125 	pageY[page - 1] < state->getScrollY()) {
1126       return page + 1;
1127     } else {
1128       return page;
1129     }
1130   case displayHorizontalContinuous:
1131     page = findHorizContinuousPage(state->getScrollX());
1132     if (page < state->getDoc()->getNumPages() &&
1133 	pageX[page - 1] < state->getScrollX()) {
1134       return page + 1;
1135     } else {
1136       return page;
1137     }
1138   }
1139 }
1140 
getMidPage()1141 int TileMap::getMidPage() {
1142   int wx, wy, pg, x, y;
1143 
1144   wx = state->getWinW() / 2;
1145   wy = state->getWinH() / 2;
1146   if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
1147     if (state->getDisplayMode() == displayContinuous) {
1148       wy += continuousPageSpacing;
1149     } else if (state->getDisplayMode() == displaySideBySideContinuous) {
1150       wx += sideBySidePageSpacing;
1151       wy += continuousPageSpacing;
1152     } else if (state->getDisplayMode() == displayHorizontalContinuous) {
1153       wx += horizContinuousPageSpacing;
1154     } else {
1155       return state->getScrollPage();
1156     }
1157     if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
1158       return 1;
1159     }
1160   }
1161   return pg;
1162 }
1163 
getLastPage()1164 int TileMap::getLastPage() {
1165   int pg, x, y, n;
1166 
1167   switch (state->getDisplayMode()) {
1168   case displaySingle:
1169   default:
1170     return state->getScrollPage();
1171   case displayContinuous:
1172     if (!cvtWindowToDev(state->getWinW() / 2, state->getWinH() - 1,
1173 			&pg, &x, &y)) {
1174       return state->getDoc()->getNumPages();
1175     }
1176     return pg;
1177   case displaySideBySideSingle:
1178     pg = state->getScrollPage() + 1;
1179     n = state->getDoc()->getNumPages();
1180     if (pg > n) {
1181       pg = n;
1182     }
1183     return pg;
1184   case displaySideBySideContinuous:
1185     if (!cvtWindowToDev(state->getWinW() / 2, state->getWinH() - 1,
1186 			&pg, &x, &y)) {
1187       return state->getScrollPage();
1188     }
1189     pg = ((pg - 1) & ~1) + 2;
1190     n = state->getDoc()->getNumPages();
1191     if (pg > n) {
1192       pg = n;
1193     }
1194     return pg;
1195   case displayHorizontalContinuous:
1196     x = state->getWinW() - 1;
1197     y = state->getWinH() / 2;
1198     if (!cvtWindowToDev(state->getWinW() - 1, state->getWinH() / 2,
1199 			&pg, &x, &y)) {
1200       return state->getDoc()->getNumPages();
1201     }
1202     return pg;
1203   }
1204 }
1205 
getDPI(int page)1206 double TileMap::getDPI(int page) {
1207   if (page < 1 || page > state->getDoc()->getNumPages()) {
1208     return 0;
1209   }
1210   updatePageParams();
1211   return pageDPI[page - 1];
1212 }
1213 
getPageBoxWidth(int page)1214 double TileMap::getPageBoxWidth(int page) {
1215   return pageBoxW[page - 1];
1216 }
1217 
getPageBoxHeight(int page)1218 double TileMap::getPageBoxHeight(int page) {
1219   return pageBoxH[page - 1];
1220 }
1221 
getContinuousPageSpacing()1222 int TileMap::getContinuousPageSpacing() {
1223   return continuousPageSpacing;
1224 }
1225 
getSideBySidePageSpacing()1226 int TileMap::getSideBySidePageSpacing() {
1227   return sideBySidePageSpacing;
1228 }
1229 
getHorizContinuousPageSpacing()1230 int TileMap::getHorizContinuousPageSpacing() {
1231   return horizContinuousPageSpacing;
1232 }
1233 
docChanged()1234 void TileMap::docChanged() {
1235   PDFDoc *doc;
1236   int nPages, pg, rot;
1237 
1238   doc = state->getDoc();
1239 
1240   if (doc) {
1241     nPages = doc->getNumPages();
1242   } else {
1243     nPages = 0;
1244   }
1245   pageBoxW = (double *)greallocn(pageBoxW, nPages, sizeof(double));
1246   pageBoxH = (double *)greallocn(pageBoxH, nPages, sizeof(double));
1247   for (pg = 1; pg <= nPages; ++pg) {
1248     rot = doc->getPageRotate(pg);
1249     if (rot == 0 || rot == 180) {
1250       pageBoxW[pg - 1] = doc->getPageCropWidth(pg);
1251       pageBoxH[pg - 1] = doc->getPageCropHeight(pg);
1252     } else {
1253       pageBoxW[pg - 1] = doc->getPageCropHeight(pg);
1254       pageBoxH[pg - 1] = doc->getPageCropWidth(pg);
1255     }
1256   }
1257 
1258   clearPageParams();
1259   clearContinuousModeParams();
1260   if (tiles) {
1261     deleteGList(tiles, PlacedTileDesc);
1262     tiles = NULL;
1263   }
1264 }
1265 
windowSizeChanged()1266 void TileMap::windowSizeChanged() {
1267   clearPageParams();
1268   clearContinuousModeParams();
1269   if (tiles) {
1270     deleteGList(tiles, PlacedTileDesc);
1271     tiles = NULL;
1272   }
1273 }
1274 
displayModeChanged()1275 void TileMap::displayModeChanged() {
1276   clearPageParams();
1277   clearContinuousModeParams();
1278   if (tiles) {
1279     deleteGList(tiles, PlacedTileDesc);
1280     tiles = NULL;
1281   }
1282 }
1283 
zoomChanged()1284 void TileMap::zoomChanged() {
1285   clearPageParams();
1286   clearContinuousModeParams();
1287   if (tiles) {
1288     deleteGList(tiles, PlacedTileDesc);
1289     tiles = NULL;
1290   }
1291 }
1292 
rotateChanged()1293 void TileMap::rotateChanged() {
1294   clearPageParams();
1295   clearContinuousModeParams();
1296   if (tiles) {
1297     deleteGList(tiles, PlacedTileDesc);
1298     tiles = NULL;
1299   }
1300 }
1301 
scrollPositionChanged()1302 void TileMap::scrollPositionChanged() {
1303   if (tiles) {
1304     deleteGList(tiles, PlacedTileDesc);
1305     tiles = NULL;
1306   }
1307 }
1308 
1309 
forceRedraw()1310 void TileMap::forceRedraw() {
1311   clearPageParams();
1312   clearContinuousModeParams();
1313   if (tiles) {
1314     deleteGList(tiles, PlacedTileDesc);
1315     tiles = NULL;
1316   }
1317 }
1318 
clearPageParams()1319 void TileMap::clearPageParams() {
1320   gfree(pageDPI);
1321   gfree(pageW);
1322   gfree(pageH);
1323   gfree(tileW);
1324   gfree(tileH);
1325   pageDPI = NULL;
1326   pageW = pageH = NULL;
1327   tileW = tileH = NULL;
1328 }
1329 
updatePageParams()1330 void TileMap::updatePageParams() {
1331   double rotPageBoxW, rotPageBoxW2, rotPageBoxH, rotPageBoxH2, rotPageBoxHMax;
1332   double hDPI, vDPI, dpi;
1333   int page, otherPage, nxTiles, nyTiles;
1334 
1335   //--- check to see if the continuous mode params have already been updated
1336   if (pageDPI) {
1337     return;
1338   }
1339 
1340   //--- allocate memory
1341   pageDPI = (double *)gmallocn(state->getDoc()->getNumPages(), sizeof(double));
1342   pageW = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
1343   pageH = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
1344   tileW = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
1345   tileH = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
1346 
1347   for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
1348 
1349     //--- special handling for side-by-side modes
1350     if (state->displayModeIsSideBySide()) {
1351 
1352       // rotate the page boxes
1353       if (page & 1) {
1354 	otherPage = page + 1;
1355 	if (otherPage >= state->getDoc()->getNumPages()) {
1356 	  otherPage = page;
1357 	}
1358       } else {
1359 	otherPage = page - 1;
1360 	if (otherPage < 1) {
1361 	  otherPage = page;
1362 	}
1363       }
1364       if (state->getRotate() == 0 || state->getRotate() == 180) {
1365 	rotPageBoxW = pageBoxW[page - 1];
1366 	rotPageBoxW2 = pageBoxW[otherPage - 1];
1367 	rotPageBoxH = pageBoxH[page - 1];
1368 	rotPageBoxH2 = pageBoxH[otherPage - 1];
1369       } else {
1370 	rotPageBoxW = pageBoxH[page - 1];
1371 	rotPageBoxW2 = pageBoxH[otherPage - 1];
1372 	rotPageBoxH = pageBoxW[page - 1];
1373 	rotPageBoxH2 = pageBoxW[otherPage - 1];
1374       }
1375       rotPageBoxHMax = (rotPageBoxH > rotPageBoxH2) ? rotPageBoxH
1376 	                                            : rotPageBoxH2;
1377 
1378       // compute resolution
1379       if (state->getZoom() == zoomPage) {
1380 	hDPI = ((state->getWinW() - sideBySidePageSpacing) /
1381 		(rotPageBoxW + rotPageBoxW2)) * 72.0;
1382 	vDPI = (state->getWinH() / rotPageBoxHMax) * 72.0;
1383 	dpi = hDPI < vDPI ? hDPI : vDPI;
1384 	// allow for some floating point jitter
1385 	dpi -= 0.01;
1386       } else if (state->getZoom() == zoomWidth) {
1387 	dpi = ((state->getWinW() - sideBySidePageSpacing) /
1388 	       (rotPageBoxW + rotPageBoxW2)) * 72.0;
1389 	// allow for some floating point jitter
1390 	dpi -= 0.01;
1391       } else if (state->getZoom() == zoomHeight) {
1392 	dpi = (state->getWinH() / rotPageBoxHMax) * 72.0;
1393 	// allow for some floating point jitter
1394 	dpi -= 0.01;
1395       } else {
1396 	dpi = 0.01 * state->getZoom() * 72.0;
1397       }
1398 
1399     //--- all other (non-side-by-side) modes
1400     } else {
1401 
1402       // rotate the page boxes
1403       if (state->getRotate() == 0 || state->getRotate() == 180) {
1404 	rotPageBoxW = pageBoxW[page - 1];
1405 	rotPageBoxH = pageBoxH[page - 1];
1406       } else {
1407 	rotPageBoxW = pageBoxH[page - 1];
1408 	rotPageBoxH = pageBoxW[page - 1];
1409       }
1410 
1411       // compute resolution
1412       if (state->getZoom() == zoomPage) {
1413 	hDPI = (state->getWinW() / rotPageBoxW) * 72.0;
1414 	vDPI = (state->getWinH() / rotPageBoxH) * 72.0;
1415 	dpi = hDPI < vDPI ? hDPI : vDPI;
1416 	// allow for some floating point jitter
1417 	dpi -= 0.01;
1418       } else if (state->getZoom() == zoomWidth) {
1419 	dpi = (state->getWinW() / rotPageBoxW) * 72.0;
1420 	// allow for some floating point jitter
1421 	dpi -= 0.01;
1422       } else if (state->getZoom() == zoomHeight) {
1423 	dpi = (state->getWinH() / rotPageBoxH) * 72.0;
1424 	// allow for some floating point jitter
1425 	dpi -= 0.01;
1426       } else {
1427 	dpi = 0.01 * state->getZoom() * 72.0;
1428       }
1429 
1430     }
1431     pageDPI[page - 1] = dpi;
1432 
1433     // compute bitmap size
1434     pageW[page - 1] = (int)((rotPageBoxW * dpi / 72.0) + 0.5);
1435     if (pageW[page - 1] < 1) {
1436       pageW[page - 1] = 1;
1437     }
1438     pageH[page - 1] = (int)((rotPageBoxH * dpi / 72.0) + 0.5);
1439     if (pageH[page - 1] < 1) {
1440       pageH[page - 1] = 1;
1441     }
1442 
1443     // compute tile size
1444     // (tile width and height are rounded up -- the bottom and right
1445     // tiles may be slightly smaller than the computed size)
1446     if (pageW[page - 1] <= state->getMaxTileWidth()) {
1447       nxTiles = 1;
1448       tileW[page - 1] = pageW[page - 1];
1449     } else {
1450       nxTiles = (pageW[page - 1] + state->getMaxTileWidth() - 1)
1451 	        / state->getMaxTileWidth();
1452       tileW[page - 1] = (pageW[page - 1] + nxTiles - 1) / nxTiles;
1453     }
1454     if (pageH[page - 1] <= state->getMaxTileHeight()) {
1455       nyTiles = 1;
1456       tileH[page - 1] = pageH[page - 1];
1457     } else {
1458       nyTiles = (pageH[page - 1] + state->getMaxTileHeight() - 1)
1459 	        / state->getMaxTileHeight();
1460       tileH[page - 1] = (pageH[page - 1] + nyTiles - 1) / nyTiles;
1461     }
1462 
1463   }
1464 }
1465 
clearContinuousModeParams()1466 void TileMap::clearContinuousModeParams() {
1467   gfree(pageX);
1468   pageX = pageY = NULL;
1469 }
1470 
updateContinuousModeParams()1471 void TileMap::updateContinuousModeParams() {
1472   int page, pageW1, pageH1, pageW2, pageH2, x, y;
1473 
1474   // check to see if the continuous mode params have already been updated
1475   if (pageX) {
1476     return;
1477   }
1478 
1479   updatePageParams();
1480 
1481   switch (state->getDisplayMode()) {
1482   case displayContinuous:
1483     if (!pageX) {
1484       pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
1485 				      sizeof(int));
1486     }
1487     y = 0;
1488     maxW = 0;
1489     for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
1490       pageY[page - 1] = y;
1491       y += pageH[page - 1] + continuousPageSpacing;
1492       if (page == 1 || pageW[page - 1] > maxW) {
1493 	maxW = pageW[page - 1];
1494       }
1495     }
1496     totalH = y - continuousPageSpacing;
1497     break;
1498   case displaySideBySideContinuous:
1499     if (!pageX) {
1500       pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
1501 				      sizeof(int));
1502     }
1503     y = 0;
1504     maxW = maxW2 = 0;
1505     for (page = 1; page <= state->getDoc()->getNumPages(); page += 2) {
1506       pageW1 = pageW[page - 1];
1507       pageH1 = pageH[page - 1];
1508       if (page + 1 <= state->getDoc()->getNumPages()) {
1509 	pageW2 = pageW[page];
1510 	pageH2 = pageH[page];
1511       } else {
1512 	pageW2 = pageW1;
1513 	pageH2 = pageH1;
1514       }
1515       pageY[page - 1] = y;
1516       if (page == 1 || pageW1 > maxW) {
1517 	maxW = pageW1;
1518       }
1519       if (page + 1 <= state->getDoc()->getNumPages()) {
1520 	pageY[page] = y;
1521       }
1522       if (pageW2 > maxW2) {
1523 	maxW2 = pageW2;
1524       }
1525       y += (pageH1 > pageH2) ? pageH1 : pageH2;
1526       y += continuousPageSpacing;
1527     }
1528     totalH = y - continuousPageSpacing;
1529     break;
1530   case displayHorizontalContinuous:
1531     if (!pageX) {
1532       pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
1533 				      sizeof(int));
1534     }
1535     x = 0;
1536     maxH = 0;
1537     for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
1538       pageX[page - 1] = x;
1539       x += pageW[page - 1] + horizContinuousPageSpacing;
1540       if (page == 1 || pageH[page - 1] > maxH) {
1541 	maxH = pageH[page - 1];
1542       }
1543     }
1544     totalW = x - horizContinuousPageSpacing;
1545     break;
1546   default:
1547     break;
1548   }
1549 }
1550 
computePageMatrix(int page,double * m)1551 void TileMap::computePageMatrix(int page, double *m) {
1552   PDFRectangle *cropBox;
1553   double px1, py1, px2, py2, k;
1554   int rotate;
1555 
1556   updatePageParams();
1557   cropBox = state->getDoc()->getCatalog()->getPage(page)->getCropBox();
1558   px1 = cropBox->x1;
1559   py1 = cropBox->y1;
1560   px2 = cropBox->x2;
1561   py2 = cropBox->y2;
1562   k = pageDPI[page - 1] / 72.0;
1563   rotate = state->getRotate() +
1564            state->getDoc()->getCatalog()->getPage(page)->getRotate();
1565   if (rotate > 360) {
1566     rotate -= 360;
1567   }
1568   switch (rotate) {
1569   case 0:
1570   default:
1571     m[0] = k;
1572     m[1] = 0;
1573     m[2] = 0;
1574     m[3] = -k;
1575     m[4] = -k * px1;
1576     m[5] = k * py2;
1577     break;
1578   case 90:
1579     m[0] = 0;
1580     m[1] = k;
1581     m[2] = k;
1582     m[3] = 0;
1583     m[4] = -k * py1;
1584     m[5] = -k * px1;
1585     break;
1586   case 180:
1587     m[0] = -k;
1588     m[1] = 0;
1589     m[2] = 0;
1590     m[3] = k;
1591     m[4] = k * px2;
1592     m[5] = -k * py1;
1593     break;
1594   case 270:
1595     m[0] = 0;
1596     m[1] = -k;
1597     m[2] = -k;
1598     m[3] = 0;
1599     m[4] = k * py2;
1600     m[5] = k * px2;
1601     break;
1602   }
1603 }
1604 
invertMatrix(double * m,double * im)1605 void TileMap::invertMatrix(double *m, double *im) {
1606   double det;
1607 
1608   det = 1 / (m[0] * m[3] - m[1] * m[2]);
1609   im[0] = m[3] * det;
1610   im[1] = -m[1] * det;
1611   im[2] = -m[2] * det;
1612   im[3] = m[0] * det;
1613   im[4] = (m[2] * m[5] - m[3] * m[4]) * det;
1614   im[5] = (m[1] * m[4] - m[0] * m[5]) * det;
1615 }
1616 
findContinuousPage(int y)1617 int TileMap::findContinuousPage(int y) {
1618   int a, b, m;
1619 
1620   if (y < pageY[0]) {
1621     return 0;
1622   }
1623   if (y >= totalH) {
1624     return state->getDoc()->getNumPages() + 1;
1625   }
1626   a = -1;
1627   b = state->getDoc()->getNumPages();
1628   // invariant: pageY[a] < y < pageY[b]
1629   while (b - a > 1) {
1630     m = (a + b) / 2;
1631     if (y > pageY[m] - continuousPageSpacing) {
1632       a = m;
1633     } else if (y < pageY[m] - continuousPageSpacing) {
1634       b = m;
1635     } else {
1636       return m + 1;
1637     }
1638   }
1639   return a + 1;
1640 }
1641 
findSideBySideContinuousPage(int y)1642 int TileMap::findSideBySideContinuousPage(int y) {
1643   int a, b, m;
1644 
1645   if (y < pageY[0]) {
1646     return 0;
1647   }
1648   if (y >= totalH) {
1649     return (state->getDoc()->getNumPages() + 2) & ~1;
1650   }
1651   a = -2;
1652   b = (state->getDoc()->getNumPages() + 1) & ~1;
1653   // invariant: pageY[a] < y < pageY[b]
1654   while (b - a > 2) {
1655     m = ((a + b) / 2) & ~1;
1656     if (y > pageY[m] - continuousPageSpacing) {
1657       a = m;
1658     } else if (y < pageY[m] - continuousPageSpacing) {
1659       b = m;
1660     } else {
1661       return m + 1;
1662     }
1663   }
1664   return a + 1;
1665 }
1666 
findHorizContinuousPage(int x)1667 int TileMap::findHorizContinuousPage(int x) {
1668   int a, b, m;
1669 
1670   if (x < pageX[0]) {
1671     return 0;
1672   }
1673   if (x >= totalW) {
1674     return state->getDoc()->getNumPages() + 1;
1675   }
1676   a = -1;
1677   b = state->getDoc()->getNumPages();
1678   // invariant: pageX[a] < x < pageX[b]
1679   while (b - a > 1) {
1680     m = (a + b) / 2;
1681     if (x > pageX[m] - horizContinuousPageSpacing) {
1682       a = m;
1683     } else if (x < pageX[m] - horizContinuousPageSpacing) {
1684       b = m;
1685     } else {
1686       return m + 1;
1687     }
1688   }
1689   return a + 1;
1690 }
1691