source: trunk/poppler/mypoppler/splash/Splash.cc @ 461

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

File size: 92.0 KB
Line 
1//========================================================================
2//
3// Splash.cc
4//
5//========================================================================
6
7//========================================================================
8//
9// Modified under the Poppler project - http://poppler.freedesktop.org
10//
11// All changes made under the Poppler project to this file are licensed
12// under GPL version 2 or later
13//
14// Copyright (C) 2005-2010 Albert Astals Cid <aacid@kde.org>
15// Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
16//
17// To see a description of the changes please see the Changelog file that
18// came with your tarball or type make ChangeLog if you are building from git
19//
20//========================================================================
21
22#include <config.h>
23
24#ifdef USE_GCC_PRAGMAS
25#pragma implementation
26#endif
27
28#include <stdlib.h>
29#include <string.h>
30#include <limits.h>
31#include "goo/gmem.h"
32#include "SplashErrorCodes.h"
33#include "SplashMath.h"
34#include "SplashBitmap.h"
35#include "SplashState.h"
36#include "SplashPath.h"
37#include "SplashXPath.h"
38#include "SplashXPathScanner.h"
39#include "SplashPattern.h"
40#include "SplashScreen.h"
41#include "SplashFont.h"
42#include "SplashGlyphBitmap.h"
43#include "Splash.h"
44
45// to get the unlikely definition
46#include "poppler/Object.h"
47
48//------------------------------------------------------------------------
49
50// distance of Bezier control point from center for circle approximation
51// = (4 * (sqrt(2) - 1) / 3) * r
52#define bezierCircle ((SplashCoord)0.55228475)
53#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
54
55// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
56static inline Guchar div255(int x) {
57  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
58}
59
60//------------------------------------------------------------------------
61// SplashPipe
62//------------------------------------------------------------------------
63
64#define splashPipeMaxStages 9
65
66struct SplashPipe {
67  // pixel coordinates
68  int x, y;
69
70  // source pattern
71  SplashPattern *pattern;
72
73  // source alpha and color
74  SplashCoord aInput;
75  GBool usesShape;
76  Guchar aSrc;
77  SplashColorPtr cSrc;
78  SplashColor cSrcVal;
79
80  // non-isolated group alpha0
81  Guchar *alpha0Ptr;
82
83  // soft mask
84  SplashColorPtr softMaskPtr;
85
86  // destination alpha and color
87  SplashColorPtr destColorPtr;
88  int destColorMask;
89  Guchar *destAlphaPtr;
90
91  // shape
92  SplashCoord shape;
93
94  // result alpha and color
95  GBool noTransparency;
96  SplashPipeResultColorCtrl resultColorCtrl;
97
98  // non-isolated group correction
99  int nonIsolatedGroup;
100};
101
102SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
103  splashPipeResultColorNoAlphaBlendMono,
104  splashPipeResultColorNoAlphaBlendMono,
105  splashPipeResultColorNoAlphaBlendRGB,
106  splashPipeResultColorNoAlphaBlendRGB,
107  splashPipeResultColorNoAlphaBlendRGB
108#if SPLASH_CMYK
109  ,
110  splashPipeResultColorNoAlphaBlendCMYK
111#endif
112};
113
114SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
115  splashPipeResultColorAlphaNoBlendMono,
116  splashPipeResultColorAlphaNoBlendMono,
117  splashPipeResultColorAlphaNoBlendRGB,
118  splashPipeResultColorNoAlphaBlendRGB,
119  splashPipeResultColorAlphaNoBlendRGB
120#if SPLASH_CMYK
121  ,
122  splashPipeResultColorAlphaNoBlendCMYK
123#endif
124};
125
126SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
127  splashPipeResultColorAlphaBlendMono,
128  splashPipeResultColorAlphaBlendMono,
129  splashPipeResultColorAlphaBlendRGB,
130  splashPipeResultColorNoAlphaBlendRGB,
131  splashPipeResultColorAlphaBlendRGB
132#if SPLASH_CMYK
133  ,
134  splashPipeResultColorAlphaBlendCMYK
135#endif
136};
137
138//------------------------------------------------------------------------
139
140static void blendXor(SplashColorPtr src, SplashColorPtr dest,
141                     SplashColorPtr blend, SplashColorMode cm) {
142  int i;
143
144  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
145    blend[i] = src[i] ^ dest[i];
146  }
147}
148
149//------------------------------------------------------------------------
150// modified region
151//------------------------------------------------------------------------
152
153void Splash::clearModRegion() {
154  modXMin = bitmap->getWidth();
155  modYMin = bitmap->getHeight();
156  modXMax = -1;
157  modYMax = -1;
158}
159
160inline void Splash::updateModX(int x) {
161  if (x < modXMin) {
162    modXMin = x;
163  }
164  if (x > modXMax) {
165    modXMax = x;
166  }
167}
168
169inline void Splash::updateModY(int y) {
170  if (y < modYMin) {
171    modYMin = y;
172  }
173  if (y > modYMax) {
174    modYMax = y;
175  }
176}
177
178//------------------------------------------------------------------------
179// pipeline
180//------------------------------------------------------------------------
181
182inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
183                             SplashPattern *pattern, SplashColorPtr cSrc,
184                             SplashCoord aInput, GBool usesShape,
185                             GBool nonIsolatedGroup) {
186  pipeSetXY(pipe, x, y);
187  pipe->pattern = NULL;
188
189  // source color
190  if (pattern) {
191    if (pattern->isStatic()) {
192      pattern->getColor(x, y, pipe->cSrcVal);
193    } else {
194      pipe->pattern = pattern;
195    }
196    pipe->cSrc = pipe->cSrcVal;
197  } else {
198    pipe->cSrc = cSrc;
199  }
200
201  // source alpha
202  pipe->aInput = aInput;
203  if (!state->softMask) {
204    if (usesShape) {
205      pipe->aInput *= 255;
206    } else {
207      pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
208    }
209  }
210  pipe->usesShape = usesShape;
211
212  // result alpha
213  if (aInput == 1 && !state->softMask && !usesShape &&
214      !state->inNonIsolatedGroup) {
215    pipe->noTransparency = gTrue;
216  } else {
217    pipe->noTransparency = gFalse;
218  }
219
220  // result color
221  if (pipe->noTransparency) {
222    // the !state->blendFunc case is handled separately in pipeRun
223    pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
224  } else if (!state->blendFunc) {
225    pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
226  } else {
227    pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
228  }
229
230  // non-isolated group correction
231  if (nonIsolatedGroup) {
232    pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
233  } else {
234    pipe->nonIsolatedGroup = 0;
235  }
236}
237
238inline void Splash::pipeRun(SplashPipe *pipe) {
239  Guchar aSrc, aDest, alpha2, alpha0, aResult;
240  SplashColor cDest, cBlend;
241  Guchar cResult0, cResult1, cResult2, cResult3;
242
243  //----- source color
244
245  // static pattern: handled in pipeInit
246  // fixed color: handled in pipeInit
247
248  // dynamic pattern
249  if (pipe->pattern) {
250    pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
251  }
252
253  if (pipe->noTransparency && !state->blendFunc) {
254
255    //----- write destination pixel
256
257    switch (bitmap->mode) {
258    case splashModeMono1:
259      cResult0 = pipe->cSrc[0];
260      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
261        *pipe->destColorPtr |= pipe->destColorMask;
262      } else {
263        *pipe->destColorPtr &= ~pipe->destColorMask;
264      }
265      if (!(pipe->destColorMask >>= 1)) {
266        pipe->destColorMask = 0x80;
267        ++pipe->destColorPtr;
268      }
269      break;
270    case splashModeMono8:
271      *pipe->destColorPtr++ = pipe->cSrc[0];
272      break;
273    case splashModeRGB8:
274      *pipe->destColorPtr++ = pipe->cSrc[0];
275      *pipe->destColorPtr++ = pipe->cSrc[1];
276      *pipe->destColorPtr++ = pipe->cSrc[2];
277      break;
278    case splashModeXBGR8:
279      *pipe->destColorPtr++ = pipe->cSrc[2];
280      *pipe->destColorPtr++ = pipe->cSrc[1];
281      *pipe->destColorPtr++ = pipe->cSrc[0];
282      *pipe->destColorPtr++ = 255;
283      break;
284    case splashModeBGR8:
285      *pipe->destColorPtr++ = pipe->cSrc[2];
286      *pipe->destColorPtr++ = pipe->cSrc[1];
287      *pipe->destColorPtr++ = pipe->cSrc[0];
288      break;
289#if SPLASH_CMYK
290    case splashModeCMYK8:
291      *pipe->destColorPtr++ = pipe->cSrc[0];
292      *pipe->destColorPtr++ = pipe->cSrc[1];
293      *pipe->destColorPtr++ = pipe->cSrc[2];
294      *pipe->destColorPtr++ = pipe->cSrc[3];
295      break;
296#endif
297    }
298    if (pipe->destAlphaPtr) {
299      *pipe->destAlphaPtr++ = 255;
300    }
301
302  } else {
303
304    //----- read destination pixel
305
306    switch (bitmap->mode) {
307    case splashModeMono1:
308      cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
309      break;
310    case splashModeMono8:
311      cDest[0] = *pipe->destColorPtr;
312      break;
313    case splashModeRGB8:
314      cDest[0] = pipe->destColorPtr[0];
315      cDest[1] = pipe->destColorPtr[1];
316      cDest[2] = pipe->destColorPtr[2];
317      break;
318    case splashModeXBGR8:
319      cDest[0] = pipe->destColorPtr[2];
320      cDest[1] = pipe->destColorPtr[1];
321      cDest[2] = pipe->destColorPtr[0];
322      cDest[3] = 255;
323      break;
324    case splashModeBGR8:
325      cDest[0] = pipe->destColorPtr[2];
326      cDest[1] = pipe->destColorPtr[1];
327      cDest[2] = pipe->destColorPtr[0];
328      break;
329#if SPLASH_CMYK
330    case splashModeCMYK8:
331      cDest[0] = pipe->destColorPtr[0];
332      cDest[1] = pipe->destColorPtr[1];
333      cDest[2] = pipe->destColorPtr[2];
334      cDest[3] = pipe->destColorPtr[3];
335      break;
336#endif
337    }
338    if (pipe->destAlphaPtr) {
339      aDest = *pipe->destAlphaPtr;
340    } else {
341      aDest = 0xff;
342    }
343
344    //----- blend function
345
346    if (state->blendFunc) {
347      (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
348    }
349
350    //----- source alpha
351
352    if (state->softMask) {
353      if (pipe->usesShape) {
354        aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
355                                   * pipe->shape);
356      } else {
357        aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
358      }
359    } else if (pipe->usesShape) {
360      // pipe->aInput is premultiplied by 255 in pipeInit
361      aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
362    } else {
363      // precomputed in pipeInit
364      aSrc = pipe->aSrc;
365    }
366
367    //----- result alpha and non-isolated group element correction
368
369    if (pipe->noTransparency) {
370      alpha2 = aResult = 255;
371    } else {
372      aResult = aSrc + aDest - div255(aSrc * aDest);
373
374      if (pipe->alpha0Ptr) {
375        alpha0 = *pipe->alpha0Ptr++;
376        alpha2 = aResult + alpha0 - div255(aResult * alpha0);
377      } else {
378        alpha2 = aResult;
379      }
380    }
381
382    //----- result color
383
384    cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
385
386    switch (pipe->resultColorCtrl) {
387
388#if SPLASH_CMYK
389    case splashPipeResultColorNoAlphaBlendCMYK:
390      cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
391#endif
392    case splashPipeResultColorNoAlphaBlendRGB:
393      cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
394      cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
395    case splashPipeResultColorNoAlphaBlendMono:
396      cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
397      break;
398
399    case splashPipeResultColorAlphaNoBlendMono:
400      if (alpha2 == 0) {
401        cResult0 = 0;
402      } else {
403        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
404                             aSrc * pipe->cSrc[0]) / alpha2);
405      }
406      break;
407    case splashPipeResultColorAlphaNoBlendRGB:
408      if (alpha2 == 0) {
409        cResult0 = 0;
410        cResult1 = 0;
411        cResult2 = 0;
412      } else {
413        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
414                             aSrc * pipe->cSrc[0]) / alpha2);
415        cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
416                             aSrc * pipe->cSrc[1]) / alpha2);
417        cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
418                             aSrc * pipe->cSrc[2]) / alpha2);
419      }
420      break;
421#if SPLASH_CMYK
422    case splashPipeResultColorAlphaNoBlendCMYK:
423      if (alpha2 == 0) {
424        cResult0 = 0;
425        cResult1 = 0;
426        cResult2 = 0;
427        cResult3 = 0;
428      } else {
429        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
430                             aSrc * pipe->cSrc[0]) / alpha2);
431        cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
432                             aSrc * pipe->cSrc[1]) / alpha2);
433        cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
434                             aSrc * pipe->cSrc[2]) / alpha2);
435        cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
436                             aSrc * pipe->cSrc[3]) / alpha2);
437      }
438      break;
439#endif
440
441    case splashPipeResultColorAlphaBlendMono:
442      if (alpha2 == 0) {
443        cResult0 = 0;
444      } else {
445        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
446                             aSrc * ((255 - aDest) * pipe->cSrc[0] +
447                                     aDest * cBlend[0]) / 255) /
448                            alpha2);
449      }
450      break;
451    case splashPipeResultColorAlphaBlendRGB:
452      if (alpha2 == 0) {
453        cResult0 = 0;
454        cResult1 = 0;
455        cResult2 = 0;
456      } else {
457        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
458                             aSrc * ((255 - aDest) * pipe->cSrc[0] +
459                                     aDest * cBlend[0]) / 255) /
460                            alpha2);
461        cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
462                             aSrc * ((255 - aDest) * pipe->cSrc[1] +
463                                     aDest * cBlend[1]) / 255) /
464                            alpha2);
465        cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
466                             aSrc * ((255 - aDest) * pipe->cSrc[2] +
467                                     aDest * cBlend[2]) / 255) /
468                            alpha2);
469      }
470      break;
471#if SPLASH_CMYK
472    case splashPipeResultColorAlphaBlendCMYK:
473      if (alpha2 == 0) {
474        cResult0 = 0;
475        cResult1 = 0;
476        cResult2 = 0;
477        cResult3 = 0;
478      } else {
479        cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
480                             aSrc * ((255 - aDest) * pipe->cSrc[0] +
481                                     aDest * cBlend[0]) / 255) /
482                            alpha2);
483        cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
484                             aSrc * ((255 - aDest) * pipe->cSrc[1] +
485                                     aDest * cBlend[1]) / 255) /
486                            alpha2);
487        cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
488                             aSrc * ((255 - aDest) * pipe->cSrc[2] +
489                                     aDest * cBlend[2]) / 255) /
490                            alpha2);
491        cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
492                             aSrc * ((255 - aDest) * pipe->cSrc[3] +
493                                     aDest * cBlend[3]) / 255) /
494                            alpha2);
495      }
496      break;
497#endif
498    }
499
500    //----- non-isolated group correction
501
502    if (aResult != 0) {
503      switch (pipe->nonIsolatedGroup) {
504#if SPLASH_CMYK
505      case 4:
506        cResult3 += (cResult3 - cDest[3]) * aDest *
507                    (255 - aResult) / (255 * aResult);
508#endif
509      case 3:
510        cResult2 += (cResult2 - cDest[2]) * aDest *
511                    (255 - aResult) / (255 * aResult);
512        cResult1 += (cResult1 - cDest[1]) * aDest *
513                    (255 - aResult) / (255 * aResult);
514      case 1:
515        cResult0 += (cResult0 - cDest[0]) * aDest *
516                    (255 - aResult) / (255 * aResult);
517      case 0:
518        break;
519      }
520    }
521
522    //----- write destination pixel
523
524    switch (bitmap->mode) {
525    case splashModeMono1:
526      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
527        *pipe->destColorPtr |= pipe->destColorMask;
528      } else {
529        *pipe->destColorPtr &= ~pipe->destColorMask;
530      }
531      if (!(pipe->destColorMask >>= 1)) {
532        pipe->destColorMask = 0x80;
533        ++pipe->destColorPtr;
534      }
535      break;
536    case splashModeMono8:
537      *pipe->destColorPtr++ = cResult0;
538      break;
539    case splashModeRGB8:
540      *pipe->destColorPtr++ = cResult0;
541      *pipe->destColorPtr++ = cResult1;
542      *pipe->destColorPtr++ = cResult2;
543      break;
544    case splashModeXBGR8:
545      *pipe->destColorPtr++ = cResult2;
546      *pipe->destColorPtr++ = cResult1;
547      *pipe->destColorPtr++ = cResult0;
548      *pipe->destColorPtr++ = 255;
549      break;
550    case splashModeBGR8:
551      *pipe->destColorPtr++ = cResult2;
552      *pipe->destColorPtr++ = cResult1;
553      *pipe->destColorPtr++ = cResult0;
554      break;
555#if SPLASH_CMYK
556    case splashModeCMYK8:
557      *pipe->destColorPtr++ = cResult0;
558      *pipe->destColorPtr++ = cResult1;
559      *pipe->destColorPtr++ = cResult2;
560      *pipe->destColorPtr++ = cResult3;
561      break;
562#endif
563    }
564    if (pipe->destAlphaPtr) {
565      *pipe->destAlphaPtr++ = aResult;
566    }
567
568  }
569
570  ++pipe->x;
571}
572
573inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
574  pipe->x = x;
575  pipe->y = y;
576  if (state->softMask) {
577    pipe->softMaskPtr =
578        &state->softMask->data[y * state->softMask->rowSize + x];
579  }
580  switch (bitmap->mode) {
581  case splashModeMono1:
582    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
583    pipe->destColorMask = 0x80 >> (x & 7);
584    break;
585  case splashModeMono8:
586    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
587    break;
588  case splashModeRGB8:
589  case splashModeBGR8:
590    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
591    break;
592  case splashModeXBGR8:
593    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
594    break;
595#if SPLASH_CMYK
596  case splashModeCMYK8:
597    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
598    break;
599#endif
600  }
601  if (bitmap->alpha) {
602    pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
603  } else {
604    pipe->destAlphaPtr = NULL;
605  }
606  if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
607    pipe->alpha0Ptr =
608        &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
609                             (alpha0X + x)];
610  } else {
611    pipe->alpha0Ptr = NULL;
612  }
613}
614
615inline void Splash::pipeIncX(SplashPipe *pipe) {
616  ++pipe->x;
617  if (state->softMask) {
618    ++pipe->softMaskPtr;
619  }
620  switch (bitmap->mode) {
621  case splashModeMono1:
622    if (!(pipe->destColorMask >>= 1)) {
623      pipe->destColorMask = 0x80;
624      ++pipe->destColorPtr;
625    }
626    break;
627  case splashModeMono8:
628    ++pipe->destColorPtr;
629    break;
630  case splashModeRGB8:
631  case splashModeBGR8:
632    pipe->destColorPtr += 3;
633    break;
634  case splashModeXBGR8:
635    pipe->destColorPtr += 4;
636    break;
637#if SPLASH_CMYK
638  case splashModeCMYK8:
639    pipe->destColorPtr += 4;
640    break;
641#endif
642  }
643  if (pipe->destAlphaPtr) {
644    ++pipe->destAlphaPtr;
645  }
646  if (pipe->alpha0Ptr) {
647    ++pipe->alpha0Ptr;
648  }
649}
650
651inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
652  if (unlikely(y < 0))
653    return;
654
655  if (noClip || state->clip->test(x, y)) {
656    pipeSetXY(pipe, x, y);
657    pipeRun(pipe);
658    updateModX(x);
659    updateModY(y);
660  }
661}
662
663inline void Splash::drawAAPixelInit() {
664  aaBufY = -1;
665}
666
667inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
668#if splashAASize == 4
669  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
670                               1, 2, 2, 3, 2, 3, 3, 4 };
671  int w;
672#else
673  int xx, yy;
674#endif
675  SplashColorPtr p;
676  int x0, x1, t;
677
678  if (x < 0 || x >= bitmap->width ||
679      y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
680    return;
681  }
682
683  // update aaBuf
684  if (y != aaBufY) {
685    memset(aaBuf->getDataPtr(), 0xff,
686           aaBuf->getRowSize() * aaBuf->getHeight());
687    x0 = 0;
688    x1 = bitmap->width - 1;
689    state->clip->clipAALine(aaBuf, &x0, &x1, y);
690    aaBufY = y;
691  }
692
693  // compute the shape value
694#if splashAASize == 4
695  p = aaBuf->getDataPtr() + (x >> 1);
696  w = aaBuf->getRowSize();
697  if (x & 1) {
698    t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
699        bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
700  } else {
701    t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
702        bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
703  }
704#else
705  t = 0;
706  for (yy = 0; yy < splashAASize; ++yy) {
707    for (xx = 0; xx < splashAASize; ++xx) {
708      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
709          ((x * splashAASize + xx) >> 3);
710      t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
711    }
712  }
713#endif
714
715  // draw the pixel
716  if (t != 0) {
717    pipeSetXY(pipe, x, y);
718    pipe->shape *= aaGamma[t];
719    pipeRun(pipe);
720    updateModX(x);
721    updateModY(y);
722  }
723}
724
725inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
726                             GBool noClip) {
727  int x;
728
729  pipeSetXY(pipe, x0, y);
730  if (noClip) {
731    for (x = x0; x <= x1; ++x) {
732      pipeRun(pipe);
733    }
734    updateModX(x0);
735    updateModX(x1);
736    updateModY(y);
737  } else {
738    for (x = x0; x <= x1; ++x) {
739      if (state->clip->test(x, y)) {
740        pipeRun(pipe);
741        updateModX(x);
742        updateModY(y);
743      } else {
744        pipeIncX(pipe);
745      }
746    }
747  }
748}
749
750inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
751#if splashAASize == 4
752  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
753                               1, 2, 2, 3, 2, 3, 3, 4 };
754  SplashColorPtr p0, p1, p2, p3;
755  int t;
756#else
757  SplashColorPtr p;
758  int xx, yy, t;
759#endif
760  int x;
761
762#if splashAASize == 4
763  p0 = aaBuf->getDataPtr() + (x0 >> 1);
764  p1 = p0 + aaBuf->getRowSize();
765  p2 = p1 + aaBuf->getRowSize();
766  p3 = p2 + aaBuf->getRowSize();
767#endif
768  pipeSetXY(pipe, x0, y);
769  for (x = x0; x <= x1; ++x) {
770
771    // compute the shape value
772#if splashAASize == 4
773    if (x & 1) {
774      t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
775          bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
776      ++p0; ++p1; ++p2; ++p3;
777    } else {
778      t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
779          bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
780    }
781#else
782    t = 0;
783    for (yy = 0; yy < splashAASize; ++yy) {
784      for (xx = 0; xx < splashAASize; ++xx) {
785        p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
786            ((x * splashAASize + xx) >> 3);
787        t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
788      }
789    }
790#endif
791
792    if (t != 0) {
793      pipe->shape = aaGamma[t];
794      pipeRun(pipe);
795      updateModX(x);
796      updateModY(y);
797    } else {
798      pipeIncX(pipe);
799    }
800  }
801}
802
803//------------------------------------------------------------------------
804
805// Transform a point from user space to device space.
806inline void Splash::transform(SplashCoord *matrix,
807                              SplashCoord xi, SplashCoord yi,
808                              SplashCoord *xo, SplashCoord *yo) {
809  //                          [ m[0] m[1] 0 ]
810  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
811  //                          [ m[4] m[5] 1 ]
812  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
813  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
814}
815
816//------------------------------------------------------------------------
817// Splash
818//------------------------------------------------------------------------
819
820Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
821               SplashScreenParams *screenParams) {
822  int i;
823
824  bitmap = bitmapA;
825  vectorAntialias = vectorAntialiasA;
826  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
827                          screenParams);
828  if (vectorAntialias) {
829    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
830                             1, splashModeMono1, gFalse);
831    for (i = 0; i <= splashAASize * splashAASize; ++i) {
832      aaGamma[i] = splashPow((SplashCoord)i /
833                               (SplashCoord)(splashAASize * splashAASize),
834                             1.5);
835    }
836  } else {
837    aaBuf = NULL;
838  }
839  clearModRegion();
840  debugMode = gFalse;
841}
842
843Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
844               SplashScreen *screenA) {
845  int i;
846
847  bitmap = bitmapA;
848  vectorAntialias = vectorAntialiasA;
849  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
850                          screenA);
851  if (vectorAntialias) {
852    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
853                             1, splashModeMono1, gFalse);
854    for (i = 0; i <= splashAASize * splashAASize; ++i) {
855      aaGamma[i] = splashPow((SplashCoord)i /
856                               (SplashCoord)(splashAASize * splashAASize),
857                             1.5);
858    }
859  } else {
860    aaBuf = NULL;
861  }
862  clearModRegion();
863  debugMode = gFalse;
864}
865
866Splash::~Splash() {
867  while (state->next) {
868    restoreState();
869  }
870  delete state;
871  if (vectorAntialias) {
872    delete aaBuf;
873  }
874}
875
876//------------------------------------------------------------------------
877// state read
878//------------------------------------------------------------------------
879
880SplashCoord *Splash::getMatrix() {
881  return state->matrix;
882}
883
884SplashPattern *Splash::getStrokePattern() {
885  return state->strokePattern;
886}
887
888SplashPattern *Splash::getFillPattern() {
889  return state->fillPattern;
890}
891
892SplashScreen *Splash::getScreen() {
893  return state->screen;
894}
895
896SplashBlendFunc Splash::getBlendFunc() {
897  return state->blendFunc;
898}
899
900SplashCoord Splash::getStrokeAlpha() {
901  return state->strokeAlpha;
902}
903
904SplashCoord Splash::getFillAlpha() {
905  return state->fillAlpha;
906}
907
908SplashCoord Splash::getLineWidth() {
909  return state->lineWidth;
910}
911
912int Splash::getLineCap() {
913  return state->lineCap;
914}
915
916int Splash::getLineJoin() {
917  return state->lineJoin;
918}
919
920SplashCoord Splash::getMiterLimit() {
921  return state->miterLimit;
922}
923
924SplashCoord Splash::getFlatness() {
925  return state->flatness;
926}
927
928SplashCoord *Splash::getLineDash() {
929  return state->lineDash;
930}
931
932int Splash::getLineDashLength() {
933  return state->lineDashLength;
934}
935
936SplashCoord Splash::getLineDashPhase() {
937  return state->lineDashPhase;
938}
939
940SplashClip *Splash::getClip() {
941  return state->clip;
942}
943
944SplashBitmap *Splash::getSoftMask() {
945  return state->softMask;
946}
947
948GBool Splash::getInNonIsolatedGroup() {
949  return state->inNonIsolatedGroup;
950}
951
952//------------------------------------------------------------------------
953// state write
954//------------------------------------------------------------------------
955
956void Splash::setMatrix(SplashCoord *matrix) {
957  memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
958}
959
960void Splash::setStrokePattern(SplashPattern *strokePattern) {
961  state->setStrokePattern(strokePattern);
962}
963
964void Splash::setFillPattern(SplashPattern *fillPattern) {
965  state->setFillPattern(fillPattern);
966}
967
968void Splash::setScreen(SplashScreen *screen) {
969  state->setScreen(screen);
970}
971
972void Splash::setBlendFunc(SplashBlendFunc func) {
973  state->blendFunc = func;
974}
975
976void Splash::setStrokeAlpha(SplashCoord alpha) {
977  state->strokeAlpha = alpha;
978}
979
980void Splash::setFillAlpha(SplashCoord alpha) {
981  state->fillAlpha = alpha;
982}
983
984void Splash::setLineWidth(SplashCoord lineWidth) {
985  state->lineWidth = lineWidth;
986}
987
988void Splash::setLineCap(int lineCap) {
989  state->lineCap = lineCap;
990}
991
992void Splash::setLineJoin(int lineJoin) {
993  state->lineJoin = lineJoin;
994}
995
996void Splash::setMiterLimit(SplashCoord miterLimit) {
997  state->miterLimit = miterLimit;
998}
999
1000void Splash::setFlatness(SplashCoord flatness) {
1001  if (flatness < 1) {
1002    state->flatness = 1;
1003  } else {
1004    state->flatness = flatness;
1005  }
1006}
1007
1008void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
1009                         SplashCoord lineDashPhase) {
1010  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
1011}
1012
1013void Splash::setStrokeAdjust(GBool strokeAdjust) {
1014  state->strokeAdjust = strokeAdjust;
1015}
1016
1017void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
1018                             SplashCoord x1, SplashCoord y1) {
1019  state->clip->resetToRect(x0, y0, x1, y1);
1020}
1021
1022SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
1023                               SplashCoord x1, SplashCoord y1) {
1024  return state->clip->clipToRect(x0, y0, x1, y1);
1025}
1026
1027SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
1028  return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
1029}
1030
1031void Splash::setSoftMask(SplashBitmap *softMask) {
1032  state->setSoftMask(softMask);
1033}
1034
1035void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
1036                                   int alpha0XA, int alpha0YA) {
1037  alpha0Bitmap = alpha0BitmapA;
1038  alpha0X = alpha0XA;
1039  alpha0Y = alpha0YA;
1040  state->inNonIsolatedGroup = gTrue;
1041}
1042
1043//------------------------------------------------------------------------
1044// state save/restore
1045//------------------------------------------------------------------------
1046
1047void Splash::saveState() {
1048  SplashState *newState;
1049
1050  newState = state->copy();
1051  newState->next = state;
1052  state = newState;
1053}
1054
1055SplashError Splash::restoreState() {
1056  SplashState *oldState;
1057
1058  if (!state->next) {
1059    return splashErrNoSave;
1060  }
1061  oldState = state;
1062  state = state->next;
1063  delete oldState;
1064  return splashOk;
1065}
1066
1067//------------------------------------------------------------------------
1068// drawing operations
1069//------------------------------------------------------------------------
1070
1071void Splash::clear(SplashColorPtr color, Guchar alpha) {
1072  SplashColorPtr row, p;
1073  Guchar mono;
1074  int x, y;
1075
1076  switch (bitmap->mode) {
1077  case splashModeMono1:
1078    mono = (color[0] & 0x80) ? 0xff : 0x00;
1079    if (bitmap->rowSize < 0) {
1080      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1081             mono, -bitmap->rowSize * bitmap->height);
1082    } else {
1083      memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1084    }
1085    break;
1086  case splashModeMono8:
1087    if (bitmap->rowSize < 0) {
1088      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1089             color[0], -bitmap->rowSize * bitmap->height);
1090    } else {
1091      memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1092    }
1093    break;
1094  case splashModeRGB8:
1095    if (color[0] == color[1] && color[1] == color[2]) {
1096      if (bitmap->rowSize < 0) {
1097        memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1098               color[0], -bitmap->rowSize * bitmap->height);
1099      } else {
1100        memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1101      }
1102    } else {
1103      row = bitmap->data;
1104      for (y = 0; y < bitmap->height; ++y) {
1105        p = row;
1106        for (x = 0; x < bitmap->width; ++x) {
1107          *p++ = color[2];
1108          *p++ = color[1];
1109          *p++ = color[0];
1110        }
1111        row += bitmap->rowSize;
1112      }
1113    }
1114    break;
1115  case splashModeXBGR8:
1116    if (color[0] == color[1] && color[1] == color[2]) {
1117      if (bitmap->rowSize < 0) {
1118        memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1119               color[0], -bitmap->rowSize * bitmap->height);
1120      } else {
1121        memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1122      }
1123    } else {
1124      row = bitmap->data;
1125      for (y = 0; y < bitmap->height; ++y) {
1126        p = row;
1127        for (x = 0; x < bitmap->width; ++x) {
1128          *p++ = color[0];
1129          *p++ = color[1];
1130          *p++ = color[2];
1131          *p++ = 255;
1132        }
1133        row += bitmap->rowSize;
1134      }
1135    }
1136    break;
1137  case splashModeBGR8:
1138    if (color[0] == color[1] && color[1] == color[2]) {
1139      if (bitmap->rowSize < 0) {
1140        memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1141               color[0], -bitmap->rowSize * bitmap->height);
1142      } else {
1143        memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1144      }
1145    } else {
1146      row = bitmap->data;
1147      for (y = 0; y < bitmap->height; ++y) {
1148        p = row;
1149        for (x = 0; x < bitmap->width; ++x) {
1150          *p++ = color[0];
1151          *p++ = color[1];
1152          *p++ = color[2];
1153        }
1154        row += bitmap->rowSize;
1155      }
1156    }
1157    break;
1158#if SPLASH_CMYK
1159  case splashModeCMYK8:
1160    if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1161      if (bitmap->rowSize < 0) {
1162        memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1163               color[0], -bitmap->rowSize * bitmap->height);
1164      } else {
1165        memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1166      }
1167    } else {
1168      row = bitmap->data;
1169      for (y = 0; y < bitmap->height; ++y) {
1170        p = row;
1171        for (x = 0; x < bitmap->width; ++x) {
1172          *p++ = color[0];
1173          *p++ = color[1];
1174          *p++ = color[2];
1175          *p++ = color[3];
1176        }
1177        row += bitmap->rowSize;
1178      }
1179    }
1180    break;
1181#endif
1182  }
1183
1184  if (bitmap->alpha) {
1185    memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
1186  }
1187
1188  updateModX(0);
1189  updateModY(0);
1190  updateModX(bitmap->width - 1);
1191  updateModY(bitmap->height - 1);
1192}
1193
1194SplashError Splash::stroke(SplashPath *path) {
1195  SplashPath *path2, *dPath;
1196
1197  if (debugMode) {
1198    printf("stroke [dash:%d] [width:%.2f]:\n",
1199           state->lineDashLength, (double)state->lineWidth);
1200    dumpPath(path);
1201  }
1202  opClipRes = splashClipAllOutside;
1203  if (path->length == 0) {
1204    return splashErrEmptyPath;
1205  }
1206  path2 = flattenPath(path, state->matrix, state->flatness);
1207  if (state->lineDashLength > 0) {
1208    dPath = makeDashedPath(path2);
1209    delete path2;
1210    path2 = dPath;
1211  }
1212  if (state->lineWidth == 0) {
1213    strokeNarrow(path2);
1214  } else {
1215    strokeWide(path2);
1216  }
1217  delete path2;
1218  return splashOk;
1219}
1220
1221void Splash::strokeNarrow(SplashPath *path) {
1222  SplashPipe pipe;
1223  SplashXPath *xPath;
1224  SplashXPathSeg *seg;
1225  int x0, x1, x2, x3, y0, y1, x, y, t;
1226  SplashCoord dx, dy, dxdy;
1227  SplashClipResult clipRes;
1228  int nClipRes[3];
1229  int i;
1230
1231  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
1232
1233  xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
1234
1235  pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
1236           gFalse, gFalse);
1237
1238  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
1239
1240    x0 = splashFloor(seg->x0);
1241    x1 = splashFloor(seg->x1);
1242    y0 = splashFloor(seg->y0);
1243    y1 = splashFloor(seg->y1);
1244
1245    // horizontal segment
1246    if (y0 == y1) {
1247      if (x0 > x1) {
1248        t = x0; x0 = x1; x1 = t;
1249      }
1250      if ((clipRes = state->clip->testSpan(x0, x1, y0))
1251          != splashClipAllOutside) {
1252        drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
1253      }
1254
1255    // segment with |dx| > |dy|
1256    } else if (splashAbs(seg->dxdy) > 1) {
1257      dx = seg->x1 - seg->x0;
1258      dy = seg->y1 - seg->y0;
1259      dxdy = seg->dxdy;
1260      if (y0 > y1) {
1261        t = y0; y0 = y1; y1 = t;
1262        t = x0; x0 = x1; x1 = t;
1263        dx = -dx;
1264        dy = -dy;
1265      }
1266      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1267                                           x0 <= x1 ? x1 : x0, y1))
1268          != splashClipAllOutside) {
1269        if (dx > 0) {
1270          x2 = x0;
1271          x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1272          drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
1273                   clipRes == splashClipAllInside);
1274          x2 = x3;
1275          for (y = y0 + 1; y <= y1 - 1; ++y) {
1276            x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1277            drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
1278            x2 = x3;
1279          }
1280          drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
1281                   clipRes == splashClipAllInside);
1282        } else {
1283          x2 = x0;
1284          x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1285          drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
1286                   clipRes == splashClipAllInside);
1287          x2 = x3;
1288          for (y = y0 + 1; y <= y1 - 1; ++y) {
1289            x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1290            drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
1291            x2 = x3;
1292          }
1293          drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
1294                   clipRes == splashClipAllInside);
1295        }
1296      }
1297
1298    // segment with |dy| > |dx|
1299    } else {
1300      dxdy = seg->dxdy;
1301      if (y0 > y1) {
1302        t = x0; x0 = x1; x1 = t;
1303        t = y0; y0 = y1; y1 = t;
1304      }
1305      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1306                                           x0 <= x1 ? x1 : x0, y1))
1307          != splashClipAllOutside) {
1308        drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
1309        for (y = y0 + 1; y <= y1 - 1; ++y) {
1310          x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
1311          drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
1312        }
1313        drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
1314    }
1315    }
1316    ++nClipRes[clipRes];
1317  }
1318  if (nClipRes[splashClipPartial] ||
1319      (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
1320    opClipRes = splashClipPartial;
1321  } else if (nClipRes[splashClipAllInside]) {
1322    opClipRes = splashClipAllInside;
1323  } else {
1324    opClipRes = splashClipAllOutside;
1325  }
1326
1327  delete xPath;
1328}
1329
1330void Splash::strokeWide(SplashPath *path) {
1331  SplashPath *path2;
1332
1333  path2 = makeStrokePath(path, gFalse);
1334  fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
1335  delete path2;
1336}
1337
1338SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
1339                                SplashCoord flatness) {
1340  SplashPath *fPath;
1341  SplashCoord flatness2;
1342  Guchar flag;
1343  int i;
1344
1345  fPath = new SplashPath();
1346  flatness2 = flatness * flatness;
1347  i = 0;
1348  while (i < path->length) {
1349    flag = path->flags[i];
1350    if (flag & splashPathFirst) {
1351      fPath->moveTo(path->pts[i].x, path->pts[i].y);
1352      ++i;
1353    } else {
1354      if (flag & splashPathCurve) {
1355        flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
1356                     path->pts[].x, path->pts[].y,
1357                     path->pts[i+1].x, path->pts[i+1].y,
1358                     path->pts[i+2].x, path->pts[i+2].y,
1359                     matrix, flatness2, fPath);
1360        i += 3;
1361      } else {
1362        fPath->lineTo(path->pts[i].x, path->pts[i].y);
1363        ++i;
1364      }
1365      if (path->flags[i-1] & splashPathClosed) {
1366        fPath->close();
1367      }
1368    }
1369  }
1370  return fPath;
1371}
1372
1373void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
1374                          SplashCoord x1, SplashCoord y1,
1375                          SplashCoord x2, SplashCoord y2,
1376                          SplashCoord x3, SplashCoord y3,
1377                          SplashCoord *matrix, SplashCoord flatness2,
1378                          SplashPath *fPath) {
1379  SplashCoord cx[splashMaxCurveSplits + 1][3];
1380  SplashCoord cy[splashMaxCurveSplits + 1][3];
1381  int cNext[splashMaxCurveSplits + 1];
1382  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
1383  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
1384  SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
1385  int p1, p2, p3;
1386
1387  // initial segment
1388  p1 = 0;
1389  p2 = splashMaxCurveSplits;
1390  cx[p1][0] = x0;  cy[p1][0] = y0;
1391  cx[p1][1] = x1;  cy[p1][1] = y1;
1392  cx[p1][2] = x2;  cy[p1][2] = y2;
1393  cx[p2][0] = x3;  cy[p2][0] = y3;
1394  cNext[p1] = p2;
1395
1396  while (p1 < splashMaxCurveSplits) {
1397
1398    // get the next segment
1399    xl0 = cx[p1][0];  yl0 = cy[p1][0];
1400    xx1 = cx[p1][1];  yy1 = cy[p1][1];
1401    xx2 = cx[p1][2];  yy2 = cy[p1][2];
1402    p2 = cNext[p1];
1403    xr3 = cx[p2][0];  yr3 = cy[p2][0];
1404
1405    // compute the distances (in device space) from the control points
1406    // to the midpoint of the straight line (this is a bit of a hack,
1407    // but it's much faster than computing the actual distances to the
1408    // line)
1409    transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
1410    transform(matrix, xx1, yy1, &tx, &ty);
1411    dx = tx - mx;
1412    dy = ty - my;
1413    d1 = dx*dx + dy*dy;
1414    transform(matrix, xx2, yy2, &tx, &ty);
1415    dx = tx - mx;
1416    dy = ty - my;
1417    d2 = dx*dx + dy*dy;
1418
1419    // if the curve is flat enough, or no more subdivisions are
1420    // allowed, add the straight line segment
1421    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
1422      fPath->lineTo(xr3, yr3);
1423      p1 = p2;
1424
1425    // otherwise, subdivide the curve
1426    } else {
1427      xl1 = (xl0 + xx1) * 0.5;
1428      yl1 = (yl0 + yy1) * 0.5;
1429      xh = (xx1 + xx2) * 0.5;
1430      yh = (yy1 + yy2) * 0.5;
1431      xl2 = (xl1 + xh) * 0.5;
1432      yl2 = (yl1 + yh) * 0.5;
1433      xr2 = (xx2 + xr3) * 0.5;
1434      yr2 = (yy2 + yr3) * 0.5;
1435      xr1 = (xh + xr2) * 0.5;
1436      yr1 = (yh + yr2) * 0.5;
1437      xr0 = (xl2 + xr1) * 0.5;
1438      yr0 = (yl2 + yr1) * 0.5;
1439      // add the new subdivision points
1440      p3 = (p1 + p2) / 2;
1441      cx[p1][1] = xl1;  cy[p1][1] = yl1;
1442      cx[p1][2] = xl2;  cy[p1][2] = yl2;
1443      cNext[p1] = p3;
1444      cx[p3][0] = xr0;  cy[p3][0] = yr0;
1445      cx[p3][1] = xr1;  cy[p3][1] = yr1;
1446      cx[p3][2] = xr2;  cy[p3][2] = yr2;
1447      cNext[p3] = p2;
1448    }
1449  }
1450}
1451
1452SplashPath *Splash::makeDashedPath(SplashPath *path) {
1453  SplashPath *dPath;
1454  SplashCoord lineDashTotal;
1455  SplashCoord lineDashStartPhase, lineDashDist, segLen;
1456  SplashCoord x0, y0, x1, y1, xa, ya;
1457  GBool lineDashStartOn, lineDashOn, newPath;
1458  int lineDashStartIdx, lineDashIdx;
1459  int i, j, k;
1460
1461  lineDashTotal = 0;
1462  for (i = 0; i < state->lineDashLength; ++i) {
1463    lineDashTotal += state->lineDash[i];
1464  }
1465  lineDashStartPhase = state->lineDashPhase;
1466  i = splashFloor(lineDashStartPhase / lineDashTotal);
1467  lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
1468  lineDashStartOn = gTrue;
1469  lineDashStartIdx = 0;
1470  while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
1471    lineDashStartOn = !lineDashStartOn;
1472    lineDashStartPhase -= state->lineDash[lineDashStartIdx];
1473    ++lineDashStartIdx;
1474  }
1475
1476  dPath = new SplashPath();
1477
1478  // process each subpath
1479  i = 0;
1480  while (i < path->length) {
1481
1482    // find the end of the subpath
1483    for (j = i;
1484         j < path->length - 1 && !(path->flags[j] & splashPathLast);
1485         ++j) ;
1486
1487    // initialize the dash parameters
1488    lineDashOn = lineDashStartOn;
1489    lineDashIdx = lineDashStartIdx;
1490    lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
1491
1492    // process each segment of the subpath
1493    newPath = gTrue;
1494    for (k = i; k < j; ++k) {
1495
1496      // grab the segment
1497      x0 = path->pts[k].x;
1498      y0 = path->pts[k].y;
1499      x1 = path->pts[k+1].x;
1500      y1 = path->pts[k+1].y;
1501      segLen = splashDist(x0, y0, x1, y1);
1502
1503      // process the segment
1504      while (segLen > 0) {
1505
1506        if (lineDashDist >= segLen) {
1507          if (lineDashOn) {
1508            if (newPath) {
1509              dPath->moveTo(x0, y0);
1510              newPath = gFalse;
1511            }
1512            dPath->lineTo(x1, y1);
1513          }
1514          lineDashDist -= segLen;
1515          segLen = 0;
1516
1517        } else {
1518          xa = x0 + (lineDashDist / segLen) * (x1 - x0);
1519          ya = y0 + (lineDashDist / segLen) * (y1 - y0);
1520          if (lineDashOn) {
1521            if (newPath) {
1522              dPath->moveTo(x0, y0);
1523              newPath = gFalse;
1524            }
1525            dPath->lineTo(xa, ya);
1526          }
1527          x0 = xa;
1528          y0 = ya;
1529          segLen -= lineDashDist;
1530          lineDashDist = 0;
1531        }
1532
1533        // get the next entry in the dash array
1534        if (lineDashDist <= 0) {
1535          lineDashOn = !lineDashOn;
1536          if (++lineDashIdx == state->lineDashLength) {
1537            lineDashIdx = 0;
1538          }
1539          lineDashDist = state->lineDash[lineDashIdx];
1540          newPath = gTrue;
1541        }
1542      }
1543    }
1544    i = j + 1;
1545  }
1546
1547  return dPath;
1548}
1549
1550SplashError Splash::fill(SplashPath *path, GBool eo) {
1551  if (debugMode) {
1552    printf("fill [eo:%d]:\n", eo);
1553    dumpPath(path);
1554  }
1555  return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
1556}
1557
1558SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
1559                                    SplashPattern *pattern,
1560                                    SplashCoord alpha) {
1561  SplashPipe pipe;
1562  SplashXPath *xPath;
1563  SplashXPathScanner *scanner;
1564  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1565  SplashClipResult clipRes, clipRes2;
1566
1567  if (path->length == 0) {
1568    return splashErrEmptyPath;
1569  }
1570  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1571  if (vectorAntialias) {
1572    xPath->aaScale();
1573  }
1574  xPath->sort();
1575  scanner = new SplashXPathScanner(xPath, eo);
1576
1577  // get the min and max x and y values
1578  if (vectorAntialias) {
1579    scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
1580  } else {
1581    scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1582  }
1583
1584  // check clipping
1585  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1586      != splashClipAllOutside) {
1587
1588    // limit the y range
1589    if (yMinI < state->clip->getYMinI()) {
1590      yMinI = state->clip->getYMinI();
1591    }
1592    if (yMaxI > state->clip->getYMaxI()) {
1593      yMaxI = state->clip->getYMaxI();
1594    }
1595
1596    pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);
1597
1598    // draw the spans
1599    if (vectorAntialias) {
1600      for (y = yMinI; y <= yMaxI; ++y) {
1601        scanner->renderAALine(aaBuf, &x0, &x1, y);
1602        if (clipRes != splashClipAllInside) {
1603          state->clip->clipAALine(aaBuf, &x0, &x1, y);
1604        }
1605        drawAALine(&pipe, x0, x1, y);
1606      }
1607    } else {
1608      for (y = yMinI; y <= yMaxI; ++y) {
1609        while (scanner->getNextSpan(y, &x0, &x1)) {
1610          if (clipRes == splashClipAllInside) {
1611            drawSpan(&pipe, x0, x1, y, gTrue);
1612          } else {
1613            // limit the x range
1614            if (x0 < state->clip->getXMinI()) {
1615              x0 = state->clip->getXMinI();
1616            }
1617            if (x1 > state->clip->getXMaxI()) {
1618              x1 = state->clip->getXMaxI();
1619            }
1620            clipRes2 = state->clip->testSpan(x0, x1, y);
1621            drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1622          }
1623        }
1624      }
1625    }
1626  }
1627  opClipRes = clipRes;
1628
1629  delete scanner;
1630  delete xPath;
1631  return splashOk;
1632}
1633
1634SplashError Splash::xorFill(SplashPath *path, GBool eo) {
1635  SplashPipe pipe;
1636  SplashXPath *xPath;
1637  SplashXPathScanner *scanner;
1638  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1639  SplashClipResult clipRes, clipRes2;
1640  SplashBlendFunc origBlendFunc;
1641
1642  if (path->length == 0) {
1643    return splashErrEmptyPath;
1644  }
1645  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1646  xPath->sort();
1647  scanner = new SplashXPathScanner(xPath, eo);
1648
1649  // get the min and max x and y values
1650  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1651
1652  // check clipping
1653  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1654      != splashClipAllOutside) {
1655
1656    // limit the y range
1657    if (yMinI < state->clip->getYMinI()) {
1658      yMinI = state->clip->getYMinI();
1659    }
1660    if (yMaxI > state->clip->getYMaxI()) {
1661      yMaxI = state->clip->getYMaxI();
1662    }
1663
1664    origBlendFunc = state->blendFunc;
1665    state->blendFunc = &blendXor;
1666    pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);
1667
1668    // draw the spans
1669    for (y = yMinI; y <= yMaxI; ++y) {
1670      while (scanner->getNextSpan(y, &x0, &x1)) {
1671        if (clipRes == splashClipAllInside) {
1672          drawSpan(&pipe, x0, x1, y, gTrue);
1673        } else {
1674          // limit the x range
1675          if (x0 < state->clip->getXMinI()) {
1676            x0 = state->clip->getXMinI();
1677          }
1678          if (x1 > state->clip->getXMaxI()) {
1679            x1 = state->clip->getXMaxI();
1680          }
1681          clipRes2 = state->clip->testSpan(x0, x1, y);
1682          drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1683        }
1684      }
1685    }
1686    state->blendFunc = origBlendFunc;
1687  }
1688  opClipRes = clipRes;
1689
1690  delete scanner;
1691  delete xPath;
1692  return splashOk;
1693}
1694
1695SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
1696                             int c, SplashFont *font) {
1697  SplashGlyphBitmap glyph;
1698  SplashCoord xt, yt;
1699  int x0, y0, xFrac, yFrac;
1700  SplashClipResult clipRes;
1701
1702  if (debugMode) {
1703    printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
1704           (double)x, (double)y, c, c, c);
1705  }
1706  transform(state->matrix, x, y, &xt, &yt);
1707  x0 = splashFloor(xt);
1708  xFrac = splashFloor((xt - x0) * splashFontFraction);
1709  y0 = splashFloor(yt);
1710  yFrac = splashFloor((yt - y0) * splashFontFraction);
1711  if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
1712    return splashErrNoGlyph;
1713  }
1714  if (clipRes != splashClipAllOutside) {
1715    fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
1716  }
1717  opClipRes = clipRes;
1718  if (glyph.freeData) {
1719    gfree(glyph.data);
1720  }
1721  return splashOk;
1722}
1723
1724void Splash::fillGlyph(SplashCoord x, SplashCoord y,
1725                              SplashGlyphBitmap *glyph) {
1726  SplashCoord xt, yt;
1727  int x0, y0;
1728
1729  transform(state->matrix, x, y, &xt, &yt);
1730  x0 = splashFloor(xt);
1731  y0 = splashFloor(yt);
1732  SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
1733                             y0 - glyph->y,
1734                             x0 - glyph->x + glyph->w - 1,
1735                             y0 - glyph->y + glyph->h - 1);
1736  if (clipRes != splashClipAllOutside) {
1737    fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
1738  }
1739  opClipRes = clipRes;
1740}
1741
1742void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
1743  SplashPipe pipe;
1744  int alpha0, alpha;
1745  Guchar *p;
1746  int x1, y1, xx, xx1, yy;
1747
1748  p = glyph->data;
1749  int xStart = x0 - glyph->x;
1750  int yStart = y0 - glyph->y;
1751  int xxLimit = glyph->w;
1752  int yyLimit = glyph->h;
1753
1754  if (yStart < 0)
1755  {
1756    p += glyph->w * -yStart; // move p to the beginning of the first painted row
1757    yyLimit += yStart;
1758    yStart = 0;
1759  }
1760
1761  if (xStart < 0)
1762  {
1763    p += -xStart; // move p to the first painted pixel
1764    xxLimit += xStart;
1765    xStart = 0;
1766  }
1767
1768  if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
1769  if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
1770
1771  if (noClip) {
1772    if (glyph->aa) {
1773      pipeInit(&pipe, xStart, yStart,
1774               state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1775      for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
1776        pipeSetXY(&pipe, xStart, y1);
1777        for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
1778          alpha = p[xx];
1779          if (alpha != 0) {
1780            pipe.shape = (SplashCoord)(alpha / 255.0);
1781            pipeRun(&pipe);
1782            updateModX(x1);
1783            updateModY(y1);
1784          } else {
1785            pipeIncX(&pipe);
1786          }
1787        }
1788        p += glyph->w;
1789      }
1790    } else {
1791      const int widthEight = splashCeil(glyph->w / 8.0);
1792
1793      pipeInit(&pipe, xStart, yStart,
1794               state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1795      for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
1796        pipeSetXY(&pipe, xStart, y1);
1797        for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
1798          alpha0 = p[xx / 8];
1799          for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
1800            if (alpha0 & 0x80) {
1801              pipeRun(&pipe);
1802              updateModX(x1);
1803              updateModY(y1);
1804            } else {
1805              pipeIncX(&pipe);
1806            }
1807            alpha0 <<= 1;
1808          }
1809        }
1810        p += widthEight;
1811      }
1812    }
1813  } else {
1814    if (glyph->aa) {
1815      pipeInit(&pipe, xStart, yStart,
1816               state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1817      for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
1818        pipeSetXY(&pipe, xStart, y1);
1819        for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
1820          if (state->clip->test(x1, y1)) {
1821            alpha = p[xx];
1822            if (alpha != 0) {
1823              pipe.shape = (SplashCoord)(alpha / 255.0);
1824              pipeRun(&pipe);
1825              updateModX(x1);
1826              updateModY(y1);
1827            } else {
1828              pipeIncX(&pipe);
1829            }
1830          } else {
1831            pipeIncX(&pipe);
1832          }
1833        }
1834        p += glyph->w;
1835      }
1836    } else {
1837      const int widthEight = splashCeil(glyph->w / 8.0);
1838
1839      pipeInit(&pipe, xStart, yStart,
1840               state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1841      for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
1842        pipeSetXY(&pipe, xStart, y1);
1843        for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
1844          alpha0 = p[xx / 8];
1845          for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
1846            if (state->clip->test(x1, y1)) {
1847              if (alpha0 & 0x80) {
1848                pipeRun(&pipe);
1849                updateModX(x1);
1850                updateModY(y1);
1851              } else {
1852                pipeIncX(&pipe);
1853              }
1854            } else {
1855              pipeIncX(&pipe);
1856            }
1857            alpha0 <<= 1;
1858          }
1859        }
1860        p += widthEight;
1861      }
1862    }
1863  }
1864}
1865
1866SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
1867                                  int w, int h, SplashCoord *mat,
1868                                  GBool glyphMode) {
1869  SplashPipe pipe;
1870  GBool rot;
1871  SplashCoord xScale, yScale, xShear, yShear, yShear1;
1872  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
1873  int ulx, uly, llx, lly, urx, ury, lrx, lry;
1874  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1875  int xMin, xMax, yMin, yMax;
1876  SplashClipResult clipRes, clipRes2;
1877  int yp, yq, yt, yStep, lastYStep;
1878  int xp, xq, xt, xStep, xSrc;
1879  int k1, spanXMin, spanXMax, spanY;
1880  SplashColorPtr pixBuf, p;
1881  int pixAcc;
1882  int x, y, x1, x2, y2;
1883  SplashCoord y1;
1884  int n, m, i, j;
1885
1886  if (debugMode) {
1887    printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1888           w, h, (double)mat[0], (double)mat[1], (double)mat[2],
1889           (double)mat[3], (double)mat[4], (double)mat[5]);
1890  }
1891
1892  if (w == 0 && h == 0) return splashErrZeroImage;
1893
1894  // check for singular matrix
1895  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1896    return splashErrSingularMatrix;
1897  }
1898
1899  // compute scale, shear, rotation, translation parameters
1900  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1901  if (rot) {
1902    xScale = -mat[1];
1903    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1904    xShear = -mat[3] / yScale;
1905    yShear = -mat[0] / mat[1];
1906  } else {
1907    xScale = mat[0];
1908    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1909    xShear = mat[2] / yScale;
1910    yShear = mat[1] / mat[0];
1911  }
1912  // Note 1: The PDF spec says that all pixels whose *centers* lie
1913  // within the region get painted -- but that doesn't seem to match
1914  // up with what Acrobat actually does: it ends up leaving gaps
1915  // between image stripes.  So we use the same rule here as for
1916  // fills: any pixel that overlaps the region gets painted.
1917  // Note 2: The "glyphMode" flag is a kludge: it switches back to
1918  // "correct" behavior (matching the spec), for use in rendering Type
1919  // 3 fonts.
1920  // Note 3: The +/-0.01 in these computations is to avoid floating
1921  // point precision problems which can lead to gaps between image
1922  // stripes (it can cause image stripes to overlap, but that's a much
1923  // less visible problem).
1924  if (glyphMode) {
1925    if (xScale >= 0) {
1926      tx = splashRound(mat[4]);
1927      tx2 = splashRound(mat[4] + xScale) - 1;
1928    } else {
1929      tx = splashRound(mat[4]) - 1;
1930      tx2 = splashRound(mat[4] + xScale);
1931    }
1932  } else {
1933    if (xScale >= 0) {
1934      tx = splashFloor(mat[4] - 0.01);
1935      tx2 = splashFloor(mat[4] + xScale + 0.01);
1936    } else {
1937      tx = splashFloor(mat[4] + 0.01);
1938      tx2 = splashFloor(mat[4] + xScale - 0.01);
1939    }
1940  }
1941  scaledWidth = abs(tx2 - tx) + 1;
1942  if (glyphMode) {
1943    if (yScale >= 0) {
1944      ty = splashRound(mat[5]);
1945      ty2 = splashRound(mat[5] + yScale) - 1;
1946    } else {
1947      ty = splashRound(mat[5]) - 1;
1948      ty2 = splashRound(mat[5] + yScale);
1949    }
1950  } else {
1951    if (yScale >= 0) {
1952      ty = splashFloor(mat[5] - 0.01);
1953      ty2 = splashFloor(mat[5] + yScale + 0.01);
1954    } else {
1955      ty = splashFloor(mat[5] + 0.01);
1956      ty2 = splashFloor(mat[5] + yScale - 0.01);
1957    }
1958  }
1959  scaledHeight = abs(ty2 - ty) + 1;
1960  xSign = (xScale < 0) ? -1 : 1;
1961  ySign = (yScale < 0) ? -1 : 1;
1962  yShear1 = (SplashCoord)xSign * yShear;
1963
1964  // clipping
1965  ulx1 = 0;
1966  uly1 = 0;
1967  urx1 = xSign * (scaledWidth - 1);
1968  ury1 = (int)(yShear * urx1);
1969  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1970  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
1971  lrx1 = xSign * (scaledWidth - 1) +
1972           splashRound(xShear * ySign * (scaledHeight - 1));
1973  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
1974  if (rot) {
1975    ulx = tx + uly1;    uly = ty - ulx1;
1976    urx = tx + ury1;    ury = ty - urx1;
1977    llx = tx + lly1;    lly = ty - llx1;
1978    lrx = tx + lry1;    lry = ty - lrx1;
1979  } else {
1980    ulx = tx + ulx1;    uly = ty + uly1;
1981    urx = tx + urx1;    ury = ty + ury1;
1982    llx = tx + llx1;    lly = ty + lly1;
1983    lrx = tx + lrx1;    lry = ty + lry1;
1984  }
1985  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1986                                   : (llx < lrx) ? llx : lrx
1987                     : (urx < llx) ? (urx < lrx) ? urx : lrx
1988                                   : (llx < lrx) ? llx : lrx;
1989  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1990                                   : (llx > lrx) ? llx : lrx
1991                     : (urx > llx) ? (urx > lrx) ? urx : lrx
1992                                   : (llx > lrx) ? llx : lrx;
1993  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1994                                   : (lly < lry) ? lly : lry
1995                     : (ury < lly) ? (ury < lry) ? ury : lry
1996                                   : (lly < lry) ? lly : lry;
1997  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1998                                   : (lly > lry) ? lly : lry
1999                     : (ury > lly) ? (ury > lry) ? ury : lry
2000                                   : (lly > lry) ? lly : lry;
2001  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
2002  opClipRes = clipRes;
2003
2004  // compute Bresenham parameters for x and y scaling
2005  yp = h / scaledHeight;
2006  yq = h % scaledHeight;
2007  xp = w / scaledWidth;
2008  xq = w % scaledWidth;
2009
2010  // allocate pixel buffer
2011  if (yp < 0 || yp > INT_MAX - 1) {
2012    return splashErrBadArg;
2013  }
2014  pixBuf = (SplashColorPtr)gmallocn((yp + 1), w);
2015
2016  // initialize the pixel pipe
2017  pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
2018           gTrue, gFalse);
2019  if (vectorAntialias) {
2020    drawAAPixelInit();
2021  }
2022
2023  // init y scale Bresenham
2024  yt = 0;
2025  lastYStep = 1;
2026
2027  for (y = 0; y < scaledHeight; ++y) {
2028
2029    // y scale Bresenham
2030    yStep = yp;
2031    yt += yq;
2032    if (yt >= scaledHeight) {
2033      yt -= scaledHeight;
2034      ++yStep;
2035    }
2036
2037    // read row(s) from image
2038    n = (yp > 0) ? yStep : lastYStep;
2039    if (n > 0) {
2040      p = pixBuf;
2041      for (i = 0; i < n; ++i) {
2042        (*src)(srcData, p);
2043        p += w;
2044      }
2045    }
2046    lastYStep = yStep;
2047
2048    // loop-invariant constants
2049    k1 = splashRound(xShear * ySign * y);
2050
2051    // clipping test
2052    if (clipRes != splashClipAllInside &&
2053        !rot &&
2054        (int)(yShear * k1) ==
2055          (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2056      if (xSign > 0) {
2057        spanXMin = tx + k1;
2058        spanXMax = spanXMin + (scaledWidth - 1);
2059      } else {
2060        spanXMax = tx + k1;
2061        spanXMin = spanXMax - (scaledWidth - 1);
2062      }
2063      spanY = ty + ySign * y + (int)(yShear * k1);
2064      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2065      if (clipRes2 == splashClipAllOutside) {
2066        continue;
2067      }
2068    } else {
2069      clipRes2 = clipRes;
2070    }
2071
2072    // init x scale Bresenham
2073    xt = 0;
2074    xSrc = 0;
2075
2076    // x shear
2077    x1 = k1;
2078
2079    // y shear
2080    y1 = (SplashCoord)ySign * y + yShear * x1;
2081    // this is a kludge: if yShear1 is negative, then (int)y1 would
2082    // change immediately after the first pixel, which is not what we
2083    // want
2084    if (yShear1 < 0) {
2085      y1 += 0.999;
2086    }
2087
2088    // loop-invariant constants
2089    n = yStep > 0 ? yStep : 1;
2090
2091    for (x = 0; x < scaledWidth; ++x) {
2092
2093      // x scale Bresenham
2094      xStep = xp;
2095      xt += xq;
2096      if (xt >= scaledWidth) {
2097        xt -= scaledWidth;
2098        ++xStep;
2099      }
2100
2101      // rotation
2102      if (rot) {
2103        x2 = (int)y1;
2104        y2 = -x1;
2105      } else {
2106        x2 = x1;
2107        y2 = (int)y1;
2108      }
2109
2110      // compute the alpha value for (x,y) after the x and y scaling
2111      // operations
2112      m = xStep > 0 ? xStep : 1;
2113      p = pixBuf + xSrc;
2114      pixAcc = 0;
2115      for (i = 0; i < n; ++i) {
2116        for (j = 0; j < m; ++j) {
2117          pixAcc += *p++;
2118        }
2119        p += w - m;
2120      }
2121
2122      // blend fill color with background
2123      if (pixAcc != 0) {
2124        pipe.shape = (pixAcc == n * m)
2125                         ? (SplashCoord)1
2126                         : (SplashCoord)pixAcc / (SplashCoord)(n * m);
2127        if (vectorAntialias && clipRes2 != splashClipAllInside) {
2128          drawAAPixel(&pipe, tx + x2, ty + y2);
2129        } else {
2130          drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
2131        }
2132      }
2133
2134      // x scale Bresenham
2135      xSrc += xStep;
2136
2137      // x shear
2138      x1 += xSign;
2139
2140      // y shear
2141      y1 += yShear1;
2142    }
2143  }
2144
2145  // free memory
2146  gfree(pixBuf);
2147
2148  return splashOk;
2149}
2150
2151SplashError Splash::drawImage(SplashImageSource src, void *srcData,
2152                              SplashColorMode srcMode, GBool srcAlpha,
2153                              int w, int h, SplashCoord *mat) {
2154  SplashPipe pipe;
2155  GBool ok, rot;
2156  SplashCoord xScale, yScale, xShear, yShear, yShear1;
2157  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
2158  int ulx, uly, llx, lly, urx, ury, lrx, lry;
2159  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
2160  int xMin, xMax, yMin, yMax;
2161  SplashClipResult clipRes, clipRes2;
2162  int yp, yq, yt, yStep, lastYStep;
2163  int xp, xq, xt, xStep, xSrc;
2164  int k1, spanXMin, spanXMax, spanY;
2165  SplashColorPtr colorBuf, p;
2166  SplashColor pix;
2167  Guchar *alphaBuf, *q;
2168#if SPLASH_CMYK
2169  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
2170#else
2171  int pixAcc0, pixAcc1, pixAcc2;
2172#endif
2173  int alphaAcc;
2174  SplashCoord pixMul, alphaMul, alpha;
2175  int x, y, x1, x2, y2;
2176  SplashCoord y1;
2177  int nComps, n, m, i, j;
2178
2179  if (debugMode) {
2180    printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2181           srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2182           (double)mat[3], (double)mat[4], (double)mat[5]);
2183  }
2184
2185  // check color modes
2186  ok = gFalse; // make gcc happy
2187  nComps = 0; // make gcc happy
2188  switch (bitmap->mode) {
2189  case splashModeMono1:
2190  case splashModeMono8:
2191    ok = srcMode == splashModeMono8;
2192    nComps = 1;
2193    break;
2194  case splashModeRGB8:
2195    ok = srcMode == splashModeRGB8;
2196    nComps = 3;
2197    break;
2198  case splashModeXBGR8:
2199    ok = srcMode == splashModeXBGR8;
2200    nComps = 4;
2201    break;
2202  case splashModeBGR8:
2203    ok = srcMode == splashModeBGR8;
2204    nComps = 3;
2205    break;
2206#if SPLASH_CMYK
2207  case splashModeCMYK8:
2208    ok = srcMode == splashModeCMYK8;
2209    nComps = 4;
2210    break;
2211#endif
2212  }
2213  if (!ok) {
2214    return splashErrModeMismatch;
2215  }
2216
2217  // check for singular matrix
2218  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
2219    return splashErrSingularMatrix;
2220  }
2221
2222  // compute scale, shear, rotation, translation parameters
2223  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
2224  if (rot) {
2225    xScale = -mat[1];
2226    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
2227    xShear = -mat[3] / yScale;
2228    yShear = -mat[0] / mat[1];
2229  } else {
2230    xScale = mat[0];
2231    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
2232    xShear = mat[2] / yScale;
2233    yShear = mat[1] / mat[0];
2234  }
2235  // Note 1: The PDF spec says that all pixels whose *centers* lie
2236  // within the region get painted -- but that doesn't seem to match
2237  // up with what Acrobat actually does: it ends up leaving gaps
2238  // between image stripes.  So we use the same rule here as for
2239  // fills: any pixel that overlaps the region gets painted.
2240  // Note 2: The +/-0.01 in these computations is to avoid floating
2241  // point precision problems which can lead to gaps between image
2242  // stripes (it can cause image stripes to overlap, but that's a much
2243  // less visible problem).
2244  if (xScale >= 0) {
2245    tx = splashFloor(mat[4] - 0.01);
2246    tx2 = splashFloor(mat[4] + xScale + 0.01);
2247  } else {
2248    tx = splashFloor(mat[4] + 0.01);
2249    tx2 = splashFloor(mat[4] + xScale - 0.01);
2250  }
2251  scaledWidth = abs(tx2 - tx) + 1;
2252  if (yScale >= 0) {
2253    ty = splashFloor(mat[5] - 0.01);
2254    ty2 = splashFloor(mat[5] + yScale + 0.01);
2255  } else {
2256    ty = splashFloor(mat[5] + 0.01);
2257    ty2 = splashFloor(mat[5] + yScale - 0.01);
2258  }
2259  scaledHeight = abs(ty2 - ty) + 1;
2260  xSign = (xScale < 0) ? -1 : 1;
2261  ySign = (yScale < 0) ? -1 : 1;
2262  yShear1 = (SplashCoord)xSign * yShear;
2263
2264  // clipping
2265  ulx1 = 0;
2266  uly1 = 0;
2267  urx1 = xSign * (scaledWidth - 1);
2268  ury1 = (int)(yShear * urx1);
2269  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
2270  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
2271  lrx1 = xSign * (scaledWidth - 1) +
2272           splashRound(xShear * ySign * (scaledHeight - 1));
2273  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
2274  if (rot) {
2275    ulx = tx + uly1;    uly = ty - ulx1;
2276    urx = tx + ury1;    ury = ty - urx1;
2277    llx = tx + lly1;    lly = ty - llx1;
2278    lrx = tx + lry1;    lry = ty - lrx1;
2279  } else {
2280    ulx = tx + ulx1;    uly = ty + uly1;
2281    urx = tx + urx1;    ury = ty + ury1;
2282    llx = tx + llx1;    lly = ty + lly1;
2283    lrx = tx + lrx1;    lry = ty + lry1;
2284  }
2285  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
2286                                   : (llx < lrx) ? llx : lrx
2287                     : (urx < llx) ? (urx < lrx) ? urx : lrx
2288                                   : (llx < lrx) ? llx : lrx;
2289  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
2290                                   : (llx > lrx) ? llx : lrx
2291                     : (urx > llx) ? (urx > lrx) ? urx : lrx
2292                                   : (llx > lrx) ? llx : lrx;
2293  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
2294                                   : (lly < lry) ? lly : lry
2295                     : (ury < lly) ? (ury < lry) ? ury : lry
2296                                   : (lly < lry) ? lly : lry;
2297  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
2298                                   : (lly > lry) ? lly : lry
2299                     : (ury > lly) ? (ury > lry) ? ury : lry
2300                                   : (lly > lry) ? lly : lry;
2301  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
2302  opClipRes = clipRes;
2303  if (clipRes == splashClipAllOutside) {
2304    return splashOk;
2305  }
2306
2307  // compute Bresenham parameters for x and y scaling
2308  yp = h / scaledHeight;
2309  yq = h % scaledHeight;
2310  xp = w / scaledWidth;
2311  xq = w % scaledWidth;
2312
2313  // allocate pixel buffers
2314  if (yp < 0 || yp > INT_MAX - 1) {
2315    return splashErrBadArg;
2316  }
2317  colorBuf = (SplashColorPtr)gmallocn3((yp + 1), w, nComps);
2318  if (srcAlpha) {
2319    alphaBuf = (Guchar *)gmallocn((yp + 1), w);
2320  } else {
2321    alphaBuf = NULL;
2322  }
2323
2324  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
2325#if SPLASH_CMYK
2326  pixAcc3 = 0; // make gcc happy
2327#endif
2328
2329  // initialize the pixel pipe
2330  pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,
2331           srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
2332           gFalse);
2333  if (vectorAntialias) {
2334    drawAAPixelInit();
2335  }
2336
2337  if (srcAlpha) {
2338
2339    // init y scale Bresenham
2340    yt = 0;
2341    lastYStep = 1;
2342
2343    for (y = 0; y < scaledHeight; ++y) {
2344
2345      // y scale Bresenham
2346      yStep = yp;
2347      yt += yq;
2348      if (yt >= scaledHeight) {
2349        yt -= scaledHeight;
2350        ++yStep;
2351      }
2352
2353      // read row(s) from image
2354      n = (yp > 0) ? yStep : lastYStep;
2355      if (n > 0) {
2356        p = colorBuf;
2357        q = alphaBuf;
2358        for (i = 0; i < n; ++i) {
2359          (*src)(srcData, p, q);
2360          p += w * nComps;
2361          q += w;
2362        }
2363      }
2364      lastYStep = yStep;
2365
2366      // loop-invariant constants
2367      k1 = splashRound(xShear * ySign * y);
2368 
2369      // clipping test
2370      if (clipRes != splashClipAllInside &&
2371          !rot &&
2372          (int)(yShear * k1) ==
2373            (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2374        if (xSign > 0) {
2375          spanXMin = tx + k1;
2376          spanXMax = spanXMin + (scaledWidth - 1);
2377        } else {
2378          spanXMax = tx + k1;
2379          spanXMin = spanXMax - (scaledWidth - 1);
2380        }
2381        spanY = ty + ySign * y + (int)(yShear * k1);
2382        clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2383        if (clipRes2 == splashClipAllOutside) {
2384          continue;
2385        }
2386      } else {
2387        clipRes2 = clipRes;
2388      }
2389
2390      // init x scale Bresenham
2391      xt = 0;
2392      xSrc = 0;
2393
2394      // x shear
2395      x1 = k1;
2396
2397      // y shear
2398      y1 = (SplashCoord)ySign * y + yShear * x1;
2399      // this is a kludge: if yShear1 is negative, then (int)y1 would
2400      // change immediately after the first pixel, which is not what
2401      // we want
2402      if (yShear1 < 0) {
2403        y1 += 0.999;
2404      }
2405
2406      // loop-invariant constants
2407      n = yStep > 0 ? yStep : 1;
2408
2409      switch (srcMode) {
2410
2411      case splashModeMono1:
2412      case splashModeMono8:
2413        for (x = 0; x < scaledWidth; ++x) {
2414
2415          // x scale Bresenham
2416          xStep = xp;
2417          xt += xq;
2418          if (xt >= scaledWidth) {
2419            xt -= scaledWidth;
2420            ++xStep;
2421          }
2422
2423          // rotation
2424          if (rot) {
2425            x2 = (int)y1;
2426            y2 = -x1;
2427          } else {
2428            x2 = x1;
2429            y2 = (int)y1;
2430          }
2431
2432          // compute the filtered pixel at (x,y) after the x and y scaling
2433          // operations
2434          m = xStep > 0 ? xStep : 1;
2435          alphaAcc = 0;
2436          p = colorBuf + xSrc;
2437          q = alphaBuf + xSrc;
2438          pixAcc0 = 0;
2439          for (i = 0; i < n; ++i) {
2440            for (j = 0; j < m; ++j) {
2441              pixAcc0 += *p++;
2442              alphaAcc += *q++;
2443            }
2444            p += w - m;
2445            q += w - m;
2446          }
2447          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2448          alphaMul = pixMul * (1.0 / 255.0);
2449          alpha = (SplashCoord)alphaAcc * alphaMul;
2450
2451          if (alpha > 0) {
2452            pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2453
2454            // set pixel
2455            pipe.shape = alpha;
2456            if (vectorAntialias && clipRes != splashClipAllInside) {
2457              drawAAPixel(&pipe, tx + x2, ty + y2);
2458            } else {
2459              drawPixel(&pipe, tx + x2, ty + y2,
2460                        clipRes2 == splashClipAllInside);
2461            }
2462          }
2463
2464          // x scale Bresenham
2465          xSrc += xStep;
2466
2467          // x shear
2468          x1 += xSign;
2469
2470          // y shear
2471          y1 += yShear1;
2472        }
2473        break;
2474
2475      case splashModeRGB8:
2476      case splashModeBGR8:
2477        for (x = 0; x < scaledWidth; ++x) {
2478
2479          // x scale Bresenham
2480          xStep = xp;
2481          xt += xq;
2482          if (xt >= scaledWidth) {
2483            xt -= scaledWidth;
2484            ++xStep;
2485          }
2486
2487          // rotation
2488          if (rot) {
2489            x2 = (int)y1;
2490            y2 = -x1;
2491          } else {
2492            x2 = x1;
2493            y2 = (int)y1;
2494          }
2495
2496          // compute the filtered pixel at (x,y) after the x and y scaling
2497          // operations
2498          m = xStep > 0 ? xStep : 1;
2499          alphaAcc = 0;
2500          p = colorBuf + xSrc * 3;
2501          q = alphaBuf + xSrc;
2502          pixAcc0 = pixAcc1 = pixAcc2 = 0;
2503          for (i = 0; i < n; ++i) {
2504            for (j = 0; j < m; ++j) {
2505              pixAcc0 += *p++;
2506              pixAcc1 += *p++;
2507              pixAcc2 += *p++;
2508              alphaAcc += *q++;
2509            }
2510            p += 3 * (w - m);
2511            q += w - m;
2512          }
2513          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2514          alphaMul = pixMul * (1.0 / 255.0);
2515          alpha = (SplashCoord)alphaAcc * alphaMul;
2516
2517          if (alpha > 0) {
2518            pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2519            pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2520            pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2521
2522            // set pixel
2523            pipe.shape = alpha;
2524            if (vectorAntialias && clipRes != splashClipAllInside) {
2525              drawAAPixel(&pipe, tx + x2, ty + y2);
2526            } else {
2527              drawPixel(&pipe, tx + x2, ty + y2,
2528                        clipRes2 == splashClipAllInside);
2529            }
2530          }
2531
2532          // x scale Bresenham
2533          xSrc += xStep;
2534
2535          // x shear
2536          x1 += xSign;
2537
2538          // y shear
2539          y1 += yShear1;
2540        }
2541        break;
2542
2543      case splashModeXBGR8:
2544        for (x = 0; x < scaledWidth; ++x) {
2545          // x scale Bresenham
2546          xStep = xp;
2547          xt += xq;
2548          if (xt >= scaledWidth) {
2549            xt -= scaledWidth;
2550            ++xStep;
2551          }
2552
2553          // rotation
2554          if (rot) {
2555            x2 = (int)y1;
2556            y2 = -x1;
2557          } else {
2558            x2 = x1;
2559            y2 = (int)y1;
2560          }
2561
2562          // compute the filtered pixel at (x,y) after the x and y scaling
2563          // operations
2564          m = xStep > 0 ? xStep : 1;
2565          alphaAcc = 0;
2566          p = colorBuf + xSrc * 4;
2567          q = alphaBuf + xSrc;
2568          pixAcc0 = pixAcc1 = pixAcc2 = 0;
2569          for (i = 0; i < n; ++i) {
2570            for (j = 0; j < m; ++j) {
2571              pixAcc0 += *p++;
2572              pixAcc1 += *p++;
2573              pixAcc2 += *p++;
2574              *p++;
2575              alphaAcc += *q++;
2576            }
2577            p += 4 * (w - m);
2578            q += w - m;
2579          }
2580          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2581          alphaMul = pixMul * (1.0 / 255.0);
2582          alpha = (SplashCoord)alphaAcc * alphaMul;
2583
2584          if (alpha > 0) {
2585            pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2586            pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2587            pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2588            pix[3] = 255;
2589
2590            // set pixel
2591            pipe.shape = alpha;
2592            if (vectorAntialias && clipRes != splashClipAllInside) {
2593              drawAAPixel(&pipe, tx + x2, ty + y2);
2594            } else {
2595              drawPixel(&pipe, tx + x2, ty + y2,
2596                        clipRes2 == splashClipAllInside);
2597            }
2598          }
2599
2600          // x scale Bresenham
2601          xSrc += xStep;
2602
2603          // x shear
2604          x1 += xSign;
2605
2606          // y shear
2607          y1 += yShear1;
2608        }
2609        break;
2610
2611
2612#if SPLASH_CMYK
2613      case splashModeCMYK8:
2614        for (x = 0; x < scaledWidth; ++x) {
2615
2616          // x scale Bresenham
2617          xStep = xp;
2618          xt += xq;
2619          if (xt >= scaledWidth) {
2620            xt -= scaledWidth;
2621            ++xStep;
2622          }
2623
2624          // rotation
2625          if (rot) {
2626            x2 = (int)y1;
2627            y2 = -x1;
2628          } else {
2629            x2 = x1;
2630            y2 = (int)y1;
2631          }
2632
2633          // compute the filtered pixel at (x,y) after the x and y scaling
2634          // operations
2635          m = xStep > 0 ? xStep : 1;
2636          alphaAcc = 0;
2637          p = colorBuf + xSrc * 4;
2638          q = alphaBuf + xSrc;
2639          pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2640          for (i = 0; i < n; ++i) {
2641            for (j = 0; j < m; ++j) {
2642              pixAcc0 += *p++;
2643              pixAcc1 += *p++;
2644              pixAcc2 += *p++;
2645              pixAcc3 += *p++;
2646              alphaAcc += *q++;
2647            }
2648            p += 4 * (w - m);
2649            q += w - m;
2650          }
2651          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2652          alphaMul = pixMul * (1.0 / 255.0);
2653          alpha = (SplashCoord)alphaAcc * alphaMul;
2654
2655          if (alpha > 0) {
2656            pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2657            pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2658            pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2659            pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2660
2661            // set pixel
2662            pipe.shape = alpha;
2663            if (vectorAntialias && clipRes != splashClipAllInside) {
2664              drawAAPixel(&pipe, tx + x2, ty + y2);
2665            } else {
2666              drawPixel(&pipe, tx + x2, ty + y2,
2667                        clipRes2 == splashClipAllInside);
2668            }
2669          }
2670
2671          // x scale Bresenham
2672          xSrc += xStep;
2673
2674          // x shear
2675          x1 += xSign;
2676
2677          // y shear
2678          y1 += yShear1;
2679        }
2680        break;
2681#endif // SPLASH_CMYK
2682      }
2683    }
2684
2685  } else {
2686
2687    // init y scale Bresenham
2688    yt = 0;
2689    lastYStep = 1;
2690
2691    for (y = 0; y < scaledHeight; ++y) {
2692
2693      // y scale Bresenham
2694      yStep = yp;
2695      yt += yq;
2696      if (yt >= scaledHeight) {
2697        yt -= scaledHeight;
2698        ++yStep;
2699      }
2700
2701      // read row(s) from image
2702      n = (yp > 0) ? yStep : lastYStep;
2703      if (n > 0) {
2704        p = colorBuf;
2705        for (i = 0; i < n; ++i) {
2706          (*src)(srcData, p, NULL);
2707          p += w * nComps;
2708        }
2709      }
2710      lastYStep = yStep;
2711
2712      // loop-invariant constants
2713      k1 = splashRound(xShear * ySign * y);
2714
2715      // clipping test
2716      if (clipRes != splashClipAllInside &&
2717          !rot &&
2718          (int)(yShear * k1) ==
2719            (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2720        if (xSign > 0) {
2721          spanXMin = tx + k1;
2722          spanXMax = spanXMin + (scaledWidth - 1);
2723        } else {
2724          spanXMax = tx + k1;
2725          spanXMin = spanXMax - (scaledWidth - 1);
2726        }
2727        spanY = ty + ySign * y + (int)(yShear * k1);
2728        clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2729        if (clipRes2 == splashClipAllOutside) {
2730          continue;
2731        }
2732      } else {
2733        clipRes2 = clipRes;
2734      }
2735
2736      // init x scale Bresenham
2737      xt = 0;
2738      xSrc = 0;
2739
2740      // x shear
2741      x1 = k1;
2742
2743      // y shear
2744      y1 = (SplashCoord)ySign * y + yShear * x1;
2745      // this is a kludge: if yShear1 is negative, then (int)y1 would
2746      // change immediately after the first pixel, which is not what
2747      // we want
2748      if (yShear1 < 0) {
2749        y1 += 0.999;
2750      }
2751
2752      // loop-invariant constants
2753      n = yStep > 0 ? yStep : 1;
2754
2755      switch (srcMode) {
2756
2757      case splashModeMono1:
2758      case splashModeMono8:
2759        for (x = 0; x < scaledWidth; ++x) {
2760
2761          // x scale Bresenham
2762          xStep = xp;
2763          xt += xq;
2764          if (xt >= scaledWidth) {
2765            xt -= scaledWidth;
2766            ++xStep;
2767          }
2768
2769          // rotation
2770          if (rot) {
2771            x2 = (int)y1;
2772            y2 = -x1;
2773          } else {
2774            x2 = x1;
2775            y2 = (int)y1;
2776          }
2777
2778          // compute the filtered pixel at (x,y) after the x and y scaling
2779          // operations
2780          m = xStep > 0 ? xStep : 1;
2781          p = colorBuf + xSrc;
2782          pixAcc0 = 0;
2783          for (i = 0; i < n; ++i) {
2784            for (j = 0; j < m; ++j) {
2785              pixAcc0 += *p++;
2786            }
2787            p += w - m;
2788          }
2789          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2790
2791          pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2792
2793          // set pixel
2794          if (vectorAntialias && clipRes != splashClipAllInside) {
2795            pipe.shape = (SplashCoord)1;
2796            drawAAPixel(&pipe, tx + x2, ty + y2);
2797          } else {
2798            drawPixel(&pipe, tx + x2, ty + y2,
2799                      clipRes2 == splashClipAllInside);
2800          }
2801
2802          // x scale Bresenham
2803          xSrc += xStep;
2804
2805          // x shear
2806          x1 += xSign;
2807
2808          // y shear
2809          y1 += yShear1;
2810        }
2811        break;
2812
2813      case splashModeRGB8:
2814      case splashModeBGR8:
2815        for (x = 0; x < scaledWidth; ++x) {
2816
2817          // x scale Bresenham
2818          xStep = xp;
2819          xt += xq;
2820          if (xt >= scaledWidth) {
2821            xt -= scaledWidth;
2822            ++xStep;
2823          }
2824
2825          // rotation
2826          if (rot) {
2827            x2 = (int)y1;
2828            y2 = -x1;
2829          } else {
2830            x2 = x1;
2831            y2 = (int)y1;
2832          }
2833
2834          // compute the filtered pixel at (x,y) after the x and y scaling
2835          // operations
2836          m = xStep > 0 ? xStep : 1;
2837          p = colorBuf + xSrc * 3;
2838          pixAcc0 = pixAcc1 = pixAcc2 = 0;
2839          for (i = 0; i < n; ++i) {
2840            for (j = 0; j < m; ++j) {
2841              pixAcc0 += *p++;
2842              pixAcc1 += *p++;
2843              pixAcc2 += *p++;
2844            }
2845            p += 3 * (w - m);
2846          }
2847          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2848
2849          pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2850          pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2851          pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2852
2853          // set pixel
2854          if (vectorAntialias && clipRes != splashClipAllInside) {
2855            pipe.shape = (SplashCoord)1;
2856            drawAAPixel(&pipe, tx + x2, ty + y2);
2857          } else {
2858            drawPixel(&pipe, tx + x2, ty + y2,
2859                      clipRes2 == splashClipAllInside);
2860          }
2861
2862          // x scale Bresenham
2863          xSrc += xStep;
2864
2865          // x shear
2866          x1 += xSign;
2867
2868          // y shear
2869          y1 += yShear1;
2870        }
2871        break;
2872
2873      case splashModeXBGR8:
2874        for (x = 0; x < scaledWidth; ++x) {
2875
2876          // x scale Bresenham
2877          xStep = xp;
2878          xt += xq;
2879          if (xt >= scaledWidth) {
2880            xt -= scaledWidth;
2881            ++xStep;
2882          }
2883
2884          // rotation
2885          if (rot) {
2886            x2 = (int)y1;
2887            y2 = -x1;
2888          } else {
2889            x2 = x1;
2890            y2 = (int)y1;
2891          }
2892
2893          // compute the filtered pixel at (x,y) after the x and y scaling
2894          // operations
2895          m = xStep > 0 ? xStep : 1;
2896          p = colorBuf + xSrc * 4;
2897          pixAcc0 = pixAcc1 = pixAcc2 = 0;
2898          for (i = 0; i < n; ++i) {
2899            for (j = 0; j < m; ++j) {
2900              pixAcc0 += *p++;
2901              pixAcc1 += *p++;
2902              pixAcc2 += *p++;
2903              *p++;
2904            }
2905            p += 4 * (w - m);
2906          }
2907          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2908
2909          pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2910          pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2911          pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2912          pix[3] = 255;
2913
2914          // set pixel
2915          if (vectorAntialias && clipRes != splashClipAllInside) {
2916            pipe.shape = (SplashCoord)1;
2917            drawAAPixel(&pipe, tx + x2, ty + y2);
2918          } else {
2919            drawPixel(&pipe, tx + x2, ty + y2,
2920                      clipRes2 == splashClipAllInside);
2921          }
2922
2923          // x scale Bresenham
2924          xSrc += xStep;
2925
2926          // x shear
2927          x1 += xSign;
2928
2929          // y shear
2930          y1 += yShear1;
2931        }
2932        break;
2933
2934#if SPLASH_CMYK
2935      case splashModeCMYK8:
2936        for (x = 0; x < scaledWidth; ++x) {
2937
2938          // x scale Bresenham
2939          xStep = xp;
2940          xt += xq;
2941          if (xt >= scaledWidth) {
2942            xt -= scaledWidth;
2943            ++xStep;
2944          }
2945
2946          // rotation
2947          if (rot) {
2948            x2 = (int)y1;
2949            y2 = -x1;
2950          } else {
2951            x2 = x1;
2952            y2 = (int)y1;
2953          }
2954
2955          // compute the filtered pixel at (x,y) after the x and y scaling
2956          // operations
2957          m = xStep > 0 ? xStep : 1;
2958          p = colorBuf + xSrc * 4;
2959          pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2960          for (i = 0; i < n; ++i) {
2961            for (j = 0; j < m; ++j) {
2962              pixAcc0 += *p++;
2963              pixAcc1 += *p++;
2964              pixAcc2 += *p++;
2965              pixAcc3 += *p++;
2966            }
2967            p += 4 * (w - m);
2968          }
2969          pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2970
2971          pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2972          pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2973          pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2974          pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2975
2976          // set pixel
2977          if (vectorAntialias && clipRes != splashClipAllInside) {
2978            pipe.shape = (SplashCoord)1;
2979            drawAAPixel(&pipe, tx + x2, ty + y2);
2980          } else {
2981            drawPixel(&pipe, tx + x2, ty + y2,
2982                      clipRes2 == splashClipAllInside);
2983          }
2984
2985          // x scale Bresenham
2986          xSrc += xStep;
2987
2988          // x shear
2989          x1 += xSign;
2990
2991          // y shear
2992          y1 += yShear1;
2993        }
2994        break;
2995#endif // SPLASH_CMYK
2996      }
2997    }
2998
2999  }
3000
3001  gfree(colorBuf);
3002  gfree(alphaBuf);
3003
3004  return splashOk;
3005}
3006
3007SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
3008                              int xDest, int yDest, int w, int h,
3009                              GBool noClip, GBool nonIsolated) {
3010  SplashPipe pipe;
3011  SplashColor pixel;
3012  Guchar alpha;
3013  Guchar *ap;
3014  int x, y;
3015
3016  if (src->mode != bitmap->mode) {
3017    return splashErrModeMismatch;
3018  }
3019
3020  if (src->alpha) {
3021    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
3022             gTrue, nonIsolated);
3023    for (y = 0; y < h; ++y) {
3024      pipeSetXY(&pipe, xDest, yDest + y);
3025      ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
3026      for (x = 0; x < w; ++x) {
3027        alpha = *ap++;
3028        if (noClip || state->clip->test(xDest + x, yDest + y)) {
3029          // this uses shape instead of alpha, which isn't technically
3030          // correct, but works out the same
3031          src->getPixel(xSrc + x, ySrc + y, pixel);
3032          pipe.shape = (SplashCoord)(alpha / 255.0);
3033          pipeRun(&pipe);
3034          updateModX(xDest + x);
3035          updateModY(yDest + y);
3036        } else {
3037          pipeIncX(&pipe);
3038        }
3039      }
3040    }
3041  } else {
3042    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
3043             gFalse, nonIsolated);
3044    for (y = 0; y < h; ++y) {
3045      pipeSetXY(&pipe, xDest, yDest + y);
3046      for (x = 0; x < w; ++x) {
3047        if (noClip || state->clip->test(xDest + x, yDest + y)) {
3048          src->getPixel(xSrc + x, ySrc + y, pixel);
3049          pipeRun(&pipe);
3050          updateModX(xDest + x);
3051          updateModY(yDest + y);
3052        } else {
3053          pipeIncX(&pipe);
3054        }
3055      }
3056    }
3057  }
3058
3059  return splashOk;
3060}
3061
3062void Splash::compositeBackground(SplashColorPtr color) {
3063  SplashColorPtr p;
3064  Guchar *q;
3065  Guchar alpha, alpha1, c, color0, color1, color2;
3066#if SPLASH_CMYK
3067  Guchar color3;
3068#endif
3069  int x, y, mask;
3070
3071  switch (bitmap->mode) {
3072  case splashModeMono1:
3073    color0 = color[0];
3074    for (y = 0; y < bitmap->height; ++y) {
3075      p = &bitmap->data[y * bitmap->rowSize];
3076      q = &bitmap->alpha[y * bitmap->width];
3077      mask = 0x80;
3078      for (x = 0; x < bitmap->width; ++x) {
3079        alpha = *q++;
3080        alpha1 = 255 - alpha;
3081        c = (*p & mask) ? 0xff : 0x00;
3082        c = div255(alpha1 * color0 + alpha * c);
3083        if (c & 0x80) {
3084          *p |= mask;
3085        } else {
3086          *p &= ~mask;
3087        }
3088        if (!(mask >>= 1)) {
3089          mask = 0x80;
3090          ++p;
3091        }
3092      }
3093    }
3094    break;
3095  case splashModeMono8:
3096    color0 = color[0];
3097    for (y = 0; y < bitmap->height; ++y) {
3098      p = &bitmap->data[y * bitmap->rowSize];
3099      q = &bitmap->alpha[y * bitmap->width];
3100      for (x = 0; x < bitmap->width; ++x) {
3101        alpha = *q++;
3102        alpha1 = 255 - alpha;
3103        p[0] = div255(alpha1 * color0 + alpha * p[0]);
3104        ++p;
3105      }
3106    }
3107    break;
3108  case splashModeRGB8:
3109  case splashModeBGR8:
3110    color0 = color[0];
3111    color1 = color[1];
3112    color2 = color[2];
3113    for (y = 0; y < bitmap->height; ++y) {
3114      p = &bitmap->data[y * bitmap->rowSize];
3115      q = &bitmap->alpha[y * bitmap->width];
3116      for (x = 0; x < bitmap->width; ++x) {
3117        alpha = *q++;
3118        if (alpha == 0)
3119        {
3120          p[0] = color0;
3121          p[1] = color1;
3122          p[2] = color2;
3123        }
3124        else if (alpha != 255)
3125        {
3126          alpha1 = 255 - alpha;
3127          p[0] = div255(alpha1 * color0 + alpha * p[0]);
3128          p[1] = div255(alpha1 * color1 + alpha * p[1]);
3129          p[2] = div255(alpha1 * color2 + alpha * p[2]);
3130        }
3131        p += 3;
3132      }
3133    }
3134    break;
3135  case splashModeXBGR8:
3136    color0 = color[0];
3137    color1 = color[1];
3138    color2 = color[2];
3139    for (y = 0; y < bitmap->height; ++y) {
3140      p = &bitmap->data[y * bitmap->rowSize];
3141      q = &bitmap->alpha[y * bitmap->width];
3142      for (x = 0; x < bitmap->width; ++x) {
3143        alpha = *q++;
3144        if (alpha == 0)
3145        {
3146          p[0] = color0;
3147          p[1] = color1;
3148          p[2] = color2;
3149        }
3150        else if (alpha != 255)
3151        {
3152          alpha1 = 255 - alpha;
3153          p[0] = div255(alpha1 * color0 + alpha * p[0]);
3154          p[1] = div255(alpha1 * color1 + alpha * p[1]);
3155          p[2] = div255(alpha1 * color2 + alpha * p[2]);
3156        }
3157        p[3] = 255;
3158        p += 4;
3159      }
3160    }
3161    break;
3162#if SPLASH_CMYK
3163  case splashModeCMYK8:
3164    color0 = color[0];
3165    color1 = color[1];
3166    color2 = color[2];
3167    color3 = color[3];
3168    for (y = 0; y < bitmap->height; ++y) {
3169      p = &bitmap->data[y * bitmap->rowSize];
3170      q = &bitmap->alpha[y * bitmap->width];
3171      for (x = 0; x < bitmap->width; ++x) {
3172        alpha = *q++;
3173        alpha1 = 255 - alpha;
3174        p[0] = div255(alpha1 * color0 + alpha * p[0]);
3175        p[1] = div255(alpha1 * color1 + alpha * p[1]);
3176        p[2] = div255(alpha1 * color2 + alpha * p[2]);
3177        p[3] = div255(alpha1 * color3 + alpha * p[3]);
3178        p += 4;
3179      }
3180    }
3181    break;
3182#endif
3183  }
3184  memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
3185}
3186
3187SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
3188                                    int xDest, int yDest, int w, int h) {
3189  SplashColor pixel;
3190  SplashColorPtr p, sp;
3191  Guchar *q;
3192  int x, y, mask;
3193
3194  if (src->mode != bitmap->mode) {
3195    return splashErrModeMismatch;
3196  }
3197
3198  switch (bitmap->mode) {
3199  case splashModeMono1:
3200    for (y = 0; y < h; ++y) {
3201      p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
3202      mask = 0x80 >> (xDest & 7);
3203      for (x = 0; x < w; ++x) {
3204        src->getPixel(xSrc + x, ySrc + y, pixel);
3205        if (pixel[0]) {
3206          *p |= mask;
3207        } else {
3208          *p &= ~mask;
3209        }
3210        if (!(mask >>= 1)) {
3211          mask = 0x80;
3212          ++p;
3213        }
3214      }
3215    }
3216    break;
3217  case splashModeMono8:
3218    for (y = 0; y < h; ++y) {
3219      p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
3220      for (x = 0; x < w; ++x) {
3221        src->getPixel(xSrc + x, ySrc + y, pixel);
3222        *p++ = pixel[0];
3223      }
3224    }
3225    break;
3226  case splashModeRGB8:
3227  case splashModeBGR8:
3228    for (y = 0; y < h; ++y) {
3229      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
3230      sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
3231      for (x = 0; x < w; ++x) {
3232        *p++ = *sp++;
3233        *p++ = *sp++;
3234        *p++ = *sp++;
3235      }
3236    }
3237    break;
3238  case splashModeXBGR8:
3239    for (y = 0; y < h; ++y) {
3240      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
3241      sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
3242      for (x = 0; x < w; ++x) {
3243        *p++ = *sp++;
3244        *p++ = *sp++;
3245        *p++ = *sp++;
3246        *p++ = 255;
3247        *sp++;
3248      }
3249    }
3250    break;
3251#if SPLASH_CMYK
3252  case splashModeCMYK8:
3253    for (y = 0; y < h; ++y) {
3254      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
3255      for (x = 0; x < w; ++x) {
3256        src->getPixel(xSrc + x, ySrc + y, pixel);
3257        *p++ = pixel[0];
3258        *p++ = pixel[1];
3259        *p++ = pixel[2];
3260        *p++ = pixel[3];
3261      }
3262    }
3263    break;
3264#endif
3265  }
3266
3267  if (bitmap->alpha) {
3268    for (y = 0; y < h; ++y) {
3269      q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
3270      for (x = 0; x < w; ++x) {
3271        *q++ = 0x00;
3272      }
3273    }
3274  }
3275
3276  return splashOk;
3277}
3278
3279SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
3280  SplashPath *pathIn, *pathOut;
3281  SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
3282  SplashCoord crossprod, dotprod, miter, m;
3283  GBool first, last, closed;
3284  int subpathStart, next, i;
3285  int left0, left1, left2, right0, right1, right2, join0, join1, join2;
3286  int leftFirst, rightFirst, firstPt;
3287
3288  if (flatten) {
3289    pathIn = flattenPath(path, state->matrix, state->flatness);
3290    if (state->lineDashLength > 0) {
3291      pathOut = makeDashedPath(pathIn);
3292      delete pathIn;
3293      pathIn = pathOut;
3294    }
3295  } else {
3296    pathIn = path;
3297  }
3298
3299  subpathStart = 0; // make gcc happy
3300  closed = gFalse; // make gcc happy
3301  left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
3302  leftFirst = rightFirst = firstPt = 0; // make gcc happy
3303
3304  pathOut = new SplashPath();
3305  w = state->lineWidth;
3306
3307  for (i = 0; i < pathIn->length - 1; ++i) {
3308    if (pathIn->flags[i] & splashPathLast) {
3309      continue;
3310    }
3311    if ((first = pathIn->flags[i] & splashPathFirst)) {
3312      subpathStart = i;
3313      closed = pathIn->flags[i] & splashPathClosed;
3314    }
3315    last = pathIn->flags[i+1] & splashPathLast;
3316
3317    // compute the deltas for segment (i, i+1)
3318    d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
3319                   pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3320    if (d == 0) {
3321      // we need to draw end caps on zero-length lines
3322      //~ not clear what the behavior should be for splashLineCapButt
3323      //~   with d==0
3324      dx = 0;
3325      dy = 1;
3326    } else {
3327      d = (SplashCoord)1 / d;
3328      dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
3329      dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
3330    }
3331    wdx = (SplashCoord)0.5 * w * dx;
3332    wdy = (SplashCoord)0.5 * w * dy;
3333
3334    // compute the deltas for segment (i+1, next)
3335    next = last ? subpathStart + 1 : i + 2;
3336    d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
3337                   pathIn->pts[next].x, pathIn->pts[next].y);
3338    if (d == 0) {
3339      // we need to draw end caps on zero-length lines
3340      //~ not clear what the behavior should be for splashLineCapButt
3341      //~   with d==0
3342      dxNext = 0;
3343      dyNext = 1;
3344    } else {
3345      d = (SplashCoord)1 / d;
3346      dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
3347      dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
3348    }
3349    wdxNext = (SplashCoord)0.5 * w * dxNext;
3350    wdyNext = (SplashCoord)0.5 * w * dyNext;
3351
3352    // draw the start cap
3353    pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
3354    if (i == subpathStart) {
3355      firstPt = pathOut->length - 1;
3356    }
3357    if (first && !closed) {
3358      switch (state->lineCap) {
3359      case splashLineCapButt:
3360        pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3361        break;
3362      case splashLineCapRound:
3363        pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
3364                         pathIn->pts[i].y + wdx - bezierCircle * wdy,
3365                         pathIn->pts[i].x - wdx - bezierCircle * wdy,
3366                         pathIn->pts[i].y - wdy + bezierCircle * wdx,
3367                         pathIn->pts[i].x - wdx,
3368                         pathIn->pts[i].y - wdy);
3369        pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
3370                         pathIn->pts[i].y - wdy - bezierCircle * wdx,
3371                         pathIn->pts[i].x + wdy - bezierCircle * wdx,
3372                         pathIn->pts[i].y - wdx - bezierCircle * wdy,
3373                         pathIn->pts[i].x + wdy,
3374                         pathIn->pts[i].y - wdx);
3375        break;
3376      case splashLineCapProjecting:
3377        pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
3378                        pathIn->pts[i].y + wdx - wdy);
3379        pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
3380                        pathIn->pts[i].y - wdx - wdy);
3381        pathOut->lineTo(pathIn->pts[i].x + wdy,
3382                        pathIn->pts[i].y - wdx);
3383        break;
3384      }
3385    } else {
3386      pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3387    }
3388
3389    // draw the left side of the segment rectangle
3390    left2 = pathOut->length - 1;
3391    pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);
3392
3393    // draw the end cap
3394    if (last && !closed) {
3395      switch (state->lineCap) {
3396      case splashLineCapButt:
3397        pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3398        break;
3399      case splashLineCapRound:
3400        pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
3401                         pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
3402                         pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
3403                         pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
3404                         pathIn->pts[i+1].x + wdx,
3405                         pathIn->pts[i+1].y + wdy);
3406        pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
3407                         pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
3408                         pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
3409                         pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
3410                         pathIn->pts[i+1].x - wdy,
3411                         pathIn->pts[i+1].y + wdx);
3412        break;
3413      case splashLineCapProjecting:
3414        pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
3415                        pathIn->pts[i+1].y - wdx + wdy);
3416        pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
3417                        pathIn->pts[i+1].y + wdx + wdy);
3418        pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3419                        pathIn->pts[i+1].y + wdx);
3420        break;
3421      }
3422    } else {
3423      pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3424    }
3425
3426    // draw the right side of the segment rectangle
3427    right2 = pathOut->length - 1;
3428    pathOut->close();
3429
3430    // draw the join
3431    join2 = pathOut->length;
3432    if (!last || closed) {
3433      crossprod = dx * dyNext - dy * dxNext;
3434      dotprod = -(dx * dxNext + dy * dyNext);
3435      if (dotprod > 0.99999) {
3436        // avoid a divide-by-zero -- set miter to something arbitrary
3437        // such that sqrt(miter) will exceed miterLimit (and m is never
3438        // used in that situation)
3439        miter = (state->miterLimit + 1) * (state->miterLimit + 1);
3440        m = 0;
3441      } else {
3442        miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
3443        if (miter < 1) {
3444          // this can happen because of floating point inaccuracies
3445          miter = 1;
3446        }
3447        m = splashSqrt(miter - 1);
3448      }
3449
3450      // round join
3451      if (state->lineJoin == splashLineJoinRound) {
3452        pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3453                        pathIn->pts[i+1].y);
3454        pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3455                         pathIn->pts[i+1].y + bezierCircle2 * w,
3456                         pathIn->pts[i+1].x + bezierCircle2 * w,
3457                         pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3458                         pathIn->pts[i+1].x,
3459                         pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
3460        pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
3461                         pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3462                         pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3463                         pathIn->pts[i+1].y + bezierCircle2 * w,
3464                         pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3465                         pathIn->pts[i+1].y);
3466        pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3467                         pathIn->pts[i+1].y - bezierCircle2 * w,
3468                         pathIn->pts[i+1].x - bezierCircle2 * w,
3469                         pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3470                         pathIn->pts[i+1].x,
3471                         pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
3472        pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
3473                         pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3474                         pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3475                         pathIn->pts[i+1].y - bezierCircle2 * w,
3476                         pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3477                         pathIn->pts[i+1].y);
3478
3479      } else {
3480        pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3481
3482        // angle < 180
3483        if (crossprod < 0) {
3484          pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
3485                          pathIn->pts[i+1].y + wdxNext);
3486          // miter join inside limit
3487          if (state->lineJoin == splashLineJoinMiter &&
3488              splashSqrt(miter) <= state->miterLimit) {
3489            pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
3490                            pathIn->pts[i+1].y + wdx + wdy * m);
3491            pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3492                            pathIn->pts[i+1].y + wdx);
3493          // bevel join or miter join outside limit
3494          } else {
3495            pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3496          }
3497
3498        // angle >= 180
3499        } else {
3500          pathOut->lineTo(pathIn->pts[i+1].x + wdy,
3501                          pathIn->pts[i+1].y - wdx);
3502          // miter join inside limit
3503          if (state->lineJoin == splashLineJoinMiter &&
3504              splashSqrt(miter) <= state->miterLimit) {
3505            pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
3506                            pathIn->pts[i+1].y - wdx + wdy * m);
3507            pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3508                            pathIn->pts[i+1].y - wdxNext);
3509          // bevel join or miter join outside limit
3510          } else {
3511            pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3512                            pathIn->pts[i+1].y - wdxNext);
3513          }
3514        }
3515      }
3516
3517      pathOut->close();
3518    }
3519
3520    // add stroke adjustment hints
3521    if (state->strokeAdjust) {
3522      if (i >= subpathStart + 1) {
3523        if (i >= subpathStart + 2) {
3524          pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3525          pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
3526        } else {
3527          pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
3528        }
3529        pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
3530      }
3531      left0 = left1;
3532      left1 = left2;
3533      right0 = right1;
3534      right1 = right2;
3535      join0 = join1;
3536      join1 = join2;
3537      if (i == subpathStart) {
3538        leftFirst = left2;
3539        rightFirst = right2;
3540      }
3541      if (last) {
3542        if (i >= subpathStart + 2) {
3543          pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3544          pathOut->addStrokeAdjustHint(left1, right1,
3545                                       join0, pathOut->length - 1);
3546        } else {
3547          pathOut->addStrokeAdjustHint(left1, right1,
3548                                       firstPt, pathOut->length - 1);
3549        }
3550        if (closed) {
3551          pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
3552          pathOut->addStrokeAdjustHint(left1, right1,
3553                                       rightFirst + 1, rightFirst + 1);
3554          pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3555                                       left1 + 1, right1);
3556          pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3557                                       join1, pathOut->length - 1);
3558        }
3559      }
3560    }
3561  }
3562
3563  if (pathIn != path) {
3564    delete pathIn;
3565  }
3566
3567  return pathOut;
3568}
3569
3570void Splash::dumpPath(SplashPath *path) {
3571  int i;
3572
3573  for (i = 0; i < path->length; ++i) {
3574    printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
3575           i, (double)path->pts[i].x, (double)path->pts[i].y,
3576           (path->flags[i] & splashPathFirst) ? " first" : "",
3577           (path->flags[i] & splashPathLast) ? " last" : "",
3578           (path->flags[i] & splashPathClosed) ? " closed" : "",
3579           (path->flags[i] & splashPathCurve) ? " curve" : "");
3580  }
3581}
3582
3583void Splash::dumpXPath(SplashXPath *path) {
3584  int i;
3585
3586  for (i = 0; i < path->length; ++i) {
3587    printf("  %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
3588           i, (double)path->segs[i].x0, (double)path->segs[i].y0,
3589           (double)path->segs[i].x1, (double)path->segs[i].y1,
3590           (path->segs[i].flags & splashXPathFirst) ? "F" : " ",
3591           (path->segs[i].flags & splashXPathLast) ? "L" : " ",
3592           (path->segs[i].flags & splashXPathEnd0) ? "0" : " ",
3593           (path->segs[i].flags & splashXPathEnd1) ? "1" : " ",
3594           (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
3595           (path->segs[i].flags & splashXPathVert) ? "V" : " ",
3596           (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
3597  }
3598}
Note: See TracBrowser for help on using the repository browser.