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