]> www.fi.muni.cz Git - evince.git/blob - pdf/splash/Splash.cc
Scroll at the top of the page when changing page
[evince.git] / pdf / splash / Splash.cc
1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
6
7 #include <aconf.h>
8
9 #ifdef USE_GCC_PRAGMAS
10 #pragma implementation
11 #endif
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include "gmem.h"
16 #include "SplashErrorCodes.h"
17 #include "SplashMath.h"
18 #include "SplashBitmap.h"
19 #include "SplashState.h"
20 #include "SplashPath.h"
21 #include "SplashXPath.h"
22 #include "SplashXPathScanner.h"
23 #include "SplashPattern.h"
24 #include "SplashScreen.h"
25 #include "SplashClip.h"
26 #include "SplashFont.h"
27 #include "SplashGlyphBitmap.h"
28 #include "Splash.h"
29
30 //------------------------------------------------------------------------
31 // Splash
32 //------------------------------------------------------------------------
33
34 Splash::Splash(SplashBitmap *bitmapA) {
35   bitmap = bitmapA;
36   state = new SplashState(bitmap->width, bitmap->height);
37   debugMode = gFalse;
38 }
39
40 Splash::~Splash() {
41   while (state->next) {
42     restoreState();
43   }
44   delete state;
45 }
46
47 //------------------------------------------------------------------------
48 // state read
49 //------------------------------------------------------------------------
50
51
52 SplashPattern *Splash::getStrokePattern() {
53   return state->strokePattern;
54 }
55
56 SplashPattern *Splash::getFillPattern() {
57   return state->fillPattern;
58 }
59
60 SplashScreen *Splash::getScreen() {
61   return state->screen;
62 }
63
64 SplashCoord Splash::getLineWidth() {
65   return state->lineWidth;
66 }
67
68 int Splash::getLineCap() {
69   return state->lineCap;
70 }
71
72 int Splash::getLineJoin() {
73   return state->lineJoin;
74 }
75
76 SplashCoord Splash::getMiterLimit() {
77   return state->miterLimit;
78 }
79
80 SplashCoord Splash::getFlatness() {
81   return state->flatness;
82 }
83
84 SplashCoord *Splash::getLineDash() {
85   return state->lineDash;
86 }
87
88 int Splash::getLineDashLength() {
89   return state->lineDashLength;
90 }
91
92 SplashCoord Splash::getLineDashPhase() {
93   return state->lineDashPhase;
94 }
95
96 SplashClip *Splash::getClip() {
97   return state->clip;
98 }
99
100 //------------------------------------------------------------------------
101 // state write
102 //------------------------------------------------------------------------
103
104 void Splash::setStrokePattern(SplashPattern *strokePattern) {
105   state->setStrokePattern(strokePattern);
106 }
107
108 void Splash::setFillPattern(SplashPattern *fillPattern) {
109   state->setFillPattern(fillPattern);
110 }
111
112 void Splash::setScreen(SplashScreen *screen) {
113   state->setScreen(screen);
114 }
115
116 void Splash::setLineWidth(SplashCoord lineWidth) {
117   state->lineWidth = lineWidth;
118 }
119
120 void Splash::setLineCap(int lineCap) {
121   state->lineCap = lineCap;
122 }
123
124 void Splash::setLineJoin(int lineJoin) {
125   state->lineJoin = lineJoin;
126 }
127
128 void Splash::setMiterLimit(SplashCoord miterLimit) {
129   state->miterLimit = miterLimit;
130 }
131
132 void Splash::setFlatness(SplashCoord flatness) {
133   if (flatness < 1) {
134     state->flatness = 1;
135   } else {
136     state->flatness = flatness;
137   }
138 }
139
140 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
141                          SplashCoord lineDashPhase) {
142   state->setLineDash(lineDash, lineDashLength, lineDashPhase);
143 }
144
145 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
146                              SplashCoord x1, SplashCoord y1) {
147   state->clip->resetToRect(x0, y0, x1, y1);
148 }
149
150 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
151                                SplashCoord x1, SplashCoord y1) {
152   return state->clip->clipToRect(x0, y0, x1, y1);
153 }
154
155 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
156   return state->clip->clipToPath(path, state->flatness, eo);
157 }
158
159 //------------------------------------------------------------------------
160 // state save/restore
161 //------------------------------------------------------------------------
162
163 void Splash::saveState() {
164   SplashState *newState;
165
166   newState = state->copy();
167   newState->next = state;
168   state = newState;
169 }
170
171 SplashError Splash::restoreState() {
172   SplashState *oldState;
173
174   if (!state->next) {
175     return splashErrNoSave;
176   }
177   oldState = state;
178   state = state->next;
179   delete oldState;
180   return splashOk;
181 }
182
183 //------------------------------------------------------------------------
184 // drawing operations
185 //------------------------------------------------------------------------
186
187 void Splash::clear(SplashColor color) {
188   SplashMono1P *mono1;
189   SplashMono8 *mono8;
190   SplashRGB8 *rgb8;
191   SplashRGB8P *rgb8pline, *rgb8p;
192   SplashBGR8P *bgr8line, *bgr8;
193   SplashMono1 data;
194   int n, i, x, y;
195
196   switch (bitmap->mode) {
197   case splashModeMono1:
198     n = ((bitmap->width + 7) >> 3) * bitmap->height;
199     data = color.mono1 ? 0xff : 0x00;
200     for (i = 0, mono1 = bitmap->data.mono1; i < n; ++i, ++mono1) {
201       *mono1 = data;
202     }
203     break;
204   case splashModeMono8:
205     n = bitmap->width * bitmap->height;
206     for (i = 0, mono8 = bitmap->data.mono8; i < n; ++i, ++mono8) {
207       *mono8 = color.mono8;
208     }
209     break;
210   case splashModeRGB8:
211     n = bitmap->width * bitmap->height;
212     for (i = 0, rgb8 = bitmap->data.rgb8; i < n; ++i, ++rgb8) {
213       *rgb8 = color.rgb8;
214     }
215     break;
216   case splashModeRGB8Packed:
217     rgb8pline = bitmap->data.rgb8p;
218     for (y = 0; y < bitmap->height; ++y) {
219       rgb8p = rgb8pline;
220       for (x = 0; x < bitmap->width; ++x) {
221         rgb8p[0] = splashRGB8R(color.rgb8);
222         rgb8p[1] = splashRGB8G(color.rgb8);
223         rgb8p[2] = splashRGB8B(color.rgb8);
224         rgb8p += 3;
225       }
226       rgb8pline += bitmap->rowSize;
227     }
228     break;
229   case splashModeBGR8Packed:
230     bgr8line = bitmap->data.bgr8;
231     for (y = 0; y < bitmap->height; ++y) {
232       bgr8 = bgr8line;
233       for (x = 0; x < bitmap->width; ++x) {
234         bgr8[2] = splashBGR8R(color.bgr8);
235         bgr8[1] = splashBGR8G(color.bgr8);
236         bgr8[0] = splashBGR8B(color.bgr8);
237         bgr8 += 3;
238       }
239       bgr8line += bitmap->rowSize;
240     }
241     break;
242   }
243 }
244
245 SplashError Splash::stroke(SplashPath *path) {
246   SplashXPath *xPath, *xPath2;
247
248   if (debugMode) {
249     printf("stroke [dash:%d] [width:%.2f]:\n",
250            state->lineDashLength, state->lineWidth);
251     dumpPath(path);
252   }
253   if (path->length == 0) {
254     return splashErrEmptyPath;
255   }
256   xPath = new SplashXPath(path, state->flatness, gFalse);
257   if (state->lineDashLength > 0) {
258     xPath2 = makeDashedPath(xPath);
259     delete xPath;
260     xPath = xPath2;
261   }
262   if (state->lineWidth <= 1) {
263     strokeNarrow(xPath);
264   } else {
265     strokeWide(xPath);
266   }
267   delete xPath;
268   return splashOk;
269 }
270
271 void Splash::strokeNarrow(SplashXPath *xPath) {
272   SplashXPathSeg *seg;
273   int x0, x1, x2, x3, y0, y1, x, y, t;
274   SplashCoord dx, dy, dxdy;
275   SplashClipResult clipRes;
276   int i;
277
278   for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
279
280     x0 = splashFloor(seg->x0);
281     x1 = splashFloor(seg->x1);
282     y0 = splashFloor(seg->y0);
283     y1 = splashFloor(seg->y1);
284
285     // horizontal segment
286     if (y0 == y1) {
287       if (x0 > x1) {
288         t = x0; x0 = x1; x1 = t;
289       }
290       if ((clipRes = state->clip->testSpan(x0, x1, y0))
291           != splashClipAllOutside) {
292         drawSpan(x0, x1, y0, state->strokePattern,
293                  clipRes == splashClipAllInside);
294       }
295
296     // segment with |dx| > |dy|
297     } else if (splashAbs(seg->dxdy) > 1) {
298       dx = seg->x1 - seg->x0;
299       dy = seg->y1 - seg->y0;
300       dxdy = seg->dxdy;
301       if (y0 > y1) {
302         t = y0; y0 = y1; y1 = t;
303         t = x0; x0 = x1; x1 = t;
304         dx = -dx;
305         dy = -dy;
306       }
307       if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
308                                            x0 <= x1 ? x1 : x0, y1))
309           != splashClipAllOutside) {
310         if (dx > 0) {
311           x2 = x0;
312           for (y = y0; y < y1; ++y) {
313             x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
314             drawSpan(x2, x3 - 1, y, state->strokePattern,
315                      clipRes == splashClipAllInside);
316             x2 = x3;
317           }
318           drawSpan(x2, x1, y, state->strokePattern,
319                    clipRes == splashClipAllInside);
320         } else {
321           x2 = x0;
322           for (y = y0; y < y1; ++y) {
323             x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
324             drawSpan(x3 + 1, x2, y, state->strokePattern,
325                      clipRes == splashClipAllInside);
326             x2 = x3;
327           }
328           drawSpan(x1, x2, y, state->strokePattern,
329                    clipRes == splashClipAllInside);
330         }
331       }
332
333     // segment with |dy| > |dx|
334     } else {
335       dxdy = seg->dxdy;
336       if (y0 > y1) {
337         t = y0; y0 = y1; y1 = t;
338       }
339       if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
340                                            x0 <= x1 ? x1 : x0, y1))
341           != splashClipAllOutside) {
342         for (y = y0; y <= y1; ++y) {
343           x = splashFloor(seg->x0 + (y - seg->y0) * dxdy);
344           drawPixel(x, y, state->strokePattern,
345                     clipRes == splashClipAllInside);
346         }
347       }
348     }
349   }
350 }
351
352 void Splash::strokeWide(SplashXPath *xPath) {
353   SplashXPathSeg *seg, *seg2;
354   SplashPath *widePath;
355   SplashCoord d, dx, dy, wdx, wdy, dxPrev, dyPrev, wdxPrev, wdyPrev;
356   SplashCoord dotprod, miter;
357   int i, j;
358
359   dx = dy = wdx = wdy = 0; // make gcc happy
360   dxPrev = dyPrev = wdxPrev = wdyPrev = 0; // make gcc happy
361
362   for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
363
364     // save the deltas for the previous segment; if this is the first
365     // segment on a subpath, compute the deltas for the last segment
366     // on the subpath (which may be used to draw a line join)
367     if (seg->flags & splashXPathFirst) {
368       for (j = i + 1, seg2 = &xPath->segs[j]; j < xPath->length; ++j, ++seg2) {
369         if (seg2->flags & splashXPathLast) {
370           d = splashDist(seg2->x0, seg2->y0, seg2->x1, seg2->y1);
371           if (d == 0) {
372             //~ not clear what the behavior should be for joins with d==0
373             dxPrev = 0;
374             dyPrev = 1;
375           } else {
376             d = 1 / d;
377             dxPrev = d * (seg2->x1 - seg2->x0);
378             dyPrev = d * (seg2->y1 - seg2->y0);
379           }
380           wdxPrev = 0.5 * state->lineWidth * dxPrev;
381           wdyPrev = 0.5 * state->lineWidth * dyPrev;
382           break;
383         }
384       }
385     } else {
386       dxPrev = dx;
387       dyPrev = dy;
388       wdxPrev = wdx;
389       wdyPrev = wdy;
390     }
391
392     // compute deltas for this line segment
393     d = splashDist(seg->x0, seg->y0, seg->x1, seg->y1);
394     if (d == 0) {
395       // we need to draw end caps on zero-length lines
396       //~ not clear what the behavior should be for splashLineCapButt with d==0
397       dx = 0;
398       dy = 1;
399     } else {
400       d = 1 / d;
401       dx = d * (seg->x1 - seg->x0);
402       dy = d * (seg->y1 - seg->y0);
403     }
404     wdx = 0.5 * state->lineWidth * dx;
405     wdy = 0.5 * state->lineWidth * dy;
406
407     // initialize the path (which will be filled)
408     widePath = new SplashPath();
409     widePath->moveTo(seg->x0 - wdy, seg->y0 + wdx);
410
411     // draw the start cap
412     if (seg->flags & splashXPathEnd0) {
413       switch (state->lineCap) {
414       case splashLineCapButt:
415         widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
416         break;
417       case splashLineCapRound:
418         widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
419         break;
420       case splashLineCapProjecting:
421         widePath->lineTo(seg->x0 - wdx - wdy, seg->y0 + wdx - wdy);
422         widePath->lineTo(seg->x0 - wdx + wdy, seg->y0 - wdx - wdy);
423         widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
424         break;
425       }
426     } else {
427       widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
428     }
429
430     // draw the left side of the segment
431     widePath->lineTo(seg->x1 + wdy, seg->y1 - wdx);
432
433     // draw the end cap
434     if (seg->flags & splashXPathEnd1) {
435       switch (state->lineCap) {
436       case splashLineCapButt:
437         widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
438         break;
439       case splashLineCapRound:
440         widePath->arcCWTo(seg->x1 - wdy, seg->y1 + wdx, seg->x1, seg->y1);
441         break;
442       case splashLineCapProjecting:
443         widePath->lineTo(seg->x1 + wdx + wdy, seg->y1 - wdx + wdy);
444         widePath->lineTo(seg->x1 + wdx - wdy, seg->y1 + wdx + wdy);
445         widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
446         break;
447       }
448     } else {
449       widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
450     }
451
452     // draw the right side of the segment
453     widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
454
455     // fill the segment
456     fillWithPattern(widePath, gTrue, state->strokePattern);
457     delete widePath;
458
459     // draw the line join
460     if (!(seg->flags & splashXPathEnd0)) {
461       widePath = NULL;
462       switch (state->lineJoin) {
463       case splashLineJoinMiter:
464         dotprod = -(dx * dxPrev + dy * dyPrev);
465         if (dotprod != 1) {
466           widePath = new SplashPath();
467           widePath->moveTo(seg->x0, seg->y0);
468           miter = 2 / (1 - dotprod);
469           if (splashSqrt(miter) <= state->miterLimit) {
470             miter = splashSqrt(miter - 1);
471             if (dy * dxPrev > dx * dyPrev) {
472               widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
473               widePath->lineTo(seg->x0 + wdy - miter * wdx,
474                                seg->y0 - wdx - miter * wdy);
475               widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
476             } else {
477               widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
478               widePath->lineTo(seg->x0 - wdy - miter * wdx,
479                                seg->y0 + wdx - miter * wdy);
480               widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
481             }
482           } else {
483             if (dy * dxPrev > dx * dyPrev) {
484               widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
485               widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
486             } else {
487               widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
488               widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
489             }
490           }
491         }
492         break;
493       case splashLineJoinRound:
494         widePath = new SplashPath();
495         widePath->moveTo(seg->x0 + wdy, seg->y0 - wdx);
496         widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
497         break;
498       case splashLineJoinBevel:
499         widePath = new SplashPath();
500         widePath->moveTo(seg->x0, seg->y0);
501         if (dy * dxPrev > dx * dyPrev) {
502           widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
503           widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
504         } else {
505           widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
506           widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
507         }
508         break;
509       }
510       if (widePath) {
511         fillWithPattern(widePath, gTrue, state->strokePattern);
512         delete widePath;
513       }
514     }
515   }
516 }
517
518 SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) {
519   SplashXPath *dPath;
520   GBool lineDashStartOn, lineDashOn;
521   GBool atSegStart, atSegEnd, atDashStart, atDashEnd;
522   int lineDashStartIdx, lineDashIdx, subpathStart;
523   SplashCoord lineDashTotal, lineDashStartPhase, lineDashDist;
524   int segIdx;
525   SplashXPathSeg *seg;
526   SplashCoord sx0, sy0, sx1, sy1, ax0, ay0, ax1, ay1, dist;
527   int i;
528
529   dPath = new SplashXPath();
530
531   lineDashTotal = 0;
532   for (i = 0; i < state->lineDashLength; ++i) {
533     lineDashTotal += state->lineDash[i];
534   }
535   lineDashStartPhase = state->lineDashPhase;
536   i = splashFloor(lineDashStartPhase / lineDashTotal);
537   lineDashStartPhase -= i * lineDashTotal;
538   lineDashStartOn = gTrue;
539   lineDashStartIdx = 0;
540   while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
541     lineDashStartOn = !lineDashStartOn;
542     lineDashStartPhase -= state->lineDash[lineDashStartIdx];
543     ++lineDashStartIdx;
544   }
545
546   segIdx = 0;
547   seg = xPath->segs;
548   sx0 = seg->x0;
549   sy0 = seg->y0;
550   sx1 = seg->x1;
551   sy1 = seg->y1;
552   dist = splashDist(sx0, sy0, sx1, sy1);
553   lineDashOn = lineDashStartOn;
554   lineDashIdx = lineDashStartIdx;
555   lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
556   atSegStart = gTrue;
557   atDashStart = gTrue;
558   subpathStart = dPath->length;
559
560   while (segIdx < xPath->length) {
561
562     ax0 = sx0;
563     ay0 = sy0;
564     if (dist <= lineDashDist) {
565       ax1 = sx1;
566       ay1 = sy1;
567       lineDashDist -= dist;
568       dist = 0;
569       atSegEnd = gTrue;
570       atDashEnd = lineDashDist == 0 || (seg->flags & splashXPathLast);
571     } else {
572       ax1 = sx0 + (lineDashDist / dist) * (sx1 - sx0);
573       ay1 = sy0 + (lineDashDist / dist) * (sy1 - sy0);
574       sx0 = ax1;
575       sy0 = ay1;
576       dist -= lineDashDist;
577       lineDashDist = 0;
578       atSegEnd = gFalse;
579       atDashEnd = gTrue;
580     }
581
582     if (lineDashOn) {
583       dPath->addSegment(ax0, ay0, ax1, ay1,
584                         atDashStart, atDashEnd,
585                         atDashStart, atDashEnd);
586       // end of closed subpath
587       if (atSegEnd &&
588           (seg->flags & splashXPathLast) &&
589           !(seg->flags & splashXPathEnd1)) {
590         dPath->segs[subpathStart].flags &= ~splashXPathEnd0;
591         dPath->segs[dPath->length - 1].flags &= ~splashXPathEnd1;
592       }
593     }
594
595     if (atDashEnd) {
596       lineDashOn = !lineDashOn;
597       if (++lineDashIdx == state->lineDashLength) {
598         lineDashIdx = 0;
599       }
600       lineDashDist = state->lineDash[lineDashIdx];
601       atDashStart = gTrue;
602     } else {
603       atDashStart = gFalse;
604     }
605     if (atSegEnd) {
606       if (++segIdx < xPath->length) {
607         ++seg;
608         sx0 = seg->x0;
609         sy0 = seg->y0;
610         sx1 = seg->x1;
611         sy1 = seg->y1;
612         dist = splashDist(sx0, sy0, sx1, sy1);
613         if (seg->flags & splashXPathFirst) {
614           lineDashOn = lineDashStartOn;
615           lineDashIdx = lineDashStartIdx;
616           lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
617           atDashStart = gTrue;
618           subpathStart = dPath->length;
619         }
620       }
621       atSegStart = gTrue;
622     } else {
623       atSegStart = gFalse;
624     }
625   }
626
627   return dPath;
628 }
629
630 SplashError Splash::fill(SplashPath *path, GBool eo) {
631   if (debugMode) {
632     printf("fill [eo:%d]:\n", eo);
633     dumpPath(path);
634   }
635   return fillWithPattern(path, eo, state->fillPattern);
636 }
637
638 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
639                                     SplashPattern *pattern) {
640   SplashXPath *xPath;
641   SplashXPathScanner *scanner;
642   int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
643   SplashClipResult clipRes, clipRes2;
644
645   if (path->length == 0) {
646     return splashErrEmptyPath;
647   }
648   xPath = new SplashXPath(path, state->flatness, gTrue);
649   xPath->sort();
650   scanner = new SplashXPathScanner(xPath, eo);
651
652   // get the min and max x and y values
653   scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
654
655   // check clipping
656   if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
657       != splashClipAllOutside) {
658
659     // draw the spans
660     for (y = yMinI; y <= yMaxI; ++y) {
661       while (scanner->getNextSpan(y, &x0, &x1)) {
662         if (clipRes == splashClipAllInside) {
663           drawSpan(x0, x1, y, pattern, gTrue);
664         } else {
665           clipRes2 = state->clip->testSpan(x0, x1, y);
666           drawSpan(x0, x1, y, pattern, clipRes2 == splashClipAllInside);
667         }
668       }
669     }
670   }
671
672   delete scanner;
673   delete xPath;
674   return splashOk;
675 }
676
677 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
678   SplashXPath *xPath;
679   SplashXPathScanner *scanner;
680   int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
681   SplashClipResult clipRes, clipRes2;
682
683   if (path->length == 0) {
684     return splashErrEmptyPath;
685   }
686   xPath = new SplashXPath(path, state->flatness, gTrue);
687   xPath->sort();
688   scanner = new SplashXPathScanner(xPath, eo);
689
690   // get the min and max x and y values
691   scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
692
693   // check clipping
694   if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
695       != splashClipAllOutside) {
696
697     // draw the spans
698     for (y = yMinI; y <= yMaxI; ++y) {
699       while (scanner->getNextSpan(y, &x0, &x1)) {
700         if (clipRes == splashClipAllInside) {
701           xorSpan(x0, x1, y, state->fillPattern, gTrue);
702         } else {
703           clipRes2 = state->clip->testSpan(x0, x1, y);
704           xorSpan(x0, x1, y, state->fillPattern,
705                   clipRes2 == splashClipAllInside);
706         }
707       }
708     }
709   }
710
711   delete scanner;
712   delete xPath;
713   return splashOk;
714 }
715
716 void Splash::drawPixel(int x, int y, SplashColor *color, GBool noClip) {
717   SplashMono1P *mono1;
718   SplashRGB8P *rgb8p;
719   SplashBGR8P *bgr8;
720
721   if (noClip || state->clip->test(x, y)) {
722     switch (bitmap->mode) {
723     case splashModeMono1:
724       mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
725       if (color->mono1) {
726         *mono1 |= 0x80 >> (x & 7);
727       } else {
728         *mono1 &= ~(0x80 >> (x & 7));
729       }
730       break;
731     case splashModeMono8:
732       bitmap->data.mono8[y * bitmap->width + x] = color->mono8;
733       break;
734     case splashModeRGB8:
735       bitmap->data.rgb8[y * bitmap->width + x] = color->rgb8;
736       break;
737     case splashModeRGB8Packed:
738       rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x];
739       rgb8p[0] = splashRGB8R(color->rgb8);
740       rgb8p[1] = splashRGB8G(color->rgb8);
741       rgb8p[2] = splashRGB8B(color->rgb8);
742       break;
743     case splashModeBGR8Packed:
744       bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
745       bgr8[2] = splashBGR8R(color->bgr8);
746       bgr8[1] = splashBGR8G(color->bgr8);
747       bgr8[0] = splashBGR8B(color->bgr8);
748       break;
749     }
750   }
751 }
752
753 void Splash::drawPixel(int x, int y, SplashPattern *pattern, GBool noClip) {
754   SplashColor color;
755   SplashMono1P *mono1;
756   SplashRGB8P *rgb8p;
757   SplashBGR8P *bgr8;
758
759   if (noClip || state->clip->test(x, y)) {
760     color = pattern->getColor(x, y);
761     switch (bitmap->mode) {
762     case splashModeMono1:
763       mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
764       if (color.mono1) {
765         *mono1 |= 0x80 >> (x & 7);
766       } else {
767         *mono1 &= ~(0x80 >> (x & 7));
768       }
769       break;
770     case splashModeMono8:
771       bitmap->data.mono8[y * bitmap->width + x] = color.mono8;
772       break;
773     case splashModeRGB8:
774       bitmap->data.rgb8[y * bitmap->width + x] = color.rgb8;
775       break;
776     case splashModeRGB8Packed:
777       rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x];
778       rgb8p[0] = splashRGB8R(color.rgb8);
779       rgb8p[1] = splashRGB8G(color.rgb8);
780       rgb8p[2] = splashRGB8B(color.rgb8);
781       break;
782     case splashModeBGR8Packed:
783       bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
784       bgr8[2] = splashBGR8R(color.bgr8);
785       bgr8[1] = splashBGR8G(color.bgr8);
786       bgr8[0] = splashBGR8B(color.bgr8);
787       break;
788     }
789   }
790 }
791
792 void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern,
793                       GBool noClip) {
794   SplashColor color;
795   SplashMono1P *mono1;
796   SplashMono8 *mono8;
797   SplashRGB8 *rgb8;
798   SplashRGB8P *rgb8p;
799   SplashBGR8P *bgr8;
800   SplashMono1 mask1;
801   int i, j, n;
802
803   n = x1 - x0 + 1;
804
805   switch (bitmap->mode) {
806   case splashModeMono1:
807     mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
808     i = 0;
809     if ((j = x0 & 7)) {
810       mask1 = 0x80 >> j;
811       for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
812         if (noClip || state->clip->test(x0 + i, y)) {
813           color = pattern->getColor(x0 + i, y);
814           if (color.mono1) {
815             *mono1 |= mask1;
816           } else {
817             *mono1 &= ~mask1;
818           }
819         }
820         mask1 >>= 1;
821       }
822       ++mono1;
823     }
824     while (i < n) {
825       mask1 = 0x80;
826       for (j = 0; j < 8 && i < n; ++i, ++j) {
827         if (noClip || state->clip->test(x0 + i, y)) {
828           color = pattern->getColor(x0 + i, y);
829           if (color.mono1) {
830             *mono1 |= mask1;
831           } else {
832             *mono1 &= ~mask1;
833           }
834         }
835         mask1 >>= 1;
836       }
837       ++mono1;
838     }
839     break;
840
841   case splashModeMono8:
842     mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
843     for (i = 0; i < n; ++i) {
844       if (noClip || state->clip->test(x0 + i, y)) {
845         color = pattern->getColor(x0 + i, y);
846         *mono8 = color.mono8;
847       }
848       ++mono8;
849     }
850     break;
851
852   case splashModeRGB8:
853     rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
854     for (i = 0; i < n; ++i) {
855       if (noClip || state->clip->test(x0 + i, y)) {
856         color = pattern->getColor(x0 + i, y);
857         *rgb8 = color.rgb8;
858       }
859       ++rgb8;
860     }
861     break;
862
863   case splashModeRGB8Packed:
864     rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x0];
865     for (i = 0; i < n; ++i) {
866       if (noClip || state->clip->test(x0 + i, y)) {
867         color = pattern->getColor(x0 + i, y);
868         rgb8p[0] = splashRGB8R(color.rgb8);
869         rgb8p[1] = splashRGB8G(color.rgb8);
870         rgb8p[2] = splashRGB8B(color.rgb8);
871       }
872       rgb8p += 3;
873     }
874     break;
875
876   case splashModeBGR8Packed:
877     bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
878     for (i = 0; i < n; ++i) {
879       if (noClip || state->clip->test(x0 + i, y)) {
880         color = pattern->getColor(x0 + i, y);
881         bgr8[2] = splashBGR8R(color.bgr8);
882         bgr8[1] = splashBGR8G(color.bgr8);
883         bgr8[0] = splashBGR8B(color.bgr8);
884       }
885       bgr8 += 3;
886     }
887     break;
888   }
889 }
890
891 void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern,
892                      GBool noClip) {
893   SplashColor color;
894   SplashMono1P *mono1;
895   SplashMono8 *mono8;
896   SplashRGB8 *rgb8;
897   SplashRGB8P *rgb8p;
898   SplashBGR8P *bgr8;
899   SplashMono1 mask1;
900   int i, j, n;
901
902   n = x1 - x0 + 1;
903
904   switch (bitmap->mode) {
905   case splashModeMono1:
906     mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
907     i = 0;
908     if ((j = x0 & 7)) {
909       mask1 = 0x80 >> j;
910       for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
911         if (noClip || state->clip->test(x0 + i, y)) {
912           color = pattern->getColor(x0 + i, y);
913           if (color.mono1) {
914             *mono1 ^= mask1;
915           }
916         }
917         mask1 >>= 1;
918       }
919       ++mono1;
920     }
921     while (i < n) {
922       mask1 = 0x80;
923       for (j = 0; j < 8 && i < n; ++i, ++j) {
924         if (noClip || state->clip->test(x0 + i, y)) {
925           color = pattern->getColor(x0 + i, y);
926           if (color.mono1) {
927             *mono1 ^= mask1;
928           }
929         }
930         mask1 >>= 1;
931       }
932       ++mono1;
933     }
934     break;
935
936   case splashModeMono8:
937     mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
938     for (i = 0; i < n; ++i) {
939       if (noClip || state->clip->test(x0 + i, y)) {
940         color = pattern->getColor(x0 + i, y);
941         *mono8 ^= color.mono8;
942       }
943       ++mono8;
944     }
945     break;
946
947   case splashModeRGB8:
948     rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
949     for (i = 0; i < n; ++i) {
950       if (noClip || state->clip->test(x0 + i, y)) {
951         color = pattern->getColor(x0 + i, y);
952         *rgb8 ^= color.rgb8;
953       }
954       ++rgb8;
955     }
956     break;
957
958   case splashModeRGB8Packed:
959     rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x0];
960     for (i = 0; i < n; ++i) {
961       if (noClip || state->clip->test(x0 + i, y)) {
962         color = pattern->getColor(x0 + i, y);
963         rgb8p[0] ^= splashRGB8R(color.rgb8);
964         rgb8p[1] ^= splashRGB8G(color.rgb8);
965         rgb8p[2] ^= splashRGB8B(color.rgb8);
966       }
967       rgb8p += 3;
968     }
969     break;
970
971   case splashModeBGR8Packed:
972     bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
973     for (i = 0; i < n; ++i) {
974       if (noClip || state->clip->test(x0 + i, y)) {
975         color = pattern->getColor(x0 + i, y);
976         bgr8[2] ^= splashBGR8R(color.bgr8);
977         bgr8[1] ^= splashBGR8G(color.bgr8);
978         bgr8[0] ^= splashBGR8B(color.bgr8);
979       }
980       bgr8 += 3;
981     }
982     break;
983   }
984 }
985
986 void Splash::getPixel(int x, int y, SplashColor *pixel) {
987   SplashRGB8P *rgb8p;
988   SplashBGR8P *bgr8;
989
990   if (y < 0 || y >= bitmap->height || x < 0 || x >= bitmap->width) {
991     return;
992   }
993   switch (bitmap->mode) {
994   case splashModeMono1:
995     pixel->mono1 = (bitmap->data.mono1[y * bitmap->rowSize + (x >> 3)]
996                     >> (7 - (x & 7))) & 1;
997     break;
998   case splashModeMono8:
999     pixel->mono8 = bitmap->data.mono8[y * bitmap->width + x];
1000     break;
1001   case splashModeRGB8:
1002     pixel->rgb8 = bitmap->data.rgb8[y * bitmap->width + x];
1003     break;
1004   case splashModeRGB8Packed:
1005     rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x];
1006     pixel->rgb8 = splashMakeRGB8(rgb8p[0], rgb8p[1], rgb8p[2]);
1007     break;
1008   case splashModeBGR8Packed:
1009     bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
1010     pixel->bgr8 = splashMakeBGR8(bgr8[2], bgr8[1], bgr8[0]);
1011     break;
1012   }
1013 }
1014
1015 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
1016                              int c, SplashFont *font) {
1017   SplashGlyphBitmap glyph;
1018   int x0, y0, xFrac, yFrac;
1019   SplashError err;
1020
1021   if (debugMode) {
1022     printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
1023            x, y, c, c, c);
1024   }
1025   x0 = splashFloor(x);
1026   xFrac = splashFloor((x - x0) * splashFontFraction);
1027   y0 = splashFloor(y);
1028   yFrac = splashFloor((y - y0) * splashFontFraction);
1029   if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
1030     return splashErrNoGlyph;
1031   }
1032   err = fillGlyph(x, y, &glyph);
1033   if (glyph.freeData) {
1034     gfree(glyph.data);
1035   }
1036   return err;
1037 }
1038
1039 SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
1040                               SplashGlyphBitmap *glyph) {
1041   int alpha, ialpha;
1042   Guchar *p;
1043   SplashColor fg;
1044   SplashMono1P *mono1Ptr;
1045   SplashMono8 *mono8Ptr;
1046   SplashRGB8 *rgb8Ptr;
1047   SplashRGB8P *rgb8pPtr;
1048   SplashBGR8P *bgr8Ptr;
1049   SplashMono8 bgMono8;
1050   int bgR, bgG, bgB;
1051   SplashClipResult clipRes;
1052   GBool noClip;
1053   int x0, y0, x1, y1, xx, xx1, yy;
1054
1055   x0 = splashFloor(x);
1056   y0 = splashFloor(y);
1057
1058   if ((clipRes = state->clip->testRect(x0 - glyph->x,
1059                                        y0 - glyph->y,
1060                                        x0 - glyph->x + glyph->w - 1,
1061                                        y0 - glyph->y + glyph->h - 1))
1062       != splashClipAllOutside) {
1063     noClip = clipRes == splashClipAllInside;
1064
1065     //~ optimize this
1066     if (glyph->aa) {
1067       p = glyph->data;
1068       for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1069         for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1070           alpha = *p++;
1071           if (alpha > 0) {
1072             if (noClip || state->clip->test(x1, y1)) {
1073               ialpha = 255 - alpha;
1074               fg = state->fillPattern->getColor(x1, y1);
1075               switch (bitmap->mode) {
1076               case splashModeMono1:
1077                 if (alpha >= 0x80) {
1078                   mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
1079                                                  (x1 >> 3)];
1080                   if (fg.mono1) {
1081                     *mono1Ptr |= 0x80 >> (x1 & 7);
1082                   } else {
1083                     *mono1Ptr &= ~(0x80 >> (x1 & 7));
1084                   }
1085                 }
1086                 break;
1087               case splashModeMono8:
1088                 mono8Ptr = &bitmap->data.mono8[y1 * bitmap->width + x1];
1089                 bgMono8 = *mono8Ptr;
1090                 // note: floor(x / 255) = x >> 8 (for 16-bit x)
1091                 *mono8Ptr = (alpha * fg.mono8 + ialpha * bgMono8) >> 8;
1092                 break;
1093               case splashModeRGB8:
1094                 rgb8Ptr = &bitmap->data.rgb8[y1 * bitmap->width + x1];
1095                 bgR = splashRGB8R(*rgb8Ptr);
1096                 bgG = splashRGB8G(*rgb8Ptr);
1097                 bgB = splashRGB8B(*rgb8Ptr);
1098                 *rgb8Ptr = splashMakeRGB8((alpha * splashRGB8R(fg.rgb8) +
1099                                            ialpha * bgR) >> 8,
1100                                           (alpha * splashRGB8G(fg.rgb8) +
1101                                            ialpha * bgG) >> 8,
1102                                           (alpha * splashRGB8B(fg.rgb8) +
1103                                            ialpha * bgB) >> 8);
1104                 break;
1105               case splashModeRGB8Packed:
1106                 rgb8pPtr = &bitmap->data.rgb8p[y1 * bitmap->rowSize + 3 * x1];
1107                 rgb8pPtr[0] =
1108                     (alpha * splashRGB8R(fg.rgb8) + ialpha * rgb8pPtr[0]) >> 8;
1109                 rgb8pPtr[1] =
1110                     (alpha * splashRGB8G(fg.rgb8) + ialpha * rgb8pPtr[1]) >> 8;
1111                 rgb8pPtr[2] =
1112                     (alpha * splashRGB8B(fg.rgb8) + ialpha * rgb8pPtr[2]) >> 8;
1113                 break;
1114               case splashModeBGR8Packed:
1115                 bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
1116                 bgr8Ptr[2] =
1117                     (alpha * splashBGR8R(fg.bgr8) + ialpha * bgr8Ptr[2]) >> 8;
1118                 bgr8Ptr[1] =
1119                     (alpha * splashBGR8G(fg.bgr8) + ialpha * bgr8Ptr[1]) >> 8;
1120                 bgr8Ptr[0] =
1121                     (alpha * splashBGR8B(fg.bgr8) + ialpha * bgr8Ptr[0]) >> 8;
1122                 break;
1123               }
1124             }
1125           }
1126         }
1127       }
1128
1129     } else {
1130       p = glyph->data;
1131       for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1132         for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1133           alpha = *p++;
1134           for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1135             if (alpha & 0x80) {
1136               if (noClip || state->clip->test(x1, y1)) {
1137                 fg = state->fillPattern->getColor(x1, y1);
1138                 switch (bitmap->mode) {
1139                 case splashModeMono1:
1140                   mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
1141                                                  (x1 >> 3)];
1142                   if (fg.mono1) {
1143                     *mono1Ptr |= 0x80 >> (x1 & 7);
1144                   } else {
1145                     *mono1Ptr &= ~(0x80 >> (x1 & 7));
1146                   }
1147                   break;
1148                 case splashModeMono8:
1149                   bitmap->data.mono8[y1 * bitmap->width + x1] = fg.mono8;
1150                   break;
1151                 case splashModeRGB8:
1152                   bitmap->data.rgb8[y1 * bitmap->width + x1] = fg.rgb8;
1153                   break;
1154                 case splashModeRGB8Packed:
1155                   rgb8pPtr = &bitmap->data.rgb8p[y1 * bitmap->rowSize + 3 * x1];
1156                   rgb8pPtr[0] = splashRGB8R(fg.rgb8);
1157                   rgb8pPtr[1] = splashRGB8G(fg.rgb8);
1158                   rgb8pPtr[2] = splashRGB8B(fg.rgb8);
1159                   break;
1160                 case splashModeBGR8Packed:
1161                   bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
1162                   bgr8Ptr[2] = splashBGR8R(fg.bgr8);
1163                   bgr8Ptr[1] = splashBGR8G(fg.bgr8);
1164                   bgr8Ptr[0] = splashBGR8B(fg.bgr8);
1165                   break;
1166                 }
1167               }
1168             }
1169             alpha <<= 1;
1170           }
1171         }
1172       }
1173     }
1174   }
1175
1176   return splashOk;
1177 }
1178
1179 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
1180                                   int w, int h, SplashCoord *mat) {
1181   GBool rot;
1182   SplashCoord xScale, yScale, xShear, yShear;
1183   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
1184   int ulx, uly, llx, lly, urx, ury, lrx, lry;
1185   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1186   int xMin, xMax, yMin, yMax;
1187   SplashClipResult clipRes, clipRes2;
1188   int yp, yq, yt, yStep, lastYStep;
1189   int xp, xq, xt, xStep, xSrc;
1190   int k1, spanXMin, spanXMax, spanY;
1191   SplashMono1 *pixBuf;
1192   SplashMono1 *p;
1193   int pixAcc;
1194   SplashCoord alpha;
1195   SplashColor fg, bg, pix;
1196   int x, y, x1, y1, x2, y2;
1197   int n, m, i, j;
1198
1199   if (debugMode) {
1200     printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1201            w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1202   }
1203
1204   // check for singular matrix
1205   if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1206     return splashErrSingularMatrix;
1207   }
1208
1209   // compute scale, shear, rotation, translation parameters
1210   rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1211   if (rot) {
1212     xScale = -mat[1];
1213     yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1214     xShear = -mat[3] / yScale;
1215     yShear = -mat[0] / mat[1];
1216   } else {
1217     xScale = mat[0];
1218     yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1219     xShear = mat[2] / yScale;
1220     yShear = mat[1] / mat[0];
1221   }
1222   tx = splashRound(mat[4]);
1223   ty = splashRound(mat[5]);
1224   scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
1225   scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
1226   xSign = (xScale < 0) ? -1 : 1;
1227   ySign = (yScale < 0) ? -1 : 1;
1228
1229   // clipping
1230   ulx1 = 0;
1231   uly1 = 0;
1232   urx1 = xSign * (scaledWidth - 1);
1233   ury1 = splashRound(yShear * urx1);
1234   llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1235   lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
1236   lrx1 = xSign * (scaledWidth - 1) +
1237            splashRound(xShear * ySign * (scaledHeight - 1));
1238   lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
1239   if (rot) {
1240     ulx = tx + uly1;    uly = ty - ulx1;
1241     urx = tx + ury1;    ury = ty - urx1;
1242     llx = tx + lly1;    lly = ty - llx1;
1243     lrx = tx + lry1;    lry = ty - lrx1;
1244   } else {
1245     ulx = tx + ulx1;    uly = ty + uly1;
1246     urx = tx + urx1;    ury = ty + ury1;
1247     llx = tx + llx1;    lly = ty + lly1;
1248     lrx = tx + lrx1;    lry = ty + lry1;
1249   }
1250   xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1251                                    : (llx < lrx) ? llx : lrx
1252                      : (urx < llx) ? (urx < lrx) ? urx : lrx
1253                                    : (llx < lrx) ? llx : lrx;
1254   xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1255                                    : (llx > lrx) ? llx : lrx
1256                      : (urx > llx) ? (urx > lrx) ? urx : lrx
1257                                    : (llx > lrx) ? llx : lrx;
1258   yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1259                                    : (lly < lry) ? lly : lry
1260                      : (ury < lly) ? (ury < lry) ? ury : lry
1261                                    : (lly < lry) ? lly : lry;
1262   yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1263                                    : (lly > lry) ? lly : lry
1264                      : (ury > lly) ? (ury > lry) ? ury : lry
1265                                    : (lly > lry) ? lly : lry;
1266   clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
1267
1268   // compute Bresenham parameters for x and y scaling
1269   yp = h / scaledHeight;
1270   yq = h % scaledHeight;
1271   xp = w / scaledWidth;
1272   xq = w % scaledWidth;
1273
1274   // allocate pixel buffer
1275   pixBuf = (SplashMono1 *)gmalloc((yp + 1) * w * sizeof(SplashMono1));
1276
1277   // init y scale Bresenham
1278   yt = 0;
1279   lastYStep = 1;
1280
1281   for (y = 0; y < scaledHeight; ++y) {
1282
1283     // y scale Bresenham
1284     yStep = yp;
1285     yt += yq;
1286     if (yt >= scaledHeight) {
1287       yt -= scaledHeight;
1288       ++yStep;
1289     }
1290
1291     // read row(s) from image
1292     n = (yp > 0) ? yStep : lastYStep;
1293     if (n > 0) {
1294       p = pixBuf;
1295       for (i = 0; i < n; ++i) {
1296         for (j = 0; j < w; ++j) {
1297           (*src)(srcData, p++);
1298         }
1299       }
1300     }
1301     lastYStep = yStep;
1302
1303     // loop-invariant constants
1304     k1 = splashRound(xShear * ySign * y);
1305
1306     // clipping test
1307     if (clipRes != splashClipAllInside &&
1308         !rot &&
1309         splashRound(yShear * k1) ==
1310           splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
1311       if (xSign > 0) {
1312         spanXMin = tx + k1;
1313         spanXMax = spanXMin + (scaledWidth - 1);
1314       } else {
1315         spanXMax = tx + k1;
1316         spanXMin = spanXMax - (scaledWidth - 1);
1317       }
1318       spanY = ty + ySign * y + splashRound(xShear * ySign * y);
1319       clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1320       if (clipRes2 == splashClipAllOutside) {
1321         continue;
1322       }
1323     } else {
1324       clipRes2 = clipRes;
1325     }
1326
1327     // init x scale Bresenham
1328     xt = 0;
1329     xSrc = 0;
1330
1331     for (x = 0; x < scaledWidth; ++x) {
1332
1333       // x scale Bresenham
1334       xStep = xp;
1335       xt += xq;
1336       if (xt >= scaledWidth) {
1337         xt -= scaledWidth;
1338         ++xStep;
1339       }
1340
1341       // x shear
1342       x1 = xSign * x + k1;
1343
1344       // y shear
1345       y1 = ySign * y + splashRound(yShear * x1);
1346
1347       // rotation
1348       if (rot) {
1349         x2 = y1;
1350         y2 = -x1;
1351       } else {
1352         x2 = x1;
1353         y2 = y1;
1354       }
1355
1356       // compute the alpha value for (x,y) after the x and y scaling
1357       // operations
1358       n = yStep > 0 ? yStep : 1;
1359       m = xStep > 0 ? xStep : 1;
1360       p = pixBuf + xSrc;
1361       pixAcc = 0;
1362       for (i = 0; i < n; ++i) {
1363         for (j = 0; j < m; ++j) {
1364           pixAcc += *p++;
1365         }
1366         p += w - m;
1367       }
1368
1369       // blend fill color with background
1370       if (pixAcc != 0) {
1371         fg = state->fillPattern->getColor(tx + x2, ty + y2);
1372         if (pixAcc == n * m) {
1373           pix = fg;
1374         } else {
1375           getPixel(tx + x2, ty + y2, &bg);
1376           alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m);
1377           switch (bitmap->mode) {
1378           case splashModeMono1:
1379             pix.mono1 = splashRound(alpha * fg.mono1 +
1380                                     (1 - alpha) * bg.mono1);
1381             break;
1382           case splashModeMono8:
1383             pix.mono8 = splashRound(alpha * fg.mono8 +
1384                                     (1 - alpha) * bg.mono8);
1385             break;
1386           case splashModeRGB8:
1387           case splashModeRGB8Packed:
1388             pix.rgb8 = splashMakeRGB8(
1389                            splashRound(alpha * splashRGB8R(fg.rgb8) +
1390                                        (1 - alpha) * splashRGB8R(bg.rgb8)),
1391                            splashRound(alpha * splashRGB8G(fg.rgb8) +
1392                                        (1 - alpha) * splashRGB8G(bg.rgb8)),
1393                            splashRound(alpha * splashRGB8B(fg.rgb8) +
1394                                        (1 - alpha) * splashRGB8B(bg.rgb8)));
1395             break;
1396           case splashModeBGR8Packed:
1397             pix.bgr8 = splashMakeBGR8(
1398                            splashRound(alpha * splashBGR8R(fg.bgr8) +
1399                                        (1 - alpha) * splashBGR8R(bg.bgr8)),
1400                            splashRound(alpha * splashBGR8G(fg.bgr8) +
1401                                        (1 - alpha) * splashBGR8G(bg.bgr8)),
1402                            splashRound(alpha * splashBGR8B(fg.bgr8) +
1403                                        (1 - alpha) * splashBGR8B(bg.bgr8)));
1404             break;
1405           }
1406         }
1407         drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
1408       }
1409
1410       // x scale Bresenham
1411       xSrc += xStep;
1412     }
1413   }
1414
1415   // free memory
1416   gfree(pixBuf);
1417
1418   return splashOk;
1419 }
1420
1421 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
1422                               SplashColorMode srcMode,
1423                               int w, int h, SplashCoord *mat) {
1424   GBool ok, rot, halftone;
1425   SplashCoord xScale, yScale, xShear, yShear;
1426   int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
1427   int ulx, uly, llx, lly, urx, ury, lrx, lry;
1428   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1429   int xMin, xMax, yMin, yMax;
1430   SplashClipResult clipRes, clipRes2;
1431   int yp, yq, yt, yStep, lastYStep;
1432   int xp, xq, xt, xStep, xSrc;
1433   int k1, spanXMin, spanXMax, spanY;
1434   SplashColor *pixBuf, *p;
1435   Guchar *alphaBuf, *q;
1436   SplashColor pix;
1437   SplashCoord pixAcc[splashMaxColorComps];
1438   int alphaAcc;
1439   SplashCoord pixMul, alphaMul, alpha;
1440   int x, y, x1, y1, x2, y2;
1441   int n, m, i, j;
1442
1443   if (debugMode) {
1444     printf("drawImage: srcMode=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1445            srcMode, w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1446   }
1447
1448   // check color modes
1449   ok = gFalse; // make gcc happy
1450   switch (bitmap->mode) {
1451   case splashModeMono1:
1452     ok = srcMode == splashModeMono1 || srcMode == splashModeMono8;
1453     break;
1454   case splashModeMono8:
1455     ok = srcMode == splashModeMono8;
1456     break;
1457   case splashModeRGB8:
1458     ok = srcMode == splashModeRGB8;
1459     break;
1460   case splashModeRGB8Packed:
1461     ok = srcMode == splashModeRGB8Packed;
1462     break;
1463   case splashModeBGR8Packed:
1464     ok = srcMode == splashModeBGR8Packed;
1465     break;
1466   }
1467   if (!ok) {
1468     return splashErrModeMismatch;
1469   }
1470   halftone = bitmap->mode == splashModeMono1 && srcMode == splashModeMono8;
1471
1472   // check for singular matrix
1473   if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1474     return splashErrSingularMatrix;
1475   }
1476
1477   // compute scale, shear, rotation, translation parameters
1478   rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1479   if (rot) {
1480     xScale = -mat[1];
1481     yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1482     xShear = -mat[3] / yScale;
1483     yShear = -mat[0] / mat[1];
1484   } else {
1485     xScale = mat[0];
1486     yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1487     xShear = mat[2] / yScale;
1488     yShear = mat[1] / mat[0];
1489   }
1490   tx = splashRound(mat[4]);
1491   ty = splashRound(mat[5]);
1492   scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
1493   scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
1494   xSign = (xScale < 0) ? -1 : 1;
1495   ySign = (yScale < 0) ? -1 : 1;
1496
1497   // clipping
1498   ulx1 = 0;
1499   uly1 = 0;
1500   urx1 = xSign * (scaledWidth - 1);
1501   ury1 = splashRound(yShear * urx1);
1502   llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1503   lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
1504   lrx1 = xSign * (scaledWidth - 1) +
1505            splashRound(xShear * ySign * (scaledHeight - 1));
1506   lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
1507   if (rot) {
1508     ulx = tx + uly1;    uly = ty - ulx1;
1509     urx = tx + ury1;    ury = ty - urx1;
1510     llx = tx + lly1;    lly = ty - llx1;
1511     lrx = tx + lry1;    lry = ty - lrx1;
1512   } else {
1513     ulx = tx + ulx1;    uly = ty + uly1;
1514     urx = tx + urx1;    ury = ty + ury1;
1515     llx = tx + llx1;    lly = ty + lly1;
1516     lrx = tx + lrx1;    lry = ty + lry1;
1517   }
1518   xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1519                                    : (llx < lrx) ? llx : lrx
1520                      : (urx < llx) ? (urx < lrx) ? urx : lrx
1521                                    : (llx < lrx) ? llx : lrx;
1522   xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1523                                    : (llx > lrx) ? llx : lrx
1524                      : (urx > llx) ? (urx > lrx) ? urx : lrx
1525                                    : (llx > lrx) ? llx : lrx;
1526   yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1527                                    : (lly < lry) ? lly : lry
1528                      : (ury < lly) ? (ury < lry) ? ury : lry
1529                                    : (lly < lry) ? lly : lry;
1530   yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1531                                    : (lly > lry) ? lly : lry
1532                      : (ury > lly) ? (ury > lry) ? ury : lry
1533                                    : (lly > lry) ? lly : lry;
1534   if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax))
1535       == splashClipAllOutside) {
1536     return splashOk;
1537   }
1538
1539   // compute Bresenham parameters for x and y scaling
1540   yp = h / scaledHeight;
1541   yq = h % scaledHeight;
1542   xp = w / scaledWidth;
1543   xq = w % scaledWidth;
1544
1545   // allocate pixel buffer
1546   pixBuf = (SplashColor *)gmalloc((yp + 1) * w * sizeof(SplashColor));
1547   alphaBuf = (Guchar *)gmalloc((yp + 1) * w * sizeof(Guchar));
1548
1549   // init y scale Bresenham
1550   yt = 0;
1551   lastYStep = 1;
1552
1553   for (y = 0; y < scaledHeight; ++y) {
1554
1555     // y scale Bresenham
1556     yStep = yp;
1557     yt += yq;
1558     if (yt >= scaledHeight) {
1559       yt -= scaledHeight;
1560       ++yStep;
1561     }
1562
1563     // read row(s) from image
1564     n = (yp > 0) ? yStep : lastYStep;
1565     if (n > 0) {
1566       p = pixBuf;
1567       q = alphaBuf;
1568       for (i = 0; i < n; ++i) {
1569         for (j = 0; j < w; ++j) {
1570           (*src)(srcData, p++, q++);
1571         }
1572       }
1573     }
1574     lastYStep = yStep;
1575
1576     // loop-invariant constants
1577     k1 = splashRound(xShear * ySign * y);
1578
1579     // clipping test
1580     if (clipRes != splashClipAllInside &&
1581         !rot &&
1582         splashRound(yShear * k1) ==
1583           splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
1584       if (xSign > 0) {
1585         spanXMin = tx + k1;
1586         spanXMax = spanXMin + (scaledWidth - 1);
1587       } else {
1588         spanXMax = tx + k1;
1589         spanXMin = spanXMax - (scaledWidth - 1);
1590       }
1591       spanY = ty + ySign * y + splashRound(xShear * ySign * y);
1592       clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1593       if (clipRes2 == splashClipAllOutside) {
1594         continue;
1595       }
1596     } else {
1597       clipRes2 = clipRes;
1598     }
1599
1600     // init x scale Bresenham
1601     xt = 0;
1602     xSrc = 0;
1603
1604     for (x = 0; x < scaledWidth; ++x) {
1605
1606       // x scale Bresenham
1607       xStep = xp;
1608       xt += xq;
1609       if (xt >= scaledWidth) {
1610         xt -= scaledWidth;
1611         ++xStep;
1612       }
1613
1614       // x shear
1615       x1 = xSign * x + k1;
1616
1617       // y shear
1618       y1 = ySign * y + splashRound(yShear * x1);
1619
1620       // rotation
1621       if (rot) {
1622         x2 = y1;
1623         y2 = -x1;
1624       } else {
1625         x2 = x1;
1626         y2 = y1;
1627       }
1628
1629       // compute the filtered pixel at (x,y) after the x and y scaling
1630       // operations
1631       n = yStep > 0 ? yStep : 1;
1632       m = xStep > 0 ? xStep : 1;
1633       p = pixBuf + xSrc;
1634       q = alphaBuf + xSrc;
1635       for (i = 0; i < splashMaxColorComps; ++i) {
1636         pixAcc[i] = 0;
1637       }
1638       alphaAcc = 0;
1639       for (i = 0; i < n; ++i) {
1640         for (j = 0; j < m; ++j) {
1641           switch (srcMode) {
1642           case splashModeMono1:
1643             pixAcc[0] += p->mono1;
1644             break;
1645           case splashModeMono8:
1646             pixAcc[0] += p->mono8;
1647             break;
1648           case splashModeRGB8:
1649           case splashModeRGB8Packed:
1650             pixAcc[0] += splashRGB8R(p->rgb8);
1651             pixAcc[1] += splashRGB8G(p->rgb8);
1652             pixAcc[2] += splashRGB8B(p->rgb8);
1653             break;
1654           case splashModeBGR8Packed:
1655             pixAcc[0] += splashBGR8R(p->bgr8);
1656             pixAcc[1] += splashBGR8G(p->bgr8);
1657             pixAcc[2] += splashBGR8B(p->bgr8);
1658             break;
1659           }
1660           ++p;
1661           alphaAcc += *q++;
1662         }
1663         p += w - m;
1664         q += w - m;
1665       }
1666       alphaMul = 1 / (SplashCoord)(n * m);
1667       if (halftone) {
1668         pixMul = (SplashCoord)alphaMul / 256.0;
1669       } else {
1670         pixMul = alphaMul;
1671       }
1672       alpha = (SplashCoord)alphaAcc * alphaMul;
1673
1674       //~ this should blend if 0 < alpha < 1
1675       if (alpha > 0.75) {
1676
1677         // mono8 -> mono1 conversion, with halftoning
1678         if (halftone) {
1679           pix.mono1 = state->screen->test(tx + x2, ty + y2,
1680                                           pixAcc[0] * pixMul);
1681
1682         // no conversion, no halftoning
1683         } else {
1684           switch (bitmap->mode) {
1685           case splashModeMono1:
1686             pix.mono1 = splashRound(pixAcc[0] * pixMul);
1687             break;
1688           case splashModeMono8:
1689             pix.mono8 = splashRound(pixAcc[0] * pixMul);
1690             break;
1691           case splashModeRGB8:
1692           case splashModeRGB8Packed:
1693             pix.rgb8 = splashMakeRGB8(splashRound(pixAcc[0] * pixMul),
1694                                       splashRound(pixAcc[1] * pixMul),
1695                                       splashRound(pixAcc[2] * pixMul));
1696             break;
1697           case splashModeBGR8Packed:
1698             pix.bgr8 = splashMakeBGR8(splashRound(pixAcc[0] * pixMul),
1699                                       splashRound(pixAcc[1] * pixMul),
1700                                       splashRound(pixAcc[2] * pixMul));
1701             break;
1702           }
1703         }
1704
1705         // set pixel
1706         drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
1707       }
1708
1709       // x scale Bresenham
1710       xSrc += xStep;
1711     }
1712   }
1713
1714   gfree(pixBuf);
1715   gfree(alphaBuf);
1716
1717   return splashOk;
1718 }
1719
1720 void Splash::dumpPath(SplashPath *path) {
1721   int i;
1722
1723   for (i = 0; i < path->length; ++i) {
1724     printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s%s\n",
1725            i, path->pts[i].x, path->pts[i].y,
1726            (path->flags[i] & splashPathFirst) ? " first" : "",
1727            (path->flags[i] & splashPathLast) ? " last" : "",
1728            (path->flags[i] & splashPathClosed) ? " closed" : "",
1729            (path->flags[i] & splashPathCurve) ? " curve" : "",
1730            (path->flags[i] & splashPathArcCW) ? " arcCW" : "");
1731   }
1732 }