oops.. only build it when it _is_ valid.
[AROS-Contrib.git] / arospdf / splash / Splash.cc
blob537ee1a7901171d852a877e1c19a93f993c3b607
1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
7 #include <aconf.h>
9 #ifdef USE_GCC_PRAGMAS
10 #pragma implementation
11 #endif
13 #include <stdlib.h>
14 #include <string.h>
15 #include "gmem.h"
16 #include "SplashErrorCodes.h"
17 #include "SplashMath.h"
18 #include "SplashBitmap.h"
19 #include "SplashState.h"
20 #include "SplashPath.h"
21 #include "SplashXPath.h"
22 #include "SplashXPathScanner.h"
23 #include "SplashPattern.h"
24 #include "SplashScreen.h"
25 #include "SplashFont.h"
26 #include "SplashGlyphBitmap.h"
27 #include "Splash.h"
29 //------------------------------------------------------------------------
31 // distance of Bezier control point from center for circle approximation
32 // = (4 * (sqrt(2) - 1) / 3) * r
33 #define bezierCircle ((SplashCoord)0.55228475)
34 #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
36 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
37 static inline Guchar div255(int x) {
38 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
41 //------------------------------------------------------------------------
42 // SplashPipe
43 //------------------------------------------------------------------------
45 #define splashPipeMaxStages 9
47 struct SplashPipe {
48 // pixel coordinates
49 int x, y;
51 // source pattern
52 SplashPattern *pattern;
54 // source alpha and color
55 SplashCoord aInput;
56 GBool usesShape;
57 Guchar aSrc;
58 SplashColorPtr cSrc;
59 SplashColor cSrcVal;
61 // non-isolated group alpha0
62 Guchar *alpha0Ptr;
64 // soft mask
65 SplashColorPtr softMaskPtr;
67 // destination alpha and color
68 SplashColorPtr destColorPtr;
69 int destColorMask;
70 Guchar *destAlphaPtr;
72 // shape
73 SplashCoord shape;
75 // result alpha and color
76 GBool noTransparency;
77 SplashPipeResultColorCtrl resultColorCtrl;
79 // non-isolated group correction
80 int nonIsolatedGroup;
83 SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
84 splashPipeResultColorNoAlphaBlendMono,
85 splashPipeResultColorNoAlphaBlendMono,
86 splashPipeResultColorNoAlphaBlendRGB,
87 splashPipeResultColorNoAlphaBlendRGB
88 #if SPLASH_CMYK
90 splashPipeResultColorNoAlphaBlendCMYK
91 #endif
94 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
95 splashPipeResultColorAlphaNoBlendMono,
96 splashPipeResultColorAlphaNoBlendMono,
97 splashPipeResultColorAlphaNoBlendRGB,
98 splashPipeResultColorAlphaNoBlendRGB
99 #if SPLASH_CMYK
101 splashPipeResultColorAlphaNoBlendCMYK
102 #endif
105 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
106 splashPipeResultColorAlphaBlendMono,
107 splashPipeResultColorAlphaBlendMono,
108 splashPipeResultColorAlphaBlendRGB,
109 splashPipeResultColorAlphaBlendRGB
110 #if SPLASH_CMYK
112 splashPipeResultColorAlphaBlendCMYK
113 #endif
116 //------------------------------------------------------------------------
118 static void blendXor(SplashColorPtr src, SplashColorPtr dest,
119 SplashColorPtr blend, SplashColorMode cm) {
120 int i;
122 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
123 blend[i] = src[i] ^ dest[i];
127 //------------------------------------------------------------------------
128 // modified region
129 //------------------------------------------------------------------------
131 void Splash::clearModRegion() {
132 modXMin = bitmap->getWidth();
133 modYMin = bitmap->getHeight();
134 modXMax = -1;
135 modYMax = -1;
138 inline void Splash::updateModX(int x) {
139 if (x < modXMin) {
140 modXMin = x;
142 if (x > modXMax) {
143 modXMax = x;
147 inline void Splash::updateModY(int y) {
148 if (y < modYMin) {
149 modYMin = y;
151 if (y > modYMax) {
152 modYMax = y;
156 //------------------------------------------------------------------------
157 // pipeline
158 //------------------------------------------------------------------------
160 inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
161 SplashPattern *pattern, SplashColorPtr cSrc,
162 SplashCoord aInput, GBool usesShape,
163 GBool nonIsolatedGroup) {
164 pipeSetXY(pipe, x, y);
165 pipe->pattern = NULL;
167 // source color
168 if (pattern) {
169 if (pattern->isStatic()) {
170 pattern->getColor(x, y, pipe->cSrcVal);
171 } else {
172 pipe->pattern = pattern;
174 pipe->cSrc = pipe->cSrcVal;
175 } else {
176 pipe->cSrc = cSrc;
179 // source alpha
180 pipe->aInput = aInput;
181 if (!state->softMask) {
182 if (usesShape) {
183 pipe->aInput *= 255;
184 } else {
185 pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
188 pipe->usesShape = usesShape;
190 // result alpha
191 if (aInput == 1 && !state->softMask && !usesShape &&
192 !state->inNonIsolatedGroup) {
193 pipe->noTransparency = gTrue;
194 } else {
195 pipe->noTransparency = gFalse;
198 // result color
199 if (pipe->noTransparency) {
200 // the !state->blendFunc case is handled separately in pipeRun
201 pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
202 } else if (!state->blendFunc) {
203 pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
204 } else {
205 pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
208 // non-isolated group correction
209 if (nonIsolatedGroup) {
210 pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
211 } else {
212 pipe->nonIsolatedGroup = 0;
216 inline void Splash::pipeRun(SplashPipe *pipe) {
217 Guchar aSrc, aDest, alpha2, alpha0, aResult;
218 SplashColor cDest, cBlend;
219 Guchar cResult0, cResult1, cResult2, cResult3;
221 //----- source color
223 // static pattern: handled in pipeInit
224 // fixed color: handled in pipeInit
226 // dynamic pattern
227 if (pipe->pattern) {
228 pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
231 if (pipe->noTransparency && !state->blendFunc) {
233 //----- write destination pixel
235 switch (bitmap->mode) {
236 case splashModeMono1:
237 cResult0 = pipe->cSrc[0];
238 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
239 *pipe->destColorPtr |= pipe->destColorMask;
240 } else {
241 *pipe->destColorPtr &= ~pipe->destColorMask;
243 if (!(pipe->destColorMask >>= 1)) {
244 pipe->destColorMask = 0x80;
245 ++pipe->destColorPtr;
247 break;
248 case splashModeMono8:
249 *pipe->destColorPtr++ = pipe->cSrc[0];
250 break;
251 case splashModeRGB8:
252 *pipe->destColorPtr++ = pipe->cSrc[0];
253 *pipe->destColorPtr++ = pipe->cSrc[1];
254 *pipe->destColorPtr++ = pipe->cSrc[2];
255 break;
256 case splashModeBGR8:
257 *pipe->destColorPtr++ = pipe->cSrc[2];
258 *pipe->destColorPtr++ = pipe->cSrc[1];
259 *pipe->destColorPtr++ = pipe->cSrc[0];
260 break;
261 #if SPLASH_CMYK
262 case splashModeCMYK8:
263 *pipe->destColorPtr++ = pipe->cSrc[0];
264 *pipe->destColorPtr++ = pipe->cSrc[1];
265 *pipe->destColorPtr++ = pipe->cSrc[2];
266 *pipe->destColorPtr++ = pipe->cSrc[3];
267 break;
268 #endif
270 if (pipe->destAlphaPtr) {
271 *pipe->destAlphaPtr++ = 255;
274 } else {
276 //----- read destination pixel
278 switch (bitmap->mode) {
279 case splashModeMono1:
280 cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
281 break;
282 case splashModeMono8:
283 cDest[0] = *pipe->destColorPtr;
284 break;
285 case splashModeRGB8:
286 cDest[0] = pipe->destColorPtr[0];
287 cDest[1] = pipe->destColorPtr[1];
288 cDest[2] = pipe->destColorPtr[2];
289 break;
290 case splashModeBGR8:
291 cDest[0] = pipe->destColorPtr[2];
292 cDest[1] = pipe->destColorPtr[1];
293 cDest[2] = pipe->destColorPtr[0];
294 break;
295 #if SPLASH_CMYK
296 case splashModeCMYK8:
297 cDest[0] = pipe->destColorPtr[0];
298 cDest[1] = pipe->destColorPtr[1];
299 cDest[2] = pipe->destColorPtr[2];
300 cDest[3] = pipe->destColorPtr[3];
301 break;
302 #endif
304 if (pipe->destAlphaPtr) {
305 aDest = *pipe->destAlphaPtr;
306 } else {
307 aDest = 0xff;
310 //----- blend function
312 if (state->blendFunc) {
313 (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
316 //----- source alpha
318 if (state->softMask) {
319 if (pipe->usesShape) {
320 aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
321 * pipe->shape);
322 } else {
323 aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
325 } else if (pipe->usesShape) {
326 // pipe->aInput is premultiplied by 255 in pipeInit
327 aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
328 } else {
329 // precomputed in pipeInit
330 aSrc = pipe->aSrc;
333 //----- result alpha and non-isolated group element correction
335 if (pipe->noTransparency) {
336 alpha2 = aResult = 255;
337 } else {
338 aResult = aSrc + aDest - div255(aSrc * aDest);
340 if (pipe->alpha0Ptr) {
341 alpha0 = *pipe->alpha0Ptr++;
342 alpha2 = aResult + alpha0 - div255(aResult * alpha0);
343 } else {
344 alpha2 = aResult;
348 //----- result color
350 cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
352 switch (pipe->resultColorCtrl) {
354 #if SPLASH_CMYK
355 case splashPipeResultColorNoAlphaBlendCMYK:
356 cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
357 #endif
358 case splashPipeResultColorNoAlphaBlendRGB:
359 cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
360 cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
361 case splashPipeResultColorNoAlphaBlendMono:
362 cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
363 break;
365 case splashPipeResultColorAlphaNoBlendMono:
366 if (alpha2 == 0) {
367 cResult0 = 0;
368 } else {
369 cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
370 aSrc * pipe->cSrc[0]) / alpha2);
372 break;
373 case splashPipeResultColorAlphaNoBlendRGB:
374 if (alpha2 == 0) {
375 cResult0 = 0;
376 cResult1 = 0;
377 cResult2 = 0;
378 } else {
379 cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
380 aSrc * pipe->cSrc[0]) / alpha2);
381 cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
382 aSrc * pipe->cSrc[1]) / alpha2);
383 cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
384 aSrc * pipe->cSrc[2]) / alpha2);
386 break;
387 #if SPLASH_CMYK
388 case splashPipeResultColorAlphaNoBlendCMYK:
389 if (alpha2 == 0) {
390 cResult0 = 0;
391 cResult1 = 0;
392 cResult2 = 0;
393 cResult3 = 0;
394 } else {
395 cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
396 aSrc * pipe->cSrc[0]) / alpha2);
397 cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
398 aSrc * pipe->cSrc[1]) / alpha2);
399 cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
400 aSrc * pipe->cSrc[2]) / alpha2);
401 cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
402 aSrc * pipe->cSrc[3]) / alpha2);
404 break;
405 #endif
407 case splashPipeResultColorAlphaBlendMono:
408 if (alpha2 == 0) {
409 cResult0 = 0;
410 } else {
411 cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
412 aSrc * ((255 - aDest) * pipe->cSrc[0] +
413 aDest * cBlend[0]) / 255) /
414 alpha2);
416 break;
417 case splashPipeResultColorAlphaBlendRGB:
418 if (alpha2 == 0) {
419 cResult0 = 0;
420 cResult1 = 0;
421 cResult2 = 0;
422 } else {
423 cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
424 aSrc * ((255 - aDest) * pipe->cSrc[0] +
425 aDest * cBlend[0]) / 255) /
426 alpha2);
427 cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
428 aSrc * ((255 - aDest) * pipe->cSrc[1] +
429 aDest * cBlend[1]) / 255) /
430 alpha2);
431 cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
432 aSrc * ((255 - aDest) * pipe->cSrc[2] +
433 aDest * cBlend[2]) / 255) /
434 alpha2);
436 break;
437 #if SPLASH_CMYK
438 case splashPipeResultColorAlphaBlendCMYK:
439 if (alpha2 == 0) {
440 cResult0 = 0;
441 cResult1 = 0;
442 cResult2 = 0;
443 cResult3 = 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 cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
450 aSrc * ((255 - aDest) * pipe->cSrc[1] +
451 aDest * cBlend[1]) / 255) /
452 alpha2);
453 cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
454 aSrc * ((255 - aDest) * pipe->cSrc[2] +
455 aDest * cBlend[2]) / 255) /
456 alpha2);
457 cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
458 aSrc * ((255 - aDest) * pipe->cSrc[3] +
459 aDest * cBlend[3]) / 255) /
460 alpha2);
462 break;
463 #endif
466 //----- non-isolated group correction
468 if (aResult != 0) {
469 switch (pipe->nonIsolatedGroup) {
470 #if SPLASH_CMYK
471 case 4:
472 cResult3 += (cResult3 - cDest[3]) * aDest *
473 (255 - aResult) / (255 * aResult);
474 #endif
475 case 3:
476 cResult2 += (cResult2 - cDest[2]) * aDest *
477 (255 - aResult) / (255 * aResult);
478 cResult1 += (cResult1 - cDest[1]) * aDest *
479 (255 - aResult) / (255 * aResult);
480 case 1:
481 cResult0 += (cResult0 - cDest[0]) * aDest *
482 (255 - aResult) / (255 * aResult);
483 case 0:
484 break;
488 //----- write destination pixel
490 switch (bitmap->mode) {
491 case splashModeMono1:
492 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
493 *pipe->destColorPtr |= pipe->destColorMask;
494 } else {
495 *pipe->destColorPtr &= ~pipe->destColorMask;
497 if (!(pipe->destColorMask >>= 1)) {
498 pipe->destColorMask = 0x80;
499 ++pipe->destColorPtr;
501 break;
502 case splashModeMono8:
503 *pipe->destColorPtr++ = cResult0;
504 break;
505 case splashModeRGB8:
506 *pipe->destColorPtr++ = cResult0;
507 *pipe->destColorPtr++ = cResult1;
508 *pipe->destColorPtr++ = cResult2;
509 break;
510 case splashModeBGR8:
511 *pipe->destColorPtr++ = cResult2;
512 *pipe->destColorPtr++ = cResult1;
513 *pipe->destColorPtr++ = cResult0;
514 break;
515 #if SPLASH_CMYK
516 case splashModeCMYK8:
517 *pipe->destColorPtr++ = cResult0;
518 *pipe->destColorPtr++ = cResult1;
519 *pipe->destColorPtr++ = cResult2;
520 *pipe->destColorPtr++ = cResult3;
521 break;
522 #endif
524 if (pipe->destAlphaPtr) {
525 *pipe->destAlphaPtr++ = aResult;
530 ++pipe->x;
533 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
534 pipe->x = x;
535 pipe->y = y;
536 if (state->softMask) {
537 pipe->softMaskPtr =
538 &state->softMask->data[y * state->softMask->rowSize + x];
540 switch (bitmap->mode) {
541 case splashModeMono1:
542 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
543 pipe->destColorMask = 0x80 >> (x & 7);
544 break;
545 case splashModeMono8:
546 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
547 break;
548 case splashModeRGB8:
549 case splashModeBGR8:
550 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
551 break;
552 #if SPLASH_CMYK
553 case splashModeCMYK8:
554 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
555 break;
556 #endif
558 if (bitmap->alpha) {
559 pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
560 } else {
561 pipe->destAlphaPtr = NULL;
563 if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
564 pipe->alpha0Ptr =
565 &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
566 (alpha0X + x)];
567 } else {
568 pipe->alpha0Ptr = NULL;
572 inline void Splash::pipeIncX(SplashPipe *pipe) {
573 ++pipe->x;
574 if (state->softMask) {
575 ++pipe->softMaskPtr;
577 switch (bitmap->mode) {
578 case splashModeMono1:
579 if (!(pipe->destColorMask >>= 1)) {
580 pipe->destColorMask = 0x80;
581 ++pipe->destColorPtr;
583 break;
584 case splashModeMono8:
585 ++pipe->destColorPtr;
586 break;
587 case splashModeRGB8:
588 case splashModeBGR8:
589 pipe->destColorPtr += 3;
590 break;
591 #if SPLASH_CMYK
592 case splashModeCMYK8:
593 pipe->destColorPtr += 4;
594 break;
595 #endif
597 if (pipe->destAlphaPtr) {
598 ++pipe->destAlphaPtr;
600 if (pipe->alpha0Ptr) {
601 ++pipe->alpha0Ptr;
605 inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
606 if (noClip || state->clip->test(x, y)) {
607 pipeSetXY(pipe, x, y);
608 pipeRun(pipe);
609 updateModX(x);
610 updateModY(y);
614 inline void Splash::drawAAPixelInit() {
615 aaBufY = -1;
618 inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
619 #if splashAASize == 4
620 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
621 1, 2, 2, 3, 2, 3, 3, 4 };
622 int w;
623 #else
624 int xx, yy;
625 #endif
626 SplashColorPtr p;
627 int x0, x1, t;
629 if (x < 0 || x >= bitmap->width ||
630 y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
631 return;
634 // update aaBuf
635 if (y != aaBufY) {
636 memset(aaBuf->getDataPtr(), 0xff,
637 aaBuf->getRowSize() * aaBuf->getHeight());
638 x0 = 0;
639 x1 = bitmap->width - 1;
640 state->clip->clipAALine(aaBuf, &x0, &x1, y);
641 aaBufY = y;
644 // compute the shape value
645 #if splashAASize == 4
646 p = aaBuf->getDataPtr() + (x >> 1);
647 w = aaBuf->getRowSize();
648 if (x & 1) {
649 t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
650 bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
651 } else {
652 t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
653 bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
655 #else
656 t = 0;
657 for (yy = 0; yy < splashAASize; ++yy) {
658 for (xx = 0; xx < splashAASize; ++xx) {
659 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
660 ((x * splashAASize + xx) >> 3);
661 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
664 #endif
666 // draw the pixel
667 if (t != 0) {
668 pipeSetXY(pipe, x, y);
669 pipe->shape *= aaGamma[t];
670 pipeRun(pipe);
671 updateModX(x);
672 updateModY(y);
676 inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
677 GBool noClip) {
678 int x;
680 pipeSetXY(pipe, x0, y);
681 if (noClip) {
682 for (x = x0; x <= x1; ++x) {
683 pipeRun(pipe);
685 updateModX(x0);
686 updateModX(x1);
687 updateModY(y);
688 } else {
689 for (x = x0; x <= x1; ++x) {
690 if (state->clip->test(x, y)) {
691 pipeRun(pipe);
692 updateModX(x);
693 updateModY(y);
694 } else {
695 pipeIncX(pipe);
701 inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
702 #if splashAASize == 4
703 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
704 1, 2, 2, 3, 2, 3, 3, 4 };
705 SplashColorPtr p0, p1, p2, p3;
706 int t;
707 #else
708 SplashColorPtr p;
709 int xx, yy, t;
710 #endif
711 int x;
713 #if splashAASize == 4
714 p0 = aaBuf->getDataPtr() + (x0 >> 1);
715 p1 = p0 + aaBuf->getRowSize();
716 p2 = p1 + aaBuf->getRowSize();
717 p3 = p2 + aaBuf->getRowSize();
718 #endif
719 pipeSetXY(pipe, x0, y);
720 for (x = x0; x <= x1; ++x) {
722 // compute the shape value
723 #if splashAASize == 4
724 if (x & 1) {
725 t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
726 bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
727 ++p0; ++p1; ++p2; ++p3;
728 } else {
729 t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
730 bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
732 #else
733 t = 0;
734 for (yy = 0; yy < splashAASize; ++yy) {
735 for (xx = 0; xx < splashAASize; ++xx) {
736 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
737 ((x * splashAASize + xx) >> 3);
738 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
741 #endif
743 if (t != 0) {
744 pipe->shape = aaGamma[t];
745 pipeRun(pipe);
746 updateModX(x);
747 updateModY(y);
748 } else {
749 pipeIncX(pipe);
754 //------------------------------------------------------------------------
756 // Transform a point from user space to device space.
757 inline void Splash::transform(SplashCoord *matrix,
758 SplashCoord xi, SplashCoord yi,
759 SplashCoord *xo, SplashCoord *yo) {
760 // [ m[0] m[1] 0 ]
761 // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
762 // [ m[4] m[5] 1 ]
763 *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
764 *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
767 //------------------------------------------------------------------------
768 // Splash
769 //------------------------------------------------------------------------
771 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
772 SplashScreenParams *screenParams) {
773 int i;
775 bitmap = bitmapA;
776 vectorAntialias = vectorAntialiasA;
777 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
778 screenParams);
779 if (vectorAntialias) {
780 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
781 1, splashModeMono1, gFalse);
782 for (i = 0; i <= splashAASize * splashAASize; ++i) {
783 aaGamma[i] = splashPow((SplashCoord)i /
784 (SplashCoord)(splashAASize * splashAASize),
785 1.5);
787 } else {
788 aaBuf = NULL;
790 clearModRegion();
791 debugMode = gFalse;
794 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
795 SplashScreen *screenA) {
796 int i;
798 bitmap = bitmapA;
799 vectorAntialias = vectorAntialiasA;
800 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
801 screenA);
802 if (vectorAntialias) {
803 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
804 1, splashModeMono1, gFalse);
805 for (i = 0; i <= splashAASize * splashAASize; ++i) {
806 aaGamma[i] = splashPow((SplashCoord)i /
807 (SplashCoord)(splashAASize * splashAASize),
808 1.5);
810 } else {
811 aaBuf = NULL;
813 clearModRegion();
814 debugMode = gFalse;
817 Splash::~Splash() {
818 while (state->next) {
819 restoreState();
821 delete state;
822 if (vectorAntialias) {
823 delete aaBuf;
827 //------------------------------------------------------------------------
828 // state read
829 //------------------------------------------------------------------------
831 SplashCoord *Splash::getMatrix() {
832 return state->matrix;
835 SplashPattern *Splash::getStrokePattern() {
836 return state->strokePattern;
839 SplashPattern *Splash::getFillPattern() {
840 return state->fillPattern;
843 SplashScreen *Splash::getScreen() {
844 return state->screen;
847 SplashBlendFunc Splash::getBlendFunc() {
848 return state->blendFunc;
851 SplashCoord Splash::getStrokeAlpha() {
852 return state->strokeAlpha;
855 SplashCoord Splash::getFillAlpha() {
856 return state->fillAlpha;
859 SplashCoord Splash::getLineWidth() {
860 return state->lineWidth;
863 int Splash::getLineCap() {
864 return state->lineCap;
867 int Splash::getLineJoin() {
868 return state->lineJoin;
871 SplashCoord Splash::getMiterLimit() {
872 return state->miterLimit;
875 SplashCoord Splash::getFlatness() {
876 return state->flatness;
879 SplashCoord *Splash::getLineDash() {
880 return state->lineDash;
883 int Splash::getLineDashLength() {
884 return state->lineDashLength;
887 SplashCoord Splash::getLineDashPhase() {
888 return state->lineDashPhase;
891 SplashClip *Splash::getClip() {
892 return state->clip;
895 SplashBitmap *Splash::getSoftMask() {
896 return state->softMask;
899 GBool Splash::getInNonIsolatedGroup() {
900 return state->inNonIsolatedGroup;
903 //------------------------------------------------------------------------
904 // state write
905 //------------------------------------------------------------------------
907 void Splash::setMatrix(SplashCoord *matrix) {
908 memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
911 void Splash::setStrokePattern(SplashPattern *strokePattern) {
912 state->setStrokePattern(strokePattern);
915 void Splash::setFillPattern(SplashPattern *fillPattern) {
916 state->setFillPattern(fillPattern);
919 void Splash::setScreen(SplashScreen *screen) {
920 state->setScreen(screen);
923 void Splash::setBlendFunc(SplashBlendFunc func) {
924 state->blendFunc = func;
927 void Splash::setStrokeAlpha(SplashCoord alpha) {
928 state->strokeAlpha = alpha;
931 void Splash::setFillAlpha(SplashCoord alpha) {
932 state->fillAlpha = alpha;
935 void Splash::setLineWidth(SplashCoord lineWidth) {
936 state->lineWidth = lineWidth;
939 void Splash::setLineCap(int lineCap) {
940 state->lineCap = lineCap;
943 void Splash::setLineJoin(int lineJoin) {
944 state->lineJoin = lineJoin;
947 void Splash::setMiterLimit(SplashCoord miterLimit) {
948 state->miterLimit = miterLimit;
951 void Splash::setFlatness(SplashCoord flatness) {
952 if (flatness < 1) {
953 state->flatness = 1;
954 } else {
955 state->flatness = flatness;
959 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
960 SplashCoord lineDashPhase) {
961 state->setLineDash(lineDash, lineDashLength, lineDashPhase);
964 void Splash::setStrokeAdjust(GBool strokeAdjust) {
965 state->strokeAdjust = strokeAdjust;
968 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
969 SplashCoord x1, SplashCoord y1) {
970 state->clip->resetToRect(x0, y0, x1, y1);
973 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
974 SplashCoord x1, SplashCoord y1) {
975 return state->clip->clipToRect(x0, y0, x1, y1);
978 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
979 return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
982 void Splash::setSoftMask(SplashBitmap *softMask) {
983 state->setSoftMask(softMask);
986 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
987 int alpha0XA, int alpha0YA) {
988 alpha0Bitmap = alpha0BitmapA;
989 alpha0X = alpha0XA;
990 alpha0Y = alpha0YA;
991 state->inNonIsolatedGroup = gTrue;
994 //------------------------------------------------------------------------
995 // state save/restore
996 //------------------------------------------------------------------------
998 void Splash::saveState() {
999 SplashState *newState;
1001 newState = state->copy();
1002 newState->next = state;
1003 state = newState;
1006 SplashError Splash::restoreState() {
1007 SplashState *oldState;
1009 if (!state->next) {
1010 return splashErrNoSave;
1012 oldState = state;
1013 state = state->next;
1014 delete oldState;
1015 return splashOk;
1018 //------------------------------------------------------------------------
1019 // drawing operations
1020 //------------------------------------------------------------------------
1022 void Splash::clear(SplashColorPtr color, Guchar alpha) {
1023 SplashColorPtr row, p;
1024 Guchar mono;
1025 int x, y;
1027 switch (bitmap->mode) {
1028 case splashModeMono1:
1029 mono = (color[0] & 0x80) ? 0xff : 0x00;
1030 if (bitmap->rowSize < 0) {
1031 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1032 mono, -bitmap->rowSize * bitmap->height);
1033 } else {
1034 memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1036 break;
1037 case splashModeMono8:
1038 if (bitmap->rowSize < 0) {
1039 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1040 color[0], -bitmap->rowSize * bitmap->height);
1041 } else {
1042 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1044 break;
1045 case splashModeRGB8:
1046 if (color[0] == color[1] && color[1] == color[2]) {
1047 if (bitmap->rowSize < 0) {
1048 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1049 color[0], -bitmap->rowSize * bitmap->height);
1050 } else {
1051 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1053 } else {
1054 row = bitmap->data;
1055 for (y = 0; y < bitmap->height; ++y) {
1056 p = row;
1057 for (x = 0; x < bitmap->width; ++x) {
1058 *p++ = color[2];
1059 *p++ = color[1];
1060 *p++ = color[0];
1062 row += bitmap->rowSize;
1065 break;
1066 case splashModeBGR8:
1067 if (color[0] == color[1] && color[1] == color[2]) {
1068 if (bitmap->rowSize < 0) {
1069 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1070 color[0], -bitmap->rowSize * bitmap->height);
1071 } else {
1072 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1074 } else {
1075 row = bitmap->data;
1076 for (y = 0; y < bitmap->height; ++y) {
1077 p = row;
1078 for (x = 0; x < bitmap->width; ++x) {
1079 *p++ = color[0];
1080 *p++ = color[1];
1081 *p++ = color[2];
1083 row += bitmap->rowSize;
1086 break;
1087 #if SPLASH_CMYK
1088 case splashModeCMYK8:
1089 if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1090 if (bitmap->rowSize < 0) {
1091 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1092 color[0], -bitmap->rowSize * bitmap->height);
1093 } else {
1094 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1096 } else {
1097 row = bitmap->data;
1098 for (y = 0; y < bitmap->height; ++y) {
1099 p = row;
1100 for (x = 0; x < bitmap->width; ++x) {
1101 *p++ = color[0];
1102 *p++ = color[1];
1103 *p++ = color[2];
1104 *p++ = color[3];
1106 row += bitmap->rowSize;
1109 break;
1110 #endif
1113 if (bitmap->alpha) {
1114 memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
1117 updateModX(0);
1118 updateModY(0);
1119 updateModX(bitmap->width - 1);
1120 updateModY(bitmap->height - 1);
1123 SplashError Splash::stroke(SplashPath *path) {
1124 SplashPath *path2, *dPath;
1126 if (debugMode) {
1127 printf("stroke [dash:%d] [width:%.2f]:\n",
1128 state->lineDashLength, (double)state->lineWidth);
1129 dumpPath(path);
1131 opClipRes = splashClipAllOutside;
1132 if (path->length == 0) {
1133 return splashErrEmptyPath;
1135 path2 = flattenPath(path, state->matrix, state->flatness);
1136 if (state->lineDashLength > 0) {
1137 dPath = makeDashedPath(path2);
1138 delete path2;
1139 path2 = dPath;
1141 if (state->lineWidth == 0) {
1142 strokeNarrow(path2);
1143 } else {
1144 strokeWide(path2);
1146 delete path2;
1147 return splashOk;
1150 void Splash::strokeNarrow(SplashPath *path) {
1151 SplashPipe pipe;
1152 SplashXPath *xPath;
1153 SplashXPathSeg *seg;
1154 int x0, x1, x2, x3, y0, y1, x, y, t;
1155 SplashCoord dx, dy, dxdy;
1156 SplashClipResult clipRes;
1157 int nClipRes[3];
1158 int i;
1160 nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
1162 xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
1164 pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
1165 gFalse, gFalse);
1167 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
1169 x0 = splashFloor(seg->x0);
1170 x1 = splashFloor(seg->x1);
1171 y0 = splashFloor(seg->y0);
1172 y1 = splashFloor(seg->y1);
1174 // horizontal segment
1175 if (y0 == y1) {
1176 if (x0 > x1) {
1177 t = x0; x0 = x1; x1 = t;
1179 if ((clipRes = state->clip->testSpan(x0, x1, y0))
1180 != splashClipAllOutside) {
1181 drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
1184 // segment with |dx| > |dy|
1185 } else if (splashAbs(seg->dxdy) > 1) {
1186 dx = seg->x1 - seg->x0;
1187 dy = seg->y1 - seg->y0;
1188 dxdy = seg->dxdy;
1189 if (y0 > y1) {
1190 t = y0; y0 = y1; y1 = t;
1191 t = x0; x0 = x1; x1 = t;
1192 dx = -dx;
1193 dy = -dy;
1195 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1196 x0 <= x1 ? x1 : x0, y1))
1197 != splashClipAllOutside) {
1198 if (dx > 0) {
1199 x2 = x0;
1200 x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1201 drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
1202 clipRes == splashClipAllInside);
1203 x2 = x3;
1204 for (y = y0 + 1; y <= y1 - 1; ++y) {
1205 x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1206 drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
1207 x2 = x3;
1209 drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
1210 clipRes == splashClipAllInside);
1211 } else {
1212 x2 = x0;
1213 x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1214 drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
1215 clipRes == splashClipAllInside);
1216 x2 = x3;
1217 for (y = y0 + 1; y <= y1 - 1; ++y) {
1218 x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1219 drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
1220 x2 = x3;
1222 drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
1223 clipRes == splashClipAllInside);
1227 // segment with |dy| > |dx|
1228 } else {
1229 dxdy = seg->dxdy;
1230 if (y0 > y1) {
1231 t = x0; x0 = x1; x1 = t;
1232 t = y0; y0 = y1; y1 = t;
1234 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1235 x0 <= x1 ? x1 : x0, y1))
1236 != splashClipAllOutside) {
1237 drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
1238 for (y = y0 + 1; y <= y1 - 1; ++y) {
1239 x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
1240 drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
1242 drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
1245 ++nClipRes[clipRes];
1247 if (nClipRes[splashClipPartial] ||
1248 (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
1249 opClipRes = splashClipPartial;
1250 } else if (nClipRes[splashClipAllInside]) {
1251 opClipRes = splashClipAllInside;
1252 } else {
1253 opClipRes = splashClipAllOutside;
1256 delete xPath;
1259 void Splash::strokeWide(SplashPath *path) {
1260 SplashPath *path2;
1262 path2 = makeStrokePath(path, gFalse);
1263 fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
1264 delete path2;
1267 SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
1268 SplashCoord flatness) {
1269 SplashPath *fPath;
1270 SplashCoord flatness2;
1271 Guchar flag;
1272 int i;
1274 fPath = new SplashPath();
1275 flatness2 = flatness * flatness;
1276 i = 0;
1277 while (i < path->length) {
1278 flag = path->flags[i];
1279 if (flag & splashPathFirst) {
1280 fPath->moveTo(path->pts[i].x, path->pts[i].y);
1281 ++i;
1282 } else {
1283 if (flag & splashPathCurve) {
1284 flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
1285 path->pts[i ].x, path->pts[i ].y,
1286 path->pts[i+1].x, path->pts[i+1].y,
1287 path->pts[i+2].x, path->pts[i+2].y,
1288 matrix, flatness2, fPath);
1289 i += 3;
1290 } else {
1291 fPath->lineTo(path->pts[i].x, path->pts[i].y);
1292 ++i;
1294 if (path->flags[i-1] & splashPathClosed) {
1295 fPath->close();
1299 return fPath;
1302 void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
1303 SplashCoord x1, SplashCoord y1,
1304 SplashCoord x2, SplashCoord y2,
1305 SplashCoord x3, SplashCoord y3,
1306 SplashCoord *matrix, SplashCoord flatness2,
1307 SplashPath *fPath) {
1308 SplashCoord cx[splashMaxCurveSplits + 1][3];
1309 SplashCoord cy[splashMaxCurveSplits + 1][3];
1310 int cNext[splashMaxCurveSplits + 1];
1311 SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
1312 SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
1313 SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
1314 int p1, p2, p3;
1316 // initial segment
1317 p1 = 0;
1318 p2 = splashMaxCurveSplits;
1319 cx[p1][0] = x0; cy[p1][0] = y0;
1320 cx[p1][1] = x1; cy[p1][1] = y1;
1321 cx[p1][2] = x2; cy[p1][2] = y2;
1322 cx[p2][0] = x3; cy[p2][0] = y3;
1323 cNext[p1] = p2;
1325 while (p1 < splashMaxCurveSplits) {
1327 // get the next segment
1328 xl0 = cx[p1][0]; yl0 = cy[p1][0];
1329 xx1 = cx[p1][1]; yy1 = cy[p1][1];
1330 xx2 = cx[p1][2]; yy2 = cy[p1][2];
1331 p2 = cNext[p1];
1332 xr3 = cx[p2][0]; yr3 = cy[p2][0];
1334 // compute the distances (in device space) from the control points
1335 // to the midpoint of the straight line (this is a bit of a hack,
1336 // but it's much faster than computing the actual distances to the
1337 // line)
1338 transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
1339 transform(matrix, xx1, yy1, &tx, &ty);
1340 dx = tx - mx;
1341 dy = ty - my;
1342 d1 = dx*dx + dy*dy;
1343 transform(matrix, xx2, yy2, &tx, &ty);
1344 dx = tx - mx;
1345 dy = ty - my;
1346 d2 = dx*dx + dy*dy;
1348 // if the curve is flat enough, or no more subdivisions are
1349 // allowed, add the straight line segment
1350 if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
1351 fPath->lineTo(xr3, yr3);
1352 p1 = p2;
1354 // otherwise, subdivide the curve
1355 } else {
1356 xl1 = (xl0 + xx1) * 0.5;
1357 yl1 = (yl0 + yy1) * 0.5;
1358 xh = (xx1 + xx2) * 0.5;
1359 yh = (yy1 + yy2) * 0.5;
1360 xl2 = (xl1 + xh) * 0.5;
1361 yl2 = (yl1 + yh) * 0.5;
1362 xr2 = (xx2 + xr3) * 0.5;
1363 yr2 = (yy2 + yr3) * 0.5;
1364 xr1 = (xh + xr2) * 0.5;
1365 yr1 = (yh + yr2) * 0.5;
1366 xr0 = (xl2 + xr1) * 0.5;
1367 yr0 = (yl2 + yr1) * 0.5;
1368 // add the new subdivision points
1369 p3 = (p1 + p2) / 2;
1370 cx[p1][1] = xl1; cy[p1][1] = yl1;
1371 cx[p1][2] = xl2; cy[p1][2] = yl2;
1372 cNext[p1] = p3;
1373 cx[p3][0] = xr0; cy[p3][0] = yr0;
1374 cx[p3][1] = xr1; cy[p3][1] = yr1;
1375 cx[p3][2] = xr2; cy[p3][2] = yr2;
1376 cNext[p3] = p2;
1381 SplashPath *Splash::makeDashedPath(SplashPath *path) {
1382 SplashPath *dPath;
1383 SplashCoord lineDashTotal;
1384 SplashCoord lineDashStartPhase, lineDashDist, segLen;
1385 SplashCoord x0, y0, x1, y1, xa, ya;
1386 GBool lineDashStartOn, lineDashOn, newPath;
1387 int lineDashStartIdx, lineDashIdx;
1388 int i, j, k;
1390 lineDashTotal = 0;
1391 for (i = 0; i < state->lineDashLength; ++i) {
1392 lineDashTotal += state->lineDash[i];
1394 lineDashStartPhase = state->lineDashPhase;
1395 i = splashFloor(lineDashStartPhase / lineDashTotal);
1396 lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
1397 lineDashStartOn = gTrue;
1398 lineDashStartIdx = 0;
1399 while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
1400 lineDashStartOn = !lineDashStartOn;
1401 lineDashStartPhase -= state->lineDash[lineDashStartIdx];
1402 ++lineDashStartIdx;
1405 dPath = new SplashPath();
1407 // process each subpath
1408 i = 0;
1409 while (i < path->length) {
1411 // find the end of the subpath
1412 for (j = i;
1413 j < path->length - 1 && !(path->flags[j] & splashPathLast);
1414 ++j) ;
1416 // initialize the dash parameters
1417 lineDashOn = lineDashStartOn;
1418 lineDashIdx = lineDashStartIdx;
1419 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
1421 // process each segment of the subpath
1422 newPath = gTrue;
1423 for (k = i; k < j; ++k) {
1425 // grab the segment
1426 x0 = path->pts[k].x;
1427 y0 = path->pts[k].y;
1428 x1 = path->pts[k+1].x;
1429 y1 = path->pts[k+1].y;
1430 segLen = splashDist(x0, y0, x1, y1);
1432 // process the segment
1433 while (segLen > 0) {
1435 if (lineDashDist >= segLen) {
1436 if (lineDashOn) {
1437 if (newPath) {
1438 dPath->moveTo(x0, y0);
1439 newPath = gFalse;
1441 dPath->lineTo(x1, y1);
1443 lineDashDist -= segLen;
1444 segLen = 0;
1446 } else {
1447 xa = x0 + (lineDashDist / segLen) * (x1 - x0);
1448 ya = y0 + (lineDashDist / segLen) * (y1 - y0);
1449 if (lineDashOn) {
1450 if (newPath) {
1451 dPath->moveTo(x0, y0);
1452 newPath = gFalse;
1454 dPath->lineTo(xa, ya);
1456 x0 = xa;
1457 y0 = ya;
1458 segLen -= lineDashDist;
1459 lineDashDist = 0;
1462 // get the next entry in the dash array
1463 if (lineDashDist <= 0) {
1464 lineDashOn = !lineDashOn;
1465 if (++lineDashIdx == state->lineDashLength) {
1466 lineDashIdx = 0;
1468 lineDashDist = state->lineDash[lineDashIdx];
1469 newPath = gTrue;
1473 i = j + 1;
1476 return dPath;
1479 SplashError Splash::fill(SplashPath *path, GBool eo) {
1480 if (debugMode) {
1481 printf("fill [eo:%d]:\n", eo);
1482 dumpPath(path);
1484 return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
1487 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
1488 SplashPattern *pattern,
1489 SplashCoord alpha) {
1490 SplashPipe pipe;
1491 SplashXPath *xPath;
1492 SplashXPathScanner *scanner;
1493 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1494 SplashClipResult clipRes, clipRes2;
1496 if (path->length == 0) {
1497 return splashErrEmptyPath;
1499 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1500 if (vectorAntialias) {
1501 xPath->aaScale();
1503 xPath->sort();
1504 scanner = new SplashXPathScanner(xPath, eo);
1506 // get the min and max x and y values
1507 if (vectorAntialias) {
1508 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
1509 } else {
1510 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1513 // check clipping
1514 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1515 != splashClipAllOutside) {
1517 // limit the y range
1518 if (yMinI < state->clip->getYMinI()) {
1519 yMinI = state->clip->getYMinI();
1521 if (yMaxI > state->clip->getYMaxI()) {
1522 yMaxI = state->clip->getYMaxI();
1525 pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);
1527 // draw the spans
1528 if (vectorAntialias) {
1529 for (y = yMinI; y <= yMaxI; ++y) {
1530 scanner->renderAALine(aaBuf, &x0, &x1, y);
1531 if (clipRes != splashClipAllInside) {
1532 state->clip->clipAALine(aaBuf, &x0, &x1, y);
1534 drawAALine(&pipe, x0, x1, y);
1536 } else {
1537 for (y = yMinI; y <= yMaxI; ++y) {
1538 while (scanner->getNextSpan(y, &x0, &x1)) {
1539 if (clipRes == splashClipAllInside) {
1540 drawSpan(&pipe, x0, x1, y, gTrue);
1541 } else {
1542 // limit the x range
1543 if (x0 < state->clip->getXMinI()) {
1544 x0 = state->clip->getXMinI();
1546 if (x1 > state->clip->getXMaxI()) {
1547 x1 = state->clip->getXMaxI();
1549 clipRes2 = state->clip->testSpan(x0, x1, y);
1550 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1556 opClipRes = clipRes;
1558 delete scanner;
1559 delete xPath;
1560 return splashOk;
1563 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
1564 SplashPipe pipe;
1565 SplashXPath *xPath;
1566 SplashXPathScanner *scanner;
1567 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1568 SplashClipResult clipRes, clipRes2;
1569 SplashBlendFunc origBlendFunc;
1571 if (path->length == 0) {
1572 return splashErrEmptyPath;
1574 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1575 xPath->sort();
1576 scanner = new SplashXPathScanner(xPath, eo);
1578 // get the min and max x and y values
1579 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1581 // check clipping
1582 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1583 != splashClipAllOutside) {
1585 // limit the y range
1586 if (yMinI < state->clip->getYMinI()) {
1587 yMinI = state->clip->getYMinI();
1589 if (yMaxI > state->clip->getYMaxI()) {
1590 yMaxI = state->clip->getYMaxI();
1593 origBlendFunc = state->blendFunc;
1594 state->blendFunc = &blendXor;
1595 pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);
1597 // draw the spans
1598 for (y = yMinI; y <= yMaxI; ++y) {
1599 while (scanner->getNextSpan(y, &x0, &x1)) {
1600 if (clipRes == splashClipAllInside) {
1601 drawSpan(&pipe, x0, x1, y, gTrue);
1602 } else {
1603 // limit the x range
1604 if (x0 < state->clip->getXMinI()) {
1605 x0 = state->clip->getXMinI();
1607 if (x1 > state->clip->getXMaxI()) {
1608 x1 = state->clip->getXMaxI();
1610 clipRes2 = state->clip->testSpan(x0, x1, y);
1611 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1615 state->blendFunc = origBlendFunc;
1617 opClipRes = clipRes;
1619 delete scanner;
1620 delete xPath;
1621 return splashOk;
1624 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
1625 int c, SplashFont *font) {
1626 SplashGlyphBitmap glyph;
1627 SplashCoord xt, yt;
1628 int x0, y0, xFrac, yFrac;
1629 SplashError err;
1631 if (debugMode) {
1632 printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
1633 (double)x, (double)y, c, c, c);
1635 transform(state->matrix, x, y, &xt, &yt);
1636 x0 = splashFloor(xt);
1637 xFrac = splashFloor((xt - x0) * splashFontFraction);
1638 y0 = splashFloor(yt);
1639 yFrac = splashFloor((yt - y0) * splashFontFraction);
1640 if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
1641 return splashErrNoGlyph;
1643 err = fillGlyph2(x0, y0, &glyph);
1644 if (glyph.freeData) {
1645 gfree(glyph.data);
1647 return err;
1650 SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
1651 SplashGlyphBitmap *glyph) {
1652 SplashCoord xt, yt;
1653 int x0, y0;
1655 transform(state->matrix, x, y, &xt, &yt);
1656 x0 = splashFloor(xt);
1657 y0 = splashFloor(yt);
1658 return fillGlyph2(x0, y0, glyph);
1661 SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
1662 SplashPipe pipe;
1663 SplashClipResult clipRes;
1664 GBool noClip;
1665 int alpha0, alpha;
1666 Guchar *p;
1667 int x1, y1, xx, xx1, yy;
1669 if ((clipRes = state->clip->testRect(x0 - glyph->x,
1670 y0 - glyph->y,
1671 x0 - glyph->x + glyph->w - 1,
1672 y0 - glyph->y + glyph->h - 1))
1673 != splashClipAllOutside) {
1674 noClip = clipRes == splashClipAllInside;
1676 if (noClip) {
1677 if (glyph->aa) {
1678 pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1679 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1680 p = glyph->data;
1681 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1682 pipeSetXY(&pipe, x0 - glyph->x, y1);
1683 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1684 alpha = *p++;
1685 if (alpha != 0) {
1686 pipe.shape = (SplashCoord)(alpha / 255.0);
1687 pipeRun(&pipe);
1688 updateModX(x1);
1689 updateModY(y1);
1690 } else {
1691 pipeIncX(&pipe);
1695 } else {
1696 pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1697 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1698 p = glyph->data;
1699 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1700 pipeSetXY(&pipe, x0 - glyph->x, y1);
1701 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1702 alpha0 = *p++;
1703 for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1704 if (alpha0 & 0x80) {
1705 pipeRun(&pipe);
1706 updateModX(x1);
1707 updateModY(y1);
1708 } else {
1709 pipeIncX(&pipe);
1711 alpha0 <<= 1;
1716 } else {
1717 if (glyph->aa) {
1718 pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1719 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1720 p = glyph->data;
1721 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1722 pipeSetXY(&pipe, x0 - glyph->x, y1);
1723 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1724 if (state->clip->test(x1, y1)) {
1725 alpha = *p++;
1726 if (alpha != 0) {
1727 pipe.shape = (SplashCoord)(alpha / 255.0);
1728 pipeRun(&pipe);
1729 updateModX(x1);
1730 updateModY(y1);
1731 } else {
1732 pipeIncX(&pipe);
1734 } else {
1735 pipeIncX(&pipe);
1736 ++p;
1740 } else {
1741 pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1742 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1743 p = glyph->data;
1744 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1745 pipeSetXY(&pipe, x0 - glyph->x, y1);
1746 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1747 alpha0 = *p++;
1748 for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1749 if (state->clip->test(x1, y1)) {
1750 if (alpha0 & 0x80) {
1751 pipeRun(&pipe);
1752 updateModX(x1);
1753 updateModY(y1);
1754 } else {
1755 pipeIncX(&pipe);
1757 } else {
1758 pipeIncX(&pipe);
1760 alpha0 <<= 1;
1767 opClipRes = clipRes;
1769 return splashOk;
1772 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
1773 int w, int h, SplashCoord *mat,
1774 GBool glyphMode) {
1775 SplashPipe pipe;
1776 GBool rot;
1777 SplashCoord xScale, yScale, xShear, yShear, yShear1;
1778 int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
1779 int ulx, uly, llx, lly, urx, ury, lrx, lry;
1780 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1781 int xMin, xMax, yMin, yMax;
1782 SplashClipResult clipRes, clipRes2;
1783 int yp, yq, yt, yStep, lastYStep;
1784 int xp, xq, xt, xStep, xSrc;
1785 int k1, spanXMin, spanXMax, spanY;
1786 SplashColorPtr pixBuf, p;
1787 int pixAcc;
1788 int x, y, x1, x2, y2;
1789 SplashCoord y1;
1790 int n, m, i, j;
1792 if (debugMode) {
1793 printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1794 w, h, (double)mat[0], (double)mat[1], (double)mat[2],
1795 (double)mat[3], (double)mat[4], (double)mat[5]);
1798 // check for singular matrix
1799 if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1800 return splashErrSingularMatrix;
1803 // compute scale, shear, rotation, translation parameters
1804 rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1805 if (rot) {
1806 xScale = -mat[1];
1807 yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1808 xShear = -mat[3] / yScale;
1809 yShear = -mat[0] / mat[1];
1810 } else {
1811 xScale = mat[0];
1812 yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1813 xShear = mat[2] / yScale;
1814 yShear = mat[1] / mat[0];
1816 // Note 1: The PDF spec says that all pixels whose *centers* lie
1817 // within the region get painted -- but that doesn't seem to match
1818 // up with what Acrobat actually does: it ends up leaving gaps
1819 // between image stripes. So we use the same rule here as for
1820 // fills: any pixel that overlaps the region gets painted.
1821 // Note 2: The "glyphMode" flag is a kludge: it switches back to
1822 // "correct" behavior (matching the spec), for use in rendering Type
1823 // 3 fonts.
1824 // Note 3: The +/-0.01 in these computations is to avoid floating
1825 // point precision problems which can lead to gaps between image
1826 // stripes (it can cause image stripes to overlap, but that's a much
1827 // less visible problem).
1828 if (glyphMode) {
1829 if (xScale >= 0) {
1830 tx = splashRound(mat[4]);
1831 tx2 = splashRound(mat[4] + xScale) - 1;
1832 } else {
1833 tx = splashRound(mat[4]) - 1;
1834 tx2 = splashRound(mat[4] + xScale);
1836 } else {
1837 if (xScale >= 0) {
1838 tx = splashFloor(mat[4] - 0.01);
1839 tx2 = splashFloor(mat[4] + xScale + 0.01);
1840 } else {
1841 tx = splashFloor(mat[4] + 0.01);
1842 tx2 = splashFloor(mat[4] + xScale - 0.01);
1845 scaledWidth = abs(tx2 - tx) + 1;
1846 if (glyphMode) {
1847 if (yScale >= 0) {
1848 ty = splashRound(mat[5]);
1849 ty2 = splashRound(mat[5] + yScale) - 1;
1850 } else {
1851 ty = splashRound(mat[5]) - 1;
1852 ty2 = splashRound(mat[5] + yScale);
1854 } else {
1855 if (yScale >= 0) {
1856 ty = splashFloor(mat[5] - 0.01);
1857 ty2 = splashFloor(mat[5] + yScale + 0.01);
1858 } else {
1859 ty = splashFloor(mat[5] + 0.01);
1860 ty2 = splashFloor(mat[5] + yScale - 0.01);
1863 scaledHeight = abs(ty2 - ty) + 1;
1864 xSign = (xScale < 0) ? -1 : 1;
1865 ySign = (yScale < 0) ? -1 : 1;
1866 yShear1 = (SplashCoord)xSign * yShear;
1868 // clipping
1869 ulx1 = 0;
1870 uly1 = 0;
1871 urx1 = xSign * (scaledWidth - 1);
1872 ury1 = (int)(yShear * urx1);
1873 llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1874 lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
1875 lrx1 = xSign * (scaledWidth - 1) +
1876 splashRound(xShear * ySign * (scaledHeight - 1));
1877 lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
1878 if (rot) {
1879 ulx = tx + uly1; uly = ty - ulx1;
1880 urx = tx + ury1; ury = ty - urx1;
1881 llx = tx + lly1; lly = ty - llx1;
1882 lrx = tx + lry1; lry = ty - lrx1;
1883 } else {
1884 ulx = tx + ulx1; uly = ty + uly1;
1885 urx = tx + urx1; ury = ty + ury1;
1886 llx = tx + llx1; lly = ty + lly1;
1887 lrx = tx + lrx1; lry = ty + lry1;
1889 xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1890 : (llx < lrx) ? llx : lrx
1891 : (urx < llx) ? (urx < lrx) ? urx : lrx
1892 : (llx < lrx) ? llx : lrx;
1893 xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1894 : (llx > lrx) ? llx : lrx
1895 : (urx > llx) ? (urx > lrx) ? urx : lrx
1896 : (llx > lrx) ? llx : lrx;
1897 yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1898 : (lly < lry) ? lly : lry
1899 : (ury < lly) ? (ury < lry) ? ury : lry
1900 : (lly < lry) ? lly : lry;
1901 yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1902 : (lly > lry) ? lly : lry
1903 : (ury > lly) ? (ury > lry) ? ury : lry
1904 : (lly > lry) ? lly : lry;
1905 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
1906 opClipRes = clipRes;
1908 // compute Bresenham parameters for x and y scaling
1909 yp = h / scaledHeight;
1910 yq = h % scaledHeight;
1911 xp = w / scaledWidth;
1912 xq = w % scaledWidth;
1914 // allocate pixel buffer
1915 pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);
1917 // initialize the pixel pipe
1918 pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
1919 gTrue, gFalse);
1920 if (vectorAntialias) {
1921 drawAAPixelInit();
1924 // init y scale Bresenham
1925 yt = 0;
1926 lastYStep = 1;
1928 for (y = 0; y < scaledHeight; ++y) {
1930 // y scale Bresenham
1931 yStep = yp;
1932 yt += yq;
1933 if (yt >= scaledHeight) {
1934 yt -= scaledHeight;
1935 ++yStep;
1938 // read row(s) from image
1939 n = (yp > 0) ? yStep : lastYStep;
1940 if (n > 0) {
1941 p = pixBuf;
1942 for (i = 0; i < n; ++i) {
1943 (*src)(srcData, p);
1944 p += w;
1947 lastYStep = yStep;
1949 // loop-invariant constants
1950 k1 = splashRound(xShear * ySign * y);
1952 // clipping test
1953 if (clipRes != splashClipAllInside &&
1954 !rot &&
1955 (int)(yShear * k1) ==
1956 (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
1957 if (xSign > 0) {
1958 spanXMin = tx + k1;
1959 spanXMax = spanXMin + (scaledWidth - 1);
1960 } else {
1961 spanXMax = tx + k1;
1962 spanXMin = spanXMax - (scaledWidth - 1);
1964 spanY = ty + ySign * y + (int)(yShear * k1);
1965 clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1966 if (clipRes2 == splashClipAllOutside) {
1967 continue;
1969 } else {
1970 clipRes2 = clipRes;
1973 // init x scale Bresenham
1974 xt = 0;
1975 xSrc = 0;
1977 // x shear
1978 x1 = k1;
1980 // y shear
1981 y1 = (SplashCoord)ySign * y + yShear * x1;
1982 // this is a kludge: if yShear1 is negative, then (int)y1 would
1983 // change immediately after the first pixel, which is not what we
1984 // want
1985 if (yShear1 < 0) {
1986 y1 += 0.999;
1989 // loop-invariant constants
1990 n = yStep > 0 ? yStep : 1;
1992 for (x = 0; x < scaledWidth; ++x) {
1994 // x scale Bresenham
1995 xStep = xp;
1996 xt += xq;
1997 if (xt >= scaledWidth) {
1998 xt -= scaledWidth;
1999 ++xStep;
2002 // rotation
2003 if (rot) {
2004 x2 = (int)y1;
2005 y2 = -x1;
2006 } else {
2007 x2 = x1;
2008 y2 = (int)y1;
2011 // compute the alpha value for (x,y) after the x and y scaling
2012 // operations
2013 m = xStep > 0 ? xStep : 1;
2014 p = pixBuf + xSrc;
2015 pixAcc = 0;
2016 for (i = 0; i < n; ++i) {
2017 for (j = 0; j < m; ++j) {
2018 pixAcc += *p++;
2020 p += w - m;
2023 // blend fill color with background
2024 if (pixAcc != 0) {
2025 pipe.shape = (pixAcc == n * m)
2026 ? (SplashCoord)1
2027 : (SplashCoord)pixAcc / (SplashCoord)(n * m);
2028 if (vectorAntialias && clipRes2 != splashClipAllInside) {
2029 drawAAPixel(&pipe, tx + x2, ty + y2);
2030 } else {
2031 drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
2035 // x scale Bresenham
2036 xSrc += xStep;
2038 // x shear
2039 x1 += xSign;
2041 // y shear
2042 y1 += yShear1;
2046 // free memory
2047 gfree(pixBuf);
2049 return splashOk;
2052 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
2053 SplashColorMode srcMode, GBool srcAlpha,
2054 int w, int h, SplashCoord *mat) {
2055 SplashPipe pipe;
2056 GBool ok, rot;
2057 SplashCoord xScale, yScale, xShear, yShear, yShear1;
2058 int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
2059 int ulx, uly, llx, lly, urx, ury, lrx, lry;
2060 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
2061 int xMin, xMax, yMin, yMax;
2062 SplashClipResult clipRes, clipRes2;
2063 int yp, yq, yt, yStep, lastYStep;
2064 int xp, xq, xt, xStep, xSrc;
2065 int k1, spanXMin, spanXMax, spanY;
2066 SplashColorPtr colorBuf, p;
2067 SplashColor pix;
2068 Guchar *alphaBuf, *q;
2069 #if SPLASH_CMYK
2070 int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
2071 #else
2072 int pixAcc0, pixAcc1, pixAcc2;
2073 #endif
2074 int alphaAcc;
2075 SplashCoord pixMul, alphaMul, alpha;
2076 int x, y, x1, x2, y2;
2077 SplashCoord y1;
2078 int nComps, n, m, i, j;
2080 if (debugMode) {
2081 printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2082 srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2083 (double)mat[3], (double)mat[4], (double)mat[5]);
2086 // check color modes
2087 ok = gFalse; // make gcc happy
2088 nComps = 0; // make gcc happy
2089 switch (bitmap->mode) {
2090 case splashModeMono1:
2091 case splashModeMono8:
2092 ok = srcMode == splashModeMono8;
2093 nComps = 1;
2094 break;
2095 case splashModeRGB8:
2096 ok = srcMode == splashModeRGB8;
2097 nComps = 3;
2098 break;
2099 case splashModeBGR8:
2100 ok = srcMode == splashModeBGR8;
2101 nComps = 3;
2102 break;
2103 #if SPLASH_CMYK
2104 case splashModeCMYK8:
2105 ok = srcMode == splashModeCMYK8;
2106 nComps = 4;
2107 break;
2108 #endif
2110 if (!ok) {
2111 return splashErrModeMismatch;
2114 // check for singular matrix
2115 if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
2116 return splashErrSingularMatrix;
2119 // compute scale, shear, rotation, translation parameters
2120 rot = splashAbs(mat[1]) > splashAbs(mat[0]);
2121 if (rot) {
2122 xScale = -mat[1];
2123 yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
2124 xShear = -mat[3] / yScale;
2125 yShear = -mat[0] / mat[1];
2126 } else {
2127 xScale = mat[0];
2128 yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
2129 xShear = mat[2] / yScale;
2130 yShear = mat[1] / mat[0];
2132 // Note 1: The PDF spec says that all pixels whose *centers* lie
2133 // within the region get painted -- but that doesn't seem to match
2134 // up with what Acrobat actually does: it ends up leaving gaps
2135 // between image stripes. So we use the same rule here as for
2136 // fills: any pixel that overlaps the region gets painted.
2137 // Note 2: The +/-0.01 in these computations is to avoid floating
2138 // point precision problems which can lead to gaps between image
2139 // stripes (it can cause image stripes to overlap, but that's a much
2140 // less visible problem).
2141 if (xScale >= 0) {
2142 tx = splashFloor(mat[4] - 0.01);
2143 tx2 = splashFloor(mat[4] + xScale + 0.01);
2144 } else {
2145 tx = splashFloor(mat[4] + 0.01);
2146 tx2 = splashFloor(mat[4] + xScale - 0.01);
2148 scaledWidth = abs(tx2 - tx) + 1;
2149 if (yScale >= 0) {
2150 ty = splashFloor(mat[5] - 0.01);
2151 ty2 = splashFloor(mat[5] + yScale + 0.01);
2152 } else {
2153 ty = splashFloor(mat[5] + 0.01);
2154 ty2 = splashFloor(mat[5] + yScale - 0.01);
2156 scaledHeight = abs(ty2 - ty) + 1;
2157 xSign = (xScale < 0) ? -1 : 1;
2158 ySign = (yScale < 0) ? -1 : 1;
2159 yShear1 = (SplashCoord)xSign * yShear;
2161 // clipping
2162 ulx1 = 0;
2163 uly1 = 0;
2164 urx1 = xSign * (scaledWidth - 1);
2165 ury1 = (int)(yShear * urx1);
2166 llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
2167 lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
2168 lrx1 = xSign * (scaledWidth - 1) +
2169 splashRound(xShear * ySign * (scaledHeight - 1));
2170 lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
2171 if (rot) {
2172 ulx = tx + uly1; uly = ty - ulx1;
2173 urx = tx + ury1; ury = ty - urx1;
2174 llx = tx + lly1; lly = ty - llx1;
2175 lrx = tx + lry1; lry = ty - lrx1;
2176 } else {
2177 ulx = tx + ulx1; uly = ty + uly1;
2178 urx = tx + urx1; ury = ty + ury1;
2179 llx = tx + llx1; lly = ty + lly1;
2180 lrx = tx + lrx1; lry = ty + lry1;
2182 xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
2183 : (llx < lrx) ? llx : lrx
2184 : (urx < llx) ? (urx < lrx) ? urx : lrx
2185 : (llx < lrx) ? llx : lrx;
2186 xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
2187 : (llx > lrx) ? llx : lrx
2188 : (urx > llx) ? (urx > lrx) ? urx : lrx
2189 : (llx > lrx) ? llx : lrx;
2190 yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
2191 : (lly < lry) ? lly : lry
2192 : (ury < lly) ? (ury < lry) ? ury : lry
2193 : (lly < lry) ? lly : lry;
2194 yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
2195 : (lly > lry) ? lly : lry
2196 : (ury > lly) ? (ury > lry) ? ury : lry
2197 : (lly > lry) ? lly : lry;
2198 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
2199 opClipRes = clipRes;
2200 if (clipRes == splashClipAllOutside) {
2201 return splashOk;
2204 // compute Bresenham parameters for x and y scaling
2205 yp = h / scaledHeight;
2206 yq = h % scaledHeight;
2207 xp = w / scaledWidth;
2208 xq = w % scaledWidth;
2210 // allocate pixel buffers
2211 colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
2212 if (srcAlpha) {
2213 alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
2214 } else {
2215 alphaBuf = NULL;
2218 pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
2219 #if SPLASH_CMYK
2220 pixAcc3 = 0; // make gcc happy
2221 #endif
2223 // initialize the pixel pipe
2224 pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,
2225 srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
2226 gFalse);
2227 if (vectorAntialias) {
2228 drawAAPixelInit();
2231 if (srcAlpha) {
2233 // init y scale Bresenham
2234 yt = 0;
2235 lastYStep = 1;
2237 for (y = 0; y < scaledHeight; ++y) {
2239 // y scale Bresenham
2240 yStep = yp;
2241 yt += yq;
2242 if (yt >= scaledHeight) {
2243 yt -= scaledHeight;
2244 ++yStep;
2247 // read row(s) from image
2248 n = (yp > 0) ? yStep : lastYStep;
2249 if (n > 0) {
2250 p = colorBuf;
2251 q = alphaBuf;
2252 for (i = 0; i < n; ++i) {
2253 (*src)(srcData, p, q);
2254 p += w * nComps;
2255 q += w;
2258 lastYStep = yStep;
2260 // loop-invariant constants
2261 k1 = splashRound(xShear * ySign * y);
2263 // clipping test
2264 if (clipRes != splashClipAllInside &&
2265 !rot &&
2266 (int)(yShear * k1) ==
2267 (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2268 if (xSign > 0) {
2269 spanXMin = tx + k1;
2270 spanXMax = spanXMin + (scaledWidth - 1);
2271 } else {
2272 spanXMax = tx + k1;
2273 spanXMin = spanXMax - (scaledWidth - 1);
2275 spanY = ty + ySign * y + (int)(yShear * k1);
2276 clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2277 if (clipRes2 == splashClipAllOutside) {
2278 continue;
2280 } else {
2281 clipRes2 = clipRes;
2284 // init x scale Bresenham
2285 xt = 0;
2286 xSrc = 0;
2288 // x shear
2289 x1 = k1;
2291 // y shear
2292 y1 = (SplashCoord)ySign * y + yShear * x1;
2293 // this is a kludge: if yShear1 is negative, then (int)y1 would
2294 // change immediately after the first pixel, which is not what
2295 // we want
2296 if (yShear1 < 0) {
2297 y1 += 0.999;
2300 // loop-invariant constants
2301 n = yStep > 0 ? yStep : 1;
2303 switch (srcMode) {
2305 case splashModeMono1:
2306 case splashModeMono8:
2307 for (x = 0; x < scaledWidth; ++x) {
2309 // x scale Bresenham
2310 xStep = xp;
2311 xt += xq;
2312 if (xt >= scaledWidth) {
2313 xt -= scaledWidth;
2314 ++xStep;
2317 // rotation
2318 if (rot) {
2319 x2 = (int)y1;
2320 y2 = -x1;
2321 } else {
2322 x2 = x1;
2323 y2 = (int)y1;
2326 // compute the filtered pixel at (x,y) after the x and y scaling
2327 // operations
2328 m = xStep > 0 ? xStep : 1;
2329 alphaAcc = 0;
2330 p = colorBuf + xSrc;
2331 q = alphaBuf + xSrc;
2332 pixAcc0 = 0;
2333 for (i = 0; i < n; ++i) {
2334 for (j = 0; j < m; ++j) {
2335 pixAcc0 += *p++;
2336 alphaAcc += *q++;
2338 p += w - m;
2339 q += w - m;
2341 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2342 alphaMul = pixMul * (1.0 / 255.0);
2343 alpha = (SplashCoord)alphaAcc * alphaMul;
2345 if (alpha > 0) {
2346 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2348 // set pixel
2349 pipe.shape = alpha;
2350 if (vectorAntialias && clipRes != splashClipAllInside) {
2351 drawAAPixel(&pipe, tx + x2, ty + y2);
2352 } else {
2353 drawPixel(&pipe, tx + x2, ty + y2,
2354 clipRes2 == splashClipAllInside);
2358 // x scale Bresenham
2359 xSrc += xStep;
2361 // x shear
2362 x1 += xSign;
2364 // y shear
2365 y1 += yShear1;
2367 break;
2369 case splashModeRGB8:
2370 case splashModeBGR8:
2371 for (x = 0; x < scaledWidth; ++x) {
2373 // x scale Bresenham
2374 xStep = xp;
2375 xt += xq;
2376 if (xt >= scaledWidth) {
2377 xt -= scaledWidth;
2378 ++xStep;
2381 // rotation
2382 if (rot) {
2383 x2 = (int)y1;
2384 y2 = -x1;
2385 } else {
2386 x2 = x1;
2387 y2 = (int)y1;
2390 // compute the filtered pixel at (x,y) after the x and y scaling
2391 // operations
2392 m = xStep > 0 ? xStep : 1;
2393 alphaAcc = 0;
2394 p = colorBuf + xSrc * 3;
2395 q = alphaBuf + xSrc;
2396 pixAcc0 = pixAcc1 = pixAcc2 = 0;
2397 for (i = 0; i < n; ++i) {
2398 for (j = 0; j < m; ++j) {
2399 pixAcc0 += *p++;
2400 pixAcc1 += *p++;
2401 pixAcc2 += *p++;
2402 alphaAcc += *q++;
2404 p += 3 * (w - m);
2405 q += w - m;
2407 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2408 alphaMul = pixMul * (1.0 / 255.0);
2409 alpha = (SplashCoord)alphaAcc * alphaMul;
2411 if (alpha > 0) {
2412 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2413 pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2414 pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2416 // set pixel
2417 pipe.shape = alpha;
2418 if (vectorAntialias && clipRes != splashClipAllInside) {
2419 drawAAPixel(&pipe, tx + x2, ty + y2);
2420 } else {
2421 drawPixel(&pipe, tx + x2, ty + y2,
2422 clipRes2 == splashClipAllInside);
2426 // x scale Bresenham
2427 xSrc += xStep;
2429 // x shear
2430 x1 += xSign;
2432 // y shear
2433 y1 += yShear1;
2435 break;
2437 #if SPLASH_CMYK
2438 case splashModeCMYK8:
2439 for (x = 0; x < scaledWidth; ++x) {
2441 // x scale Bresenham
2442 xStep = xp;
2443 xt += xq;
2444 if (xt >= scaledWidth) {
2445 xt -= scaledWidth;
2446 ++xStep;
2449 // rotation
2450 if (rot) {
2451 x2 = (int)y1;
2452 y2 = -x1;
2453 } else {
2454 x2 = x1;
2455 y2 = (int)y1;
2458 // compute the filtered pixel at (x,y) after the x and y scaling
2459 // operations
2460 m = xStep > 0 ? xStep : 1;
2461 alphaAcc = 0;
2462 p = colorBuf + xSrc * 4;
2463 q = alphaBuf + xSrc;
2464 pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2465 for (i = 0; i < n; ++i) {
2466 for (j = 0; j < m; ++j) {
2467 pixAcc0 += *p++;
2468 pixAcc1 += *p++;
2469 pixAcc2 += *p++;
2470 pixAcc3 += *p++;
2471 alphaAcc += *q++;
2473 p += 4 * (w - m);
2474 q += w - m;
2476 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2477 alphaMul = pixMul * (1.0 / 255.0);
2478 alpha = (SplashCoord)alphaAcc * alphaMul;
2480 if (alpha > 0) {
2481 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2482 pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2483 pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2484 pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2486 // set pixel
2487 pipe.shape = alpha;
2488 if (vectorAntialias && clipRes != splashClipAllInside) {
2489 drawAAPixel(&pipe, tx + x2, ty + y2);
2490 } else {
2491 drawPixel(&pipe, tx + x2, ty + y2,
2492 clipRes2 == splashClipAllInside);
2496 // x scale Bresenham
2497 xSrc += xStep;
2499 // x shear
2500 x1 += xSign;
2502 // y shear
2503 y1 += yShear1;
2505 break;
2506 #endif // SPLASH_CMYK
2510 } else {
2512 // init y scale Bresenham
2513 yt = 0;
2514 lastYStep = 1;
2516 for (y = 0; y < scaledHeight; ++y) {
2518 // y scale Bresenham
2519 yStep = yp;
2520 yt += yq;
2521 if (yt >= scaledHeight) {
2522 yt -= scaledHeight;
2523 ++yStep;
2526 // read row(s) from image
2527 n = (yp > 0) ? yStep : lastYStep;
2528 if (n > 0) {
2529 p = colorBuf;
2530 for (i = 0; i < n; ++i) {
2531 (*src)(srcData, p, NULL);
2532 p += w * nComps;
2535 lastYStep = yStep;
2537 // loop-invariant constants
2538 k1 = splashRound(xShear * ySign * y);
2540 // clipping test
2541 if (clipRes != splashClipAllInside &&
2542 !rot &&
2543 (int)(yShear * k1) ==
2544 (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2545 if (xSign > 0) {
2546 spanXMin = tx + k1;
2547 spanXMax = spanXMin + (scaledWidth - 1);
2548 } else {
2549 spanXMax = tx + k1;
2550 spanXMin = spanXMax - (scaledWidth - 1);
2552 spanY = ty + ySign * y + (int)(yShear * k1);
2553 clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2554 if (clipRes2 == splashClipAllOutside) {
2555 continue;
2557 } else {
2558 clipRes2 = clipRes;
2561 // init x scale Bresenham
2562 xt = 0;
2563 xSrc = 0;
2565 // x shear
2566 x1 = k1;
2568 // y shear
2569 y1 = (SplashCoord)ySign * y + yShear * x1;
2570 // this is a kludge: if yShear1 is negative, then (int)y1 would
2571 // change immediately after the first pixel, which is not what
2572 // we want
2573 if (yShear1 < 0) {
2574 y1 += 0.999;
2577 // loop-invariant constants
2578 n = yStep > 0 ? yStep : 1;
2580 switch (srcMode) {
2582 case splashModeMono1:
2583 case splashModeMono8:
2584 for (x = 0; x < scaledWidth; ++x) {
2586 // x scale Bresenham
2587 xStep = xp;
2588 xt += xq;
2589 if (xt >= scaledWidth) {
2590 xt -= scaledWidth;
2591 ++xStep;
2594 // rotation
2595 if (rot) {
2596 x2 = (int)y1;
2597 y2 = -x1;
2598 } else {
2599 x2 = x1;
2600 y2 = (int)y1;
2603 // compute the filtered pixel at (x,y) after the x and y scaling
2604 // operations
2605 m = xStep > 0 ? xStep : 1;
2606 p = colorBuf + xSrc;
2607 pixAcc0 = 0;
2608 for (i = 0; i < n; ++i) {
2609 for (j = 0; j < m; ++j) {
2610 pixAcc0 += *p++;
2612 p += w - m;
2614 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2616 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2618 // set pixel
2619 if (vectorAntialias && clipRes != splashClipAllInside) {
2620 pipe.shape = (SplashCoord)1;
2621 drawAAPixel(&pipe, tx + x2, ty + y2);
2622 } else {
2623 drawPixel(&pipe, tx + x2, ty + y2,
2624 clipRes2 == splashClipAllInside);
2627 // x scale Bresenham
2628 xSrc += xStep;
2630 // x shear
2631 x1 += xSign;
2633 // y shear
2634 y1 += yShear1;
2636 break;
2638 case splashModeRGB8:
2639 case splashModeBGR8:
2640 for (x = 0; x < scaledWidth; ++x) {
2642 // x scale Bresenham
2643 xStep = xp;
2644 xt += xq;
2645 if (xt >= scaledWidth) {
2646 xt -= scaledWidth;
2647 ++xStep;
2650 // rotation
2651 if (rot) {
2652 x2 = (int)y1;
2653 y2 = -x1;
2654 } else {
2655 x2 = x1;
2656 y2 = (int)y1;
2659 // compute the filtered pixel at (x,y) after the x and y scaling
2660 // operations
2661 m = xStep > 0 ? xStep : 1;
2662 p = colorBuf + xSrc * 3;
2663 pixAcc0 = pixAcc1 = pixAcc2 = 0;
2664 for (i = 0; i < n; ++i) {
2665 for (j = 0; j < m; ++j) {
2666 pixAcc0 += *p++;
2667 pixAcc1 += *p++;
2668 pixAcc2 += *p++;
2670 p += 3 * (w - m);
2672 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2674 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2675 pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2676 pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2678 // set pixel
2679 if (vectorAntialias && clipRes != splashClipAllInside) {
2680 pipe.shape = (SplashCoord)1;
2681 drawAAPixel(&pipe, tx + x2, ty + y2);
2682 } else {
2683 drawPixel(&pipe, tx + x2, ty + y2,
2684 clipRes2 == splashClipAllInside);
2687 // x scale Bresenham
2688 xSrc += xStep;
2690 // x shear
2691 x1 += xSign;
2693 // y shear
2694 y1 += yShear1;
2696 break;
2698 #if SPLASH_CMYK
2699 case splashModeCMYK8:
2700 for (x = 0; x < scaledWidth; ++x) {
2702 // x scale Bresenham
2703 xStep = xp;
2704 xt += xq;
2705 if (xt >= scaledWidth) {
2706 xt -= scaledWidth;
2707 ++xStep;
2710 // rotation
2711 if (rot) {
2712 x2 = (int)y1;
2713 y2 = -x1;
2714 } else {
2715 x2 = x1;
2716 y2 = (int)y1;
2719 // compute the filtered pixel at (x,y) after the x and y scaling
2720 // operations
2721 m = xStep > 0 ? xStep : 1;
2722 p = colorBuf + xSrc * 4;
2723 pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2724 for (i = 0; i < n; ++i) {
2725 for (j = 0; j < m; ++j) {
2726 pixAcc0 += *p++;
2727 pixAcc1 += *p++;
2728 pixAcc2 += *p++;
2729 pixAcc3 += *p++;
2731 p += 4 * (w - m);
2733 pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2735 pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2736 pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2737 pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2738 pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2740 // set pixel
2741 if (vectorAntialias && clipRes != splashClipAllInside) {
2742 pipe.shape = (SplashCoord)1;
2743 drawAAPixel(&pipe, tx + x2, ty + y2);
2744 } else {
2745 drawPixel(&pipe, tx + x2, ty + y2,
2746 clipRes2 == splashClipAllInside);
2749 // x scale Bresenham
2750 xSrc += xStep;
2752 // x shear
2753 x1 += xSign;
2755 // y shear
2756 y1 += yShear1;
2758 break;
2759 #endif // SPLASH_CMYK
2765 gfree(colorBuf);
2766 gfree(alphaBuf);
2768 return splashOk;
2771 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
2772 int xDest, int yDest, int w, int h,
2773 GBool noClip, GBool nonIsolated) {
2774 SplashPipe pipe;
2775 SplashColor pixel;
2776 Guchar alpha;
2777 Guchar *ap;
2778 int x, y;
2780 if (src->mode != bitmap->mode) {
2781 return splashErrModeMismatch;
2784 if (src->alpha) {
2785 pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
2786 gTrue, nonIsolated);
2787 for (y = 0; y < h; ++y) {
2788 pipeSetXY(&pipe, xDest, yDest + y);
2789 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
2790 for (x = 0; x < w; ++x) {
2791 src->getPixel(xSrc + x, ySrc + y, pixel);
2792 alpha = *ap++;
2793 if (noClip || state->clip->test(xDest + x, yDest + y)) {
2794 // this uses shape instead of alpha, which isn't technically
2795 // correct, but works out the same
2796 pipe.shape = (SplashCoord)(alpha / 255.0);
2797 pipeRun(&pipe);
2798 updateModX(xDest + x);
2799 updateModY(yDest + y);
2800 } else {
2801 pipeIncX(&pipe);
2805 } else {
2806 pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
2807 gFalse, nonIsolated);
2808 for (y = 0; y < h; ++y) {
2809 pipeSetXY(&pipe, xDest, yDest + y);
2810 for (x = 0; x < w; ++x) {
2811 src->getPixel(xSrc + x, ySrc + y, pixel);
2812 if (noClip || state->clip->test(xDest + x, yDest + y)) {
2813 pipeRun(&pipe);
2814 updateModX(xDest + x);
2815 updateModY(yDest + y);
2816 } else {
2817 pipeIncX(&pipe);
2823 return splashOk;
2826 void Splash::compositeBackground(SplashColorPtr color) {
2827 SplashColorPtr p;
2828 Guchar *q;
2829 Guchar alpha, alpha1, c, color0, color1, color2, color3;
2830 int x, y, mask;
2832 switch (bitmap->mode) {
2833 case splashModeMono1:
2834 color0 = color[0];
2835 for (y = 0; y < bitmap->height; ++y) {
2836 p = &bitmap->data[y * bitmap->rowSize];
2837 q = &bitmap->alpha[y * bitmap->width];
2838 mask = 0x80;
2839 for (x = 0; x < bitmap->width; ++x) {
2840 alpha = *q++;
2841 alpha1 = 255 - alpha;
2842 c = (*p & mask) ? 0xff : 0x00;
2843 c = div255(alpha1 * color0 + alpha * c);
2844 if (c & 0x80) {
2845 *p |= mask;
2846 } else {
2847 *p &= ~mask;
2849 if (!(mask >>= 1)) {
2850 mask = 0x80;
2851 ++p;
2855 break;
2856 case splashModeMono8:
2857 color0 = color[0];
2858 for (y = 0; y < bitmap->height; ++y) {
2859 p = &bitmap->data[y * bitmap->rowSize];
2860 q = &bitmap->alpha[y * bitmap->width];
2861 for (x = 0; x < bitmap->width; ++x) {
2862 alpha = *q++;
2863 alpha1 = 255 - alpha;
2864 p[0] = div255(alpha1 * color0 + alpha * p[0]);
2865 ++p;
2868 break;
2869 case splashModeRGB8:
2870 case splashModeBGR8:
2871 color0 = color[0];
2872 color1 = color[1];
2873 color2 = color[2];
2874 for (y = 0; y < bitmap->height; ++y) {
2875 p = &bitmap->data[y * bitmap->rowSize];
2876 q = &bitmap->alpha[y * bitmap->width];
2877 for (x = 0; x < bitmap->width; ++x) {
2878 alpha = *q++;
2879 alpha1 = 255 - alpha;
2880 p[0] = div255(alpha1 * color0 + alpha * p[0]);
2881 p[1] = div255(alpha1 * color1 + alpha * p[1]);
2882 p[2] = div255(alpha1 * color2 + alpha * p[2]);
2883 p += 3;
2886 break;
2887 #if SPLASH_CMYK
2888 case splashModeCMYK8:
2889 color0 = color[0];
2890 color1 = color[1];
2891 color2 = color[2];
2892 color3 = color[3];
2893 for (y = 0; y < bitmap->height; ++y) {
2894 p = &bitmap->data[y * bitmap->rowSize];
2895 q = &bitmap->alpha[y * bitmap->width];
2896 for (x = 0; x < bitmap->width; ++x) {
2897 alpha = *q++;
2898 alpha1 = 255 - alpha;
2899 p[0] = div255(alpha1 * color0 + alpha * p[0]);
2900 p[1] = div255(alpha1 * color1 + alpha * p[1]);
2901 p[2] = div255(alpha1 * color2 + alpha * p[2]);
2902 p[3] = div255(alpha1 * color3 + alpha * p[3]);
2903 p += 4;
2906 break;
2907 #endif
2909 memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
2912 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
2913 int xDest, int yDest, int w, int h) {
2914 SplashColor pixel;
2915 SplashColorPtr p;
2916 Guchar *q;
2917 int x, y, mask;
2919 if (src->mode != bitmap->mode) {
2920 return splashErrModeMismatch;
2923 switch (bitmap->mode) {
2924 case splashModeMono1:
2925 for (y = 0; y < h; ++y) {
2926 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
2927 mask = 0x80 >> (xDest & 7);
2928 for (x = 0; x < w; ++x) {
2929 src->getPixel(xSrc + x, ySrc + y, pixel);
2930 if (pixel[0]) {
2931 *p |= mask;
2932 } else {
2933 *p &= ~mask;
2935 if (!(mask >>= 1)) {
2936 mask = 0x80;
2937 ++p;
2941 break;
2942 case splashModeMono8:
2943 for (y = 0; y < h; ++y) {
2944 p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
2945 for (x = 0; x < w; ++x) {
2946 src->getPixel(xSrc + x, ySrc + y, pixel);
2947 *p++ = pixel[0];
2950 break;
2951 case splashModeRGB8:
2952 case splashModeBGR8:
2953 for (y = 0; y < h; ++y) {
2954 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
2955 for (x = 0; x < w; ++x) {
2956 src->getPixel(xSrc + x, ySrc + y, pixel);
2957 *p++ = pixel[0];
2958 *p++ = pixel[1];
2959 *p++ = pixel[2];
2962 break;
2963 #if SPLASH_CMYK
2964 case splashModeCMYK8:
2965 for (y = 0; y < h; ++y) {
2966 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
2967 for (x = 0; x < w; ++x) {
2968 src->getPixel(xSrc + x, ySrc + y, pixel);
2969 *p++ = pixel[0];
2970 *p++ = pixel[1];
2971 *p++ = pixel[2];
2972 *p++ = pixel[3];
2975 break;
2976 #endif
2979 if (bitmap->alpha) {
2980 for (y = 0; y < h; ++y) {
2981 q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
2982 for (x = 0; x < w; ++x) {
2983 *q++ = 0x00;
2988 return splashOk;
2991 SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
2992 SplashPath *pathIn, *pathOut;
2993 SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
2994 SplashCoord crossprod, dotprod, miter, m;
2995 GBool first, last, closed;
2996 int subpathStart, next, i;
2997 int left0, left1, left2, right0, right1, right2, join0, join1, join2;
2998 int leftFirst, rightFirst, firstPt;
3000 if (flatten) {
3001 pathIn = flattenPath(path, state->matrix, state->flatness);
3002 if (state->lineDashLength > 0) {
3003 pathOut = makeDashedPath(pathIn);
3004 delete pathIn;
3005 pathIn = pathOut;
3007 } else {
3008 pathIn = path;
3011 subpathStart = 0; // make gcc happy
3012 closed = gFalse; // make gcc happy
3013 left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
3014 leftFirst = rightFirst = firstPt = 0; // make gcc happy
3016 pathOut = new SplashPath();
3017 w = state->lineWidth;
3019 for (i = 0; i < pathIn->length - 1; ++i) {
3020 if (pathIn->flags[i] & splashPathLast) {
3021 continue;
3023 if ((first = pathIn->flags[i] & splashPathFirst)) {
3024 subpathStart = i;
3025 closed = pathIn->flags[i] & splashPathClosed;
3027 last = pathIn->flags[i+1] & splashPathLast;
3029 // compute the deltas for segment (i, i+1)
3030 d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
3031 pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3032 if (d == 0) {
3033 // we need to draw end caps on zero-length lines
3034 //~ not clear what the behavior should be for splashLineCapButt
3035 //~ with d==0
3036 dx = 0;
3037 dy = 1;
3038 } else {
3039 d = (SplashCoord)1 / d;
3040 dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
3041 dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
3043 wdx = (SplashCoord)0.5 * w * dx;
3044 wdy = (SplashCoord)0.5 * w * dy;
3046 // compute the deltas for segment (i+1, next)
3047 next = last ? subpathStart + 1 : i + 2;
3048 d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
3049 pathIn->pts[next].x, pathIn->pts[next].y);
3050 if (d == 0) {
3051 // we need to draw end caps on zero-length lines
3052 //~ not clear what the behavior should be for splashLineCapButt
3053 //~ with d==0
3054 dxNext = 0;
3055 dyNext = 1;
3056 } else {
3057 d = (SplashCoord)1 / d;
3058 dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
3059 dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
3061 wdxNext = (SplashCoord)0.5 * w * dxNext;
3062 wdyNext = (SplashCoord)0.5 * w * dyNext;
3064 // draw the start cap
3065 pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
3066 if (i == subpathStart) {
3067 firstPt = pathOut->length - 1;
3069 if (first && !closed) {
3070 switch (state->lineCap) {
3071 case splashLineCapButt:
3072 pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3073 break;
3074 case splashLineCapRound:
3075 pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
3076 pathIn->pts[i].y + wdx - bezierCircle * wdy,
3077 pathIn->pts[i].x - wdx - bezierCircle * wdy,
3078 pathIn->pts[i].y - wdy + bezierCircle * wdx,
3079 pathIn->pts[i].x - wdx,
3080 pathIn->pts[i].y - wdy);
3081 pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
3082 pathIn->pts[i].y - wdy - bezierCircle * wdx,
3083 pathIn->pts[i].x + wdy - bezierCircle * wdx,
3084 pathIn->pts[i].y - wdx - bezierCircle * wdy,
3085 pathIn->pts[i].x + wdy,
3086 pathIn->pts[i].y - wdx);
3087 break;
3088 case splashLineCapProjecting:
3089 pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
3090 pathIn->pts[i].y + wdx - wdy);
3091 pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
3092 pathIn->pts[i].y - wdx - wdy);
3093 pathOut->lineTo(pathIn->pts[i].x + wdy,
3094 pathIn->pts[i].y - wdx);
3095 break;
3097 } else {
3098 pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3101 // draw the left side of the segment rectangle
3102 left2 = pathOut->length - 1;
3103 pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);
3105 // draw the end cap
3106 if (last && !closed) {
3107 switch (state->lineCap) {
3108 case splashLineCapButt:
3109 pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3110 break;
3111 case splashLineCapRound:
3112 pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
3113 pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
3114 pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
3115 pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
3116 pathIn->pts[i+1].x + wdx,
3117 pathIn->pts[i+1].y + wdy);
3118 pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
3119 pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
3120 pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
3121 pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
3122 pathIn->pts[i+1].x - wdy,
3123 pathIn->pts[i+1].y + wdx);
3124 break;
3125 case splashLineCapProjecting:
3126 pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
3127 pathIn->pts[i+1].y - wdx + wdy);
3128 pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
3129 pathIn->pts[i+1].y + wdx + wdy);
3130 pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3131 pathIn->pts[i+1].y + wdx);
3132 break;
3134 } else {
3135 pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3138 // draw the right side of the segment rectangle
3139 right2 = pathOut->length - 1;
3140 pathOut->close();
3142 // draw the join
3143 join2 = pathOut->length;
3144 if (!last || closed) {
3145 crossprod = dx * dyNext - dy * dxNext;
3146 dotprod = -(dx * dxNext + dy * dyNext);
3147 if (dotprod > 0.99999) {
3148 // avoid a divide-by-zero -- set miter to something arbitrary
3149 // such that sqrt(miter) will exceed miterLimit (and m is never
3150 // used in that situation)
3151 miter = (state->miterLimit + 1) * (state->miterLimit + 1);
3152 m = 0;
3153 } else {
3154 miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
3155 if (miter < 1) {
3156 // this can happen because of floating point inaccuracies
3157 miter = 1;
3159 m = splashSqrt(miter - 1);
3162 // round join
3163 if (state->lineJoin == splashLineJoinRound) {
3164 pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3165 pathIn->pts[i+1].y);
3166 pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3167 pathIn->pts[i+1].y + bezierCircle2 * w,
3168 pathIn->pts[i+1].x + bezierCircle2 * w,
3169 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3170 pathIn->pts[i+1].x,
3171 pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
3172 pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
3173 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3174 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3175 pathIn->pts[i+1].y + bezierCircle2 * w,
3176 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3177 pathIn->pts[i+1].y);
3178 pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3179 pathIn->pts[i+1].y - bezierCircle2 * w,
3180 pathIn->pts[i+1].x - bezierCircle2 * w,
3181 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3182 pathIn->pts[i+1].x,
3183 pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
3184 pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
3185 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3186 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3187 pathIn->pts[i+1].y - bezierCircle2 * w,
3188 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3189 pathIn->pts[i+1].y);
3191 } else {
3192 pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3194 // angle < 180
3195 if (crossprod < 0) {
3196 pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
3197 pathIn->pts[i+1].y + wdxNext);
3198 // miter join inside limit
3199 if (state->lineJoin == splashLineJoinMiter &&
3200 splashSqrt(miter) <= state->miterLimit) {
3201 pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
3202 pathIn->pts[i+1].y + wdx + wdy * m);
3203 pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3204 pathIn->pts[i+1].y + wdx);
3205 // bevel join or miter join outside limit
3206 } else {
3207 pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3210 // angle >= 180
3211 } else {
3212 pathOut->lineTo(pathIn->pts[i+1].x + wdy,
3213 pathIn->pts[i+1].y - wdx);
3214 // miter join inside limit
3215 if (state->lineJoin == splashLineJoinMiter &&
3216 splashSqrt(miter) <= state->miterLimit) {
3217 pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
3218 pathIn->pts[i+1].y - wdx + wdy * m);
3219 pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3220 pathIn->pts[i+1].y - wdxNext);
3221 // bevel join or miter join outside limit
3222 } else {
3223 pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3224 pathIn->pts[i+1].y - wdxNext);
3229 pathOut->close();
3232 // add stroke adjustment hints
3233 if (state->strokeAdjust) {
3234 if (i >= subpathStart + 1) {
3235 if (i >= subpathStart + 2) {
3236 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3237 pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
3238 } else {
3239 pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
3241 pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
3243 left0 = left1;
3244 left1 = left2;
3245 right0 = right1;
3246 right1 = right2;
3247 join0 = join1;
3248 join1 = join2;
3249 if (i == subpathStart) {
3250 leftFirst = left2;
3251 rightFirst = right2;
3253 if (last) {
3254 if (i >= subpathStart + 2) {
3255 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3256 pathOut->addStrokeAdjustHint(left1, right1,
3257 join0, pathOut->length - 1);
3258 } else {
3259 pathOut->addStrokeAdjustHint(left1, right1,
3260 firstPt, pathOut->length - 1);
3262 if (closed) {
3263 pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
3264 pathOut->addStrokeAdjustHint(left1, right1,
3265 rightFirst + 1, rightFirst + 1);
3266 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3267 left1 + 1, right1);
3268 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3269 join1, pathOut->length - 1);
3275 if (pathIn != path) {
3276 delete pathIn;
3279 return pathOut;
3282 void Splash::dumpPath(SplashPath *path) {
3283 int i;
3285 for (i = 0; i < path->length; ++i) {
3286 printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
3287 i, (double)path->pts[i].x, (double)path->pts[i].y,
3288 (path->flags[i] & splashPathFirst) ? " first" : "",
3289 (path->flags[i] & splashPathLast) ? " last" : "",
3290 (path->flags[i] & splashPathClosed) ? " closed" : "",
3291 (path->flags[i] & splashPathCurve) ? " curve" : "");
3295 void Splash::dumpXPath(SplashXPath *path) {
3296 int i;
3298 for (i = 0; i < path->length; ++i) {
3299 printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
3300 i, (double)path->segs[i].x0, (double)path->segs[i].y0,
3301 (double)path->segs[i].x1, (double)path->segs[i].y1,
3302 (path->segs[i].flags & splashXPathFirst) ? "F" : " ",
3303 (path->segs[i].flags & splashXPathLast) ? "L" : " ",
3304 (path->segs[i].flags & splashXPathEnd0) ? "0" : " ",
3305 (path->segs[i].flags & splashXPathEnd1) ? "1" : " ",
3306 (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
3307 (path->segs[i].flags & splashXPathVert) ? "V" : " ",
3308 (path->segs[i].flags & splashXPathFlip) ? "P" : " ");