1 //========================================================================
5 //========================================================================
10 #pragma implementation
16 #include "SplashErrorCodes.h"
17 #include "SplashMath.h"
18 #include "SplashBitmap.h"
19 #include "SplashState.h"
20 #include "SplashPath.h"
21 #include "SplashXPath.h"
22 #include "SplashXPathScanner.h"
23 #include "SplashPattern.h"
24 #include "SplashScreen.h"
25 #include "SplashClip.h"
26 #include "SplashFont.h"
27 #include "SplashGlyphBitmap.h"
30 //------------------------------------------------------------------------
32 //------------------------------------------------------------------------
34 Splash::Splash(SplashBitmap *bitmapA) {
36 state = new SplashState(bitmap->width, bitmap->height);
47 //------------------------------------------------------------------------
49 //------------------------------------------------------------------------
52 SplashPattern *Splash::getStrokePattern() {
53 return state->strokePattern;
56 SplashPattern *Splash::getFillPattern() {
57 return state->fillPattern;
60 SplashScreen *Splash::getScreen() {
64 SplashCoord Splash::getLineWidth() {
65 return state->lineWidth;
68 int Splash::getLineCap() {
69 return state->lineCap;
72 int Splash::getLineJoin() {
73 return state->lineJoin;
76 SplashCoord Splash::getMiterLimit() {
77 return state->miterLimit;
80 SplashCoord Splash::getFlatness() {
81 return state->flatness;
84 SplashCoord *Splash::getLineDash() {
85 return state->lineDash;
88 int Splash::getLineDashLength() {
89 return state->lineDashLength;
92 SplashCoord Splash::getLineDashPhase() {
93 return state->lineDashPhase;
96 SplashClip *Splash::getClip() {
100 //------------------------------------------------------------------------
102 //------------------------------------------------------------------------
104 void Splash::setStrokePattern(SplashPattern *strokePattern) {
105 state->setStrokePattern(strokePattern);
108 void Splash::setFillPattern(SplashPattern *fillPattern) {
109 state->setFillPattern(fillPattern);
112 void Splash::setScreen(SplashScreen *screen) {
113 state->setScreen(screen);
116 void Splash::setLineWidth(SplashCoord lineWidth) {
117 state->lineWidth = lineWidth;
120 void Splash::setLineCap(int lineCap) {
121 state->lineCap = lineCap;
124 void Splash::setLineJoin(int lineJoin) {
125 state->lineJoin = lineJoin;
128 void Splash::setMiterLimit(SplashCoord miterLimit) {
129 state->miterLimit = miterLimit;
132 void Splash::setFlatness(SplashCoord flatness) {
136 state->flatness = flatness;
140 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
141 SplashCoord lineDashPhase) {
142 state->setLineDash(lineDash, lineDashLength, lineDashPhase);
145 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
146 SplashCoord x1, SplashCoord y1) {
147 state->clip->resetToRect(x0, y0, x1, y1);
150 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
151 SplashCoord x1, SplashCoord y1) {
152 return state->clip->clipToRect(x0, y0, x1, y1);
155 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
156 return state->clip->clipToPath(path, state->flatness, eo);
159 //------------------------------------------------------------------------
160 // state save/restore
161 //------------------------------------------------------------------------
163 void Splash::saveState() {
164 SplashState *newState;
166 newState = state->copy();
167 newState->next = state;
171 SplashError Splash::restoreState() {
172 SplashState *oldState;
175 return splashErrNoSave;
183 //------------------------------------------------------------------------
184 // drawing operations
185 //------------------------------------------------------------------------
187 void Splash::clear(SplashColor color) {
191 SplashBGR8P *bgr8line, *bgr8;
195 switch (bitmap->mode) {
196 case splashModeMono1:
197 n = ((bitmap->width + 7) >> 3) * bitmap->height;
198 data = color.mono1 ? 0xff : 0x00;
199 for (i = 0, mono1 = bitmap->data.mono1; i < n; ++i, ++mono1) {
203 case splashModeMono8:
204 n = bitmap->width * bitmap->height;
205 for (i = 0, mono8 = bitmap->data.mono8; i < n; ++i, ++mono8) {
206 *mono8 = color.mono8;
210 n = bitmap->width * bitmap->height;
211 for (i = 0, rgb8 = bitmap->data.rgb8; i < n; ++i, ++rgb8) {
215 case splashModeBGR8Packed:
216 bgr8line = bitmap->data.bgr8;
217 for (y = 0; y < bitmap->height; ++y) {
219 for (x = 0; x < bitmap->width; ++x) {
220 bgr8[2] = splashBGR8R(color.bgr8);
221 bgr8[1] = splashBGR8G(color.bgr8);
222 bgr8[0] = splashBGR8B(color.bgr8);
225 bgr8line += bitmap->rowSize;
231 SplashError Splash::stroke(SplashPath *path) {
232 SplashXPath *xPath, *xPath2;
235 printf("stroke [dash:%d] [width:%.2f]:\n",
236 state->lineDashLength, state->lineWidth);
239 if (path->length == 0) {
240 return splashErrEmptyPath;
242 xPath = new SplashXPath(path, state->flatness, gFalse);
243 if (state->lineDashLength > 0) {
244 xPath2 = makeDashedPath(xPath);
248 if (state->lineWidth <= 1) {
257 void Splash::strokeNarrow(SplashXPath *xPath) {
259 int x0, x1, x2, x3, y0, y1, x, y, t;
260 SplashCoord dx, dy, dxdy;
261 SplashClipResult clipRes;
264 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
266 x0 = splashFloor(seg->x0);
267 x1 = splashFloor(seg->x1);
268 y0 = splashFloor(seg->y0);
269 y1 = splashFloor(seg->y1);
271 // horizontal segment
274 t = x0; x0 = x1; x1 = t;
276 if ((clipRes = state->clip->testSpan(x0, x1, y0))
277 != splashClipAllOutside) {
278 drawSpan(x0, x1, y0, state->strokePattern,
279 clipRes == splashClipAllInside);
282 // segment with |dx| > |dy|
283 } else if (splashAbs(seg->dxdy) > 1) {
284 dx = seg->x1 - seg->x0;
285 dy = seg->y1 - seg->y0;
288 t = y0; y0 = y1; y1 = t;
289 t = x0; x0 = x1; x1 = t;
293 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
294 x0 <= x1 ? x1 : x0, y1))
295 != splashClipAllOutside) {
298 for (y = y0; y < y1; ++y) {
299 x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
300 drawSpan(x2, x3 - 1, y, state->strokePattern,
301 clipRes == splashClipAllInside);
304 drawSpan(x2, x1, y, state->strokePattern,
305 clipRes == splashClipAllInside);
308 for (y = y0; y < y1; ++y) {
309 x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
310 drawSpan(x3 + 1, x2, y, state->strokePattern,
311 clipRes == splashClipAllInside);
314 drawSpan(x1, x2, y, state->strokePattern,
315 clipRes == splashClipAllInside);
319 // segment with |dy| > |dx|
323 t = y0; y0 = y1; y1 = t;
325 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
326 x0 <= x1 ? x1 : x0, y1))
327 != splashClipAllOutside) {
328 for (y = y0; y <= y1; ++y) {
329 x = splashFloor(seg->x0 + (y - seg->y0) * dxdy);
330 drawPixel(x, y, state->strokePattern,
331 clipRes == splashClipAllInside);
338 void Splash::strokeWide(SplashXPath *xPath) {
339 SplashXPathSeg *seg, *seg2;
340 SplashPath *widePath;
341 SplashCoord d, dx, dy, wdx, wdy, dxPrev, dyPrev, wdxPrev, wdyPrev;
342 SplashCoord dotprod, miter;
345 dx = dy = wdx = wdy = 0; // make gcc happy
346 dxPrev = dyPrev = wdxPrev = wdyPrev = 0; // make gcc happy
348 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
350 // save the deltas for the previous segment; if this is the first
351 // segment on a subpath, compute the deltas for the last segment
352 // on the subpath (which may be used to draw a line join)
353 if (seg->flags & splashXPathFirst) {
354 for (j = i + 1, seg2 = &xPath->segs[j]; j < xPath->length; ++j, ++seg2) {
355 if (seg2->flags & splashXPathLast) {
356 d = splashDist(seg2->x0, seg2->y0, seg2->x1, seg2->y1);
358 //~ not clear what the behavior should be for joins with d==0
363 dxPrev = d * (seg2->x1 - seg2->x0);
364 dyPrev = d * (seg2->y1 - seg2->y0);
366 wdxPrev = 0.5 * state->lineWidth * dxPrev;
367 wdyPrev = 0.5 * state->lineWidth * dyPrev;
378 // compute deltas for this line segment
379 d = splashDist(seg->x0, seg->y0, seg->x1, seg->y1);
381 // we need to draw end caps on zero-length lines
382 //~ not clear what the behavior should be for splashLineCapButt with d==0
387 dx = d * (seg->x1 - seg->x0);
388 dy = d * (seg->y1 - seg->y0);
390 wdx = 0.5 * state->lineWidth * dx;
391 wdy = 0.5 * state->lineWidth * dy;
393 // initialize the path (which will be filled)
394 widePath = new SplashPath();
395 widePath->moveTo(seg->x0 - wdy, seg->y0 + wdx);
397 // draw the start cap
398 if (seg->flags & splashXPathEnd0) {
399 switch (state->lineCap) {
400 case splashLineCapButt:
401 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
403 case splashLineCapRound:
404 widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
406 case splashLineCapProjecting:
407 widePath->lineTo(seg->x0 - wdx - wdy, seg->y0 + wdx - wdy);
408 widePath->lineTo(seg->x0 - wdx + wdy, seg->y0 - wdx - wdy);
409 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
413 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
416 // draw the left side of the segment
417 widePath->lineTo(seg->x1 + wdy, seg->y1 - wdx);
420 if (seg->flags & splashXPathEnd1) {
421 switch (state->lineCap) {
422 case splashLineCapButt:
423 widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
425 case splashLineCapRound:
426 widePath->arcCWTo(seg->x1 - wdy, seg->y1 + wdx, seg->x1, seg->y1);
428 case splashLineCapProjecting:
429 widePath->lineTo(seg->x1 + wdx + wdy, seg->y1 - wdx + wdy);
430 widePath->lineTo(seg->x1 + wdx - wdy, seg->y1 + wdx + wdy);
431 widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
435 widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
438 // draw the right side of the segment
439 widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
442 fillWithPattern(widePath, gTrue, state->strokePattern);
445 // draw the line join
446 if (!(seg->flags & splashXPathEnd0)) {
448 switch (state->lineJoin) {
449 case splashLineJoinMiter:
450 dotprod = -(dx * dxPrev + dy * dyPrev);
452 widePath = new SplashPath();
453 widePath->moveTo(seg->x0, seg->y0);
454 miter = 2 / (1 - dotprod);
455 if (splashSqrt(miter) <= state->miterLimit) {
456 miter = splashSqrt(miter - 1);
457 if (dy * dxPrev > dx * dyPrev) {
458 widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
459 widePath->lineTo(seg->x0 + wdy - miter * wdx,
460 seg->y0 - wdx - miter * wdy);
461 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
463 widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
464 widePath->lineTo(seg->x0 - wdy - miter * wdx,
465 seg->y0 + wdx - miter * wdy);
466 widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
469 if (dy * dxPrev > dx * dyPrev) {
470 widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
471 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
473 widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
474 widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
479 case splashLineJoinRound:
480 widePath = new SplashPath();
481 widePath->moveTo(seg->x0 + wdy, seg->y0 - wdx);
482 widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
484 case splashLineJoinBevel:
485 widePath = new SplashPath();
486 widePath->moveTo(seg->x0, seg->y0);
487 if (dy * dxPrev > dx * dyPrev) {
488 widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
489 widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
491 widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
492 widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
497 fillWithPattern(widePath, gTrue, state->strokePattern);
504 SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) {
506 GBool lineDashStartOn, lineDashOn;
507 GBool atSegStart, atSegEnd, atDashStart, atDashEnd;
508 int lineDashStartIdx, lineDashIdx, subpathStart;
509 SplashCoord lineDashTotal, lineDashStartPhase, lineDashDist;
512 SplashCoord sx0, sy0, sx1, sy1, ax0, ay0, ax1, ay1, dist;
515 dPath = new SplashXPath();
518 for (i = 0; i < state->lineDashLength; ++i) {
519 lineDashTotal += state->lineDash[i];
521 lineDashStartPhase = state->lineDashPhase;
522 i = splashFloor(lineDashStartPhase / lineDashTotal);
523 lineDashStartPhase -= i * lineDashTotal;
524 lineDashStartOn = gTrue;
525 lineDashStartIdx = 0;
526 while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
527 lineDashStartOn = !lineDashStartOn;
528 lineDashStartPhase -= state->lineDash[lineDashStartIdx];
538 dist = splashDist(sx0, sy0, sx1, sy1);
539 lineDashOn = lineDashStartOn;
540 lineDashIdx = lineDashStartIdx;
541 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
544 subpathStart = dPath->length;
546 while (segIdx < xPath->length) {
550 if (dist <= lineDashDist) {
553 lineDashDist -= dist;
556 atDashEnd = lineDashDist == 0 || (seg->flags & splashXPathLast);
558 ax1 = sx0 + (lineDashDist / dist) * (sx1 - sx0);
559 ay1 = sy0 + (lineDashDist / dist) * (sy1 - sy0);
562 dist -= lineDashDist;
569 dPath->addSegment(ax0, ay0, ax1, ay1,
570 atDashStart, atDashEnd,
571 atDashStart, atDashEnd);
572 // end of closed subpath
574 (seg->flags & splashXPathLast) &&
575 !(seg->flags & splashXPathEnd1)) {
576 dPath->segs[subpathStart].flags &= ~splashXPathEnd0;
577 dPath->segs[dPath->length - 1].flags &= ~splashXPathEnd1;
582 lineDashOn = !lineDashOn;
583 if (++lineDashIdx == state->lineDashLength) {
586 lineDashDist = state->lineDash[lineDashIdx];
589 atDashStart = gFalse;
592 if (++segIdx < xPath->length) {
598 dist = splashDist(sx0, sy0, sx1, sy1);
599 if (seg->flags & splashXPathFirst) {
600 lineDashOn = lineDashStartOn;
601 lineDashIdx = lineDashStartIdx;
602 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
604 subpathStart = dPath->length;
616 SplashError Splash::fill(SplashPath *path, GBool eo) {
618 printf("fill [eo:%d]:\n", eo);
621 return fillWithPattern(path, eo, state->fillPattern);
624 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
625 SplashPattern *pattern) {
627 SplashXPathScanner *scanner;
628 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
629 SplashClipResult clipRes, clipRes2;
631 if (path->length == 0) {
632 return splashErrEmptyPath;
634 xPath = new SplashXPath(path, state->flatness, gTrue);
636 scanner = new SplashXPathScanner(xPath, eo);
638 // get the min and max x and y values
639 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
642 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
643 != splashClipAllOutside) {
646 for (y = yMinI; y <= yMaxI; ++y) {
647 while (scanner->getNextSpan(y, &x0, &x1)) {
648 if (clipRes == splashClipAllInside) {
649 drawSpan(x0, x1, y, pattern, gTrue);
651 clipRes2 = state->clip->testSpan(x0, x1, y);
652 drawSpan(x0, x1, y, pattern, clipRes2 == splashClipAllInside);
663 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
665 SplashXPathScanner *scanner;
666 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
667 SplashClipResult clipRes, clipRes2;
669 if (path->length == 0) {
670 return splashErrEmptyPath;
672 xPath = new SplashXPath(path, state->flatness, gTrue);
674 scanner = new SplashXPathScanner(xPath, eo);
676 // get the min and max x and y values
677 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
680 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
681 != splashClipAllOutside) {
684 for (y = yMinI; y <= yMaxI; ++y) {
685 while (scanner->getNextSpan(y, &x0, &x1)) {
686 if (clipRes == splashClipAllInside) {
687 xorSpan(x0, x1, y, state->fillPattern, gTrue);
689 clipRes2 = state->clip->testSpan(x0, x1, y);
690 xorSpan(x0, x1, y, state->fillPattern,
691 clipRes2 == splashClipAllInside);
702 void Splash::drawPixel(int x, int y, SplashColor *color, GBool noClip) {
706 if (noClip || state->clip->test(x, y)) {
707 switch (bitmap->mode) {
708 case splashModeMono1:
709 mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
711 *mono1 |= 0x80 >> (x & 7);
713 *mono1 &= ~(0x80 >> (x & 7));
716 case splashModeMono8:
717 bitmap->data.mono8[y * bitmap->width + x] = color->mono8;
720 bitmap->data.rgb8[y * bitmap->width + x] = color->rgb8;
722 case splashModeBGR8Packed:
723 bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
724 bgr8[2] = splashBGR8R(color->bgr8);
725 bgr8[1] = splashBGR8G(color->bgr8);
726 bgr8[0] = splashBGR8B(color->bgr8);
732 void Splash::drawPixel(int x, int y, SplashPattern *pattern, GBool noClip) {
737 if (noClip || state->clip->test(x, y)) {
738 color = pattern->getColor(x, y);
739 switch (bitmap->mode) {
740 case splashModeMono1:
741 mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
743 *mono1 |= 0x80 >> (x & 7);
745 *mono1 &= ~(0x80 >> (x & 7));
748 case splashModeMono8:
749 bitmap->data.mono8[y * bitmap->width + x] = color.mono8;
752 bitmap->data.rgb8[y * bitmap->width + x] = color.rgb8;
754 case splashModeBGR8Packed:
755 bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
756 bgr8[2] = splashBGR8R(color.bgr8);
757 bgr8[1] = splashBGR8G(color.bgr8);
758 bgr8[0] = splashBGR8B(color.bgr8);
764 void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern,
776 switch (bitmap->mode) {
777 case splashModeMono1:
778 mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
782 for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
783 if (noClip || state->clip->test(x0 + i, y)) {
784 color = pattern->getColor(x0 + i, y);
797 for (j = 0; j < 8 && i < n; ++i, ++j) {
798 if (noClip || state->clip->test(x0 + i, y)) {
799 color = pattern->getColor(x0 + i, y);
812 case splashModeMono8:
813 mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
814 for (i = 0; i < n; ++i) {
815 if (noClip || state->clip->test(x0 + i, y)) {
816 color = pattern->getColor(x0 + i, y);
817 *mono8 = color.mono8;
824 rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
825 for (i = 0; i < n; ++i) {
826 if (noClip || state->clip->test(x0 + i, y)) {
827 color = pattern->getColor(x0 + i, y);
834 case splashModeBGR8Packed:
835 bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
836 for (i = 0; i < n; ++i) {
837 if (noClip || state->clip->test(x0 + i, y)) {
838 color = pattern->getColor(x0 + i, y);
839 bgr8[2] = splashBGR8R(color.bgr8);
840 bgr8[1] = splashBGR8G(color.bgr8);
841 bgr8[0] = splashBGR8B(color.bgr8);
849 void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern,
861 switch (bitmap->mode) {
862 case splashModeMono1:
863 mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
867 for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
868 if (noClip || state->clip->test(x0 + i, y)) {
869 color = pattern->getColor(x0 + i, y);
880 for (j = 0; j < 8 && i < n; ++i, ++j) {
881 if (noClip || state->clip->test(x0 + i, y)) {
882 color = pattern->getColor(x0 + i, y);
893 case splashModeMono8:
894 mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
895 for (i = 0; i < n; ++i) {
896 if (noClip || state->clip->test(x0 + i, y)) {
897 color = pattern->getColor(x0 + i, y);
898 *mono8 ^= color.mono8;
905 rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
906 for (i = 0; i < n; ++i) {
907 if (noClip || state->clip->test(x0 + i, y)) {
908 color = pattern->getColor(x0 + i, y);
915 case splashModeBGR8Packed:
916 bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
917 for (i = 0; i < n; ++i) {
918 if (noClip || state->clip->test(x0 + i, y)) {
919 color = pattern->getColor(x0 + i, y);
920 bgr8[2] ^= splashBGR8R(color.bgr8);
921 bgr8[1] ^= splashBGR8G(color.bgr8);
922 bgr8[0] ^= splashBGR8B(color.bgr8);
930 void Splash::getPixel(int x, int y, SplashColor *pixel) {
933 if (y < 0 || y >= bitmap->height || x < 0 || x >= bitmap->width) {
936 switch (bitmap->mode) {
937 case splashModeMono1:
938 pixel->mono1 = (bitmap->data.mono1[y * bitmap->rowSize + (x >> 3)]
939 >> (7 - (x & 7))) & 1;
941 case splashModeMono8:
942 pixel->mono8 = bitmap->data.mono8[y * bitmap->width + x];
945 pixel->rgb8 = bitmap->data.rgb8[y * bitmap->width + x];
947 case splashModeBGR8Packed:
948 bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
949 pixel->bgr8 = splashMakeBGR8(bgr8[2], bgr8[1], bgr8[0]);
954 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
955 int c, SplashFont *font) {
956 SplashGlyphBitmap glyph;
957 int x0, y0, xFrac, yFrac;
961 printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
965 xFrac = splashFloor((x - x0) * splashFontFraction);
967 yFrac = splashFloor((y - y0) * splashFontFraction);
968 if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
969 return splashErrNoGlyph;
971 err = fillGlyph(x, y, &glyph);
972 if (glyph.freeData) {
978 SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
979 SplashGlyphBitmap *glyph) {
983 SplashMono1P *mono1Ptr;
984 SplashMono8 *mono8Ptr;
986 SplashBGR8P *bgr8Ptr;
989 SplashClipResult clipRes;
991 int x0, y0, x1, y1, xx, xx1, yy;
996 if ((clipRes = state->clip->testRect(x0 - glyph->x,
998 x0 - glyph->x + glyph->w - 1,
999 y0 - glyph->y + glyph->h - 1))
1000 != splashClipAllOutside) {
1001 noClip = clipRes == splashClipAllInside;
1006 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1007 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1010 if (noClip || state->clip->test(x1, y1)) {
1011 ialpha = 255 - alpha;
1012 fg = state->fillPattern->getColor(x1, y1);
1013 switch (bitmap->mode) {
1014 case splashModeMono1:
1015 if (alpha >= 0x80) {
1016 mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
1019 *mono1Ptr |= 0x80 >> (x1 & 7);
1021 *mono1Ptr &= ~(0x80 >> (x1 & 7));
1025 case splashModeMono8:
1026 mono8Ptr = &bitmap->data.mono8[y1 * bitmap->width + x1];
1027 bgMono8 = *mono8Ptr;
1028 // note: floor(x / 255) = x >> 8 (for 16-bit x)
1029 *mono8Ptr = (alpha * fg.mono8 + ialpha * bgMono8) >> 8;
1031 case splashModeRGB8:
1032 rgb8Ptr = &bitmap->data.rgb8[y1 * bitmap->width + x1];
1033 bgR = splashRGB8R(*rgb8Ptr);
1034 bgG = splashRGB8G(*rgb8Ptr);
1035 bgB = splashRGB8B(*rgb8Ptr);
1036 *rgb8Ptr = splashMakeRGB8((alpha * splashRGB8R(fg.rgb8) +
1038 (alpha * splashRGB8G(fg.rgb8) +
1040 (alpha * splashRGB8B(fg.rgb8) +
1041 ialpha * bgB) >> 8);
1043 case splashModeBGR8Packed:
1044 bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
1046 (alpha * splashBGR8R(fg.bgr8) + ialpha * bgr8Ptr[2]) >> 8;
1048 (alpha * splashBGR8G(fg.bgr8) + ialpha * bgr8Ptr[1]) >> 8;
1050 (alpha * splashBGR8B(fg.bgr8) + ialpha * bgr8Ptr[0]) >> 8;
1060 for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1061 for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1063 for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1065 if (noClip || state->clip->test(x1, y1)) {
1066 fg = state->fillPattern->getColor(x1, y1);
1067 switch (bitmap->mode) {
1068 case splashModeMono1:
1069 mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
1072 *mono1Ptr |= 0x80 >> (x1 & 7);
1074 *mono1Ptr &= ~(0x80 >> (x1 & 7));
1077 case splashModeMono8:
1078 bitmap->data.mono8[y1 * bitmap->width + x1] = fg.mono8;
1080 case splashModeRGB8:
1081 bitmap->data.rgb8[y1 * bitmap->width + x1] = fg.rgb8;
1083 case splashModeBGR8Packed:
1084 bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
1085 bgr8Ptr[2] = splashBGR8R(fg.bgr8);
1086 bgr8Ptr[1] = splashBGR8G(fg.bgr8);
1087 bgr8Ptr[0] = splashBGR8B(fg.bgr8);
1102 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
1103 int w, int h, SplashCoord *mat) {
1105 SplashCoord xScale, yScale, xShear, yShear;
1106 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
1107 int ulx, uly, llx, lly, urx, ury, lrx, lry;
1108 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1109 int xMin, xMax, yMin, yMax;
1110 SplashClipResult clipRes, clipRes2;
1111 int yp, yq, yt, yStep, lastYStep;
1112 int xp, xq, xt, xStep, xSrc;
1113 int k1, spanXMin, spanXMax, spanY;
1114 SplashMono1 *pixBuf;
1118 SplashColor fg, bg, pix;
1119 int x, y, x1, y1, x2, y2;
1123 printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1124 w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1127 // check for singular matrix
1128 if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1129 return splashErrSingularMatrix;
1132 // compute scale, shear, rotation, translation parameters
1133 rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1136 yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1137 xShear = -mat[3] / yScale;
1138 yShear = -mat[0] / mat[1];
1141 yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1142 xShear = mat[2] / yScale;
1143 yShear = mat[1] / mat[0];
1145 tx = splashRound(mat[4]);
1146 ty = splashRound(mat[5]);
1147 scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
1148 scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
1149 xSign = (xScale < 0) ? -1 : 1;
1150 ySign = (yScale < 0) ? -1 : 1;
1155 urx1 = xSign * (scaledWidth - 1);
1156 ury1 = splashRound(yShear * urx1);
1157 llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1158 lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
1159 lrx1 = xSign * (scaledWidth - 1) +
1160 splashRound(xShear * ySign * (scaledHeight - 1));
1161 lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
1163 ulx = tx + uly1; uly = ty - ulx1;
1164 urx = tx + ury1; ury = ty - urx1;
1165 llx = tx + lly1; lly = ty - llx1;
1166 lrx = tx + lry1; lry = ty - lrx1;
1168 ulx = tx + ulx1; uly = ty + uly1;
1169 urx = tx + urx1; ury = ty + ury1;
1170 llx = tx + llx1; lly = ty + lly1;
1171 lrx = tx + lrx1; lry = ty + lry1;
1173 xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1174 : (llx < lrx) ? llx : lrx
1175 : (urx < llx) ? (urx < lrx) ? urx : lrx
1176 : (llx < lrx) ? llx : lrx;
1177 xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1178 : (llx > lrx) ? llx : lrx
1179 : (urx > llx) ? (urx > lrx) ? urx : lrx
1180 : (llx > lrx) ? llx : lrx;
1181 yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1182 : (lly < lry) ? lly : lry
1183 : (ury < lly) ? (ury < lry) ? ury : lry
1184 : (lly < lry) ? lly : lry;
1185 yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1186 : (lly > lry) ? lly : lry
1187 : (ury > lly) ? (ury > lry) ? ury : lry
1188 : (lly > lry) ? lly : lry;
1189 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
1191 // compute Bresenham parameters for x and y scaling
1192 yp = h / scaledHeight;
1193 yq = h % scaledHeight;
1194 xp = w / scaledWidth;
1195 xq = w % scaledWidth;
1197 // allocate pixel buffer
1198 pixBuf = (SplashMono1 *)gmalloc((yp + 1) * w * sizeof(SplashMono1));
1200 // init y scale Bresenham
1204 for (y = 0; y < scaledHeight; ++y) {
1206 // y scale Bresenham
1209 if (yt >= scaledHeight) {
1214 // read row(s) from image
1215 n = (yp > 0) ? yStep : lastYStep;
1218 for (i = 0; i < n; ++i) {
1219 for (j = 0; j < w; ++j) {
1220 (*src)(srcData, p++);
1226 // loop-invariant constants
1227 k1 = splashRound(xShear * ySign * y);
1230 if (clipRes != splashClipAllInside &&
1232 splashRound(yShear * k1) ==
1233 splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
1236 spanXMax = spanXMin + (scaledWidth - 1);
1239 spanXMin = spanXMax - (scaledWidth - 1);
1241 spanY = ty + ySign * y + splashRound(xShear * ySign * y);
1242 clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1243 if (clipRes2 == splashClipAllOutside) {
1250 // init x scale Bresenham
1254 for (x = 0; x < scaledWidth; ++x) {
1256 // x scale Bresenham
1259 if (xt >= scaledWidth) {
1265 x1 = xSign * x + k1;
1268 y1 = ySign * y + splashRound(yShear * x1);
1279 // compute the alpha value for (x,y) after the x and y scaling
1281 n = yStep > 0 ? yStep : 1;
1282 m = xStep > 0 ? xStep : 1;
1285 for (i = 0; i < n; ++i) {
1286 for (j = 0; j < m; ++j) {
1292 // blend fill color with background
1294 fg = state->fillPattern->getColor(tx + x2, ty + y2);
1295 if (pixAcc == n * m) {
1298 getPixel(tx + x2, ty + y2, &bg);
1299 alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m);
1300 switch (bitmap->mode) {
1301 case splashModeMono1:
1302 pix.mono1 = splashRound(alpha * fg.mono1 +
1303 (1 - alpha) * bg.mono1);
1305 case splashModeMono8:
1306 pix.mono8 = splashRound(alpha * fg.mono8 +
1307 (1 - alpha) * bg.mono8);
1309 case splashModeRGB8:
1310 pix.rgb8 = splashMakeRGB8(
1311 splashRound(alpha * splashRGB8R(fg.rgb8) +
1312 (1 - alpha) * splashRGB8R(bg.rgb8)),
1313 splashRound(alpha * splashRGB8G(fg.rgb8) +
1314 (1 - alpha) * splashRGB8G(bg.rgb8)),
1315 splashRound(alpha * splashRGB8B(fg.rgb8) +
1316 (1 - alpha) * splashRGB8B(bg.rgb8)));
1317 case splashModeBGR8Packed:
1318 pix.bgr8 = splashMakeBGR8(
1319 splashRound(alpha * splashBGR8R(fg.bgr8) +
1320 (1 - alpha) * splashBGR8R(bg.bgr8)),
1321 splashRound(alpha * splashBGR8G(fg.bgr8) +
1322 (1 - alpha) * splashBGR8G(bg.bgr8)),
1323 splashRound(alpha * splashBGR8B(fg.bgr8) +
1324 (1 - alpha) * splashBGR8B(bg.bgr8)));
1328 drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
1331 // x scale Bresenham
1342 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
1343 SplashColorMode srcMode,
1344 int w, int h, SplashCoord *mat) {
1345 GBool ok, rot, halftone;
1346 SplashCoord xScale, yScale, xShear, yShear;
1347 int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
1348 int ulx, uly, llx, lly, urx, ury, lrx, lry;
1349 int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1350 int xMin, xMax, yMin, yMax;
1351 SplashClipResult clipRes, clipRes2;
1352 int yp, yq, yt, yStep, lastYStep;
1353 int xp, xq, xt, xStep, xSrc;
1354 int k1, spanXMin, spanXMax, spanY;
1355 SplashColor *pixBuf, *p;
1356 Guchar *alphaBuf, *q;
1358 SplashCoord pixAcc[splashMaxColorComps];
1360 SplashCoord pixMul, alphaMul, alpha;
1361 int x, y, x1, y1, x2, y2;
1365 printf("drawImage: srcMode=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1366 srcMode, w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1369 // check color modes
1370 ok = gFalse; // make gcc happy
1371 switch (bitmap->mode) {
1372 case splashModeMono1:
1373 ok = srcMode == splashModeMono1 || srcMode == splashModeMono8;
1375 case splashModeMono8:
1376 ok = srcMode == splashModeMono8;
1378 case splashModeRGB8:
1379 ok = srcMode == splashModeRGB8;
1381 case splashModeBGR8Packed:
1382 ok = srcMode == splashModeBGR8Packed;
1386 return splashErrModeMismatch;
1388 halftone = bitmap->mode == splashModeMono1 && srcMode == splashModeMono8;
1390 // check for singular matrix
1391 if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1392 return splashErrSingularMatrix;
1395 // compute scale, shear, rotation, translation parameters
1396 rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1399 yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1400 xShear = -mat[3] / yScale;
1401 yShear = -mat[0] / mat[1];
1404 yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1405 xShear = mat[2] / yScale;
1406 yShear = mat[1] / mat[0];
1408 tx = splashRound(mat[4]);
1409 ty = splashRound(mat[5]);
1410 scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
1411 scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
1412 xSign = (xScale < 0) ? -1 : 1;
1413 ySign = (yScale < 0) ? -1 : 1;
1418 urx1 = xSign * (scaledWidth - 1);
1419 ury1 = splashRound(yShear * urx1);
1420 llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1421 lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
1422 lrx1 = xSign * (scaledWidth - 1) +
1423 splashRound(xShear * ySign * (scaledHeight - 1));
1424 lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
1426 ulx = tx + uly1; uly = ty - ulx1;
1427 urx = tx + ury1; ury = ty - urx1;
1428 llx = tx + lly1; lly = ty - llx1;
1429 lrx = tx + lry1; lry = ty - lrx1;
1431 ulx = tx + ulx1; uly = ty + uly1;
1432 urx = tx + urx1; ury = ty + ury1;
1433 llx = tx + llx1; lly = ty + lly1;
1434 lrx = tx + lrx1; lry = ty + lry1;
1436 xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1437 : (llx < lrx) ? llx : lrx
1438 : (urx < llx) ? (urx < lrx) ? urx : lrx
1439 : (llx < lrx) ? llx : lrx;
1440 xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1441 : (llx > lrx) ? llx : lrx
1442 : (urx > llx) ? (urx > lrx) ? urx : lrx
1443 : (llx > lrx) ? llx : lrx;
1444 yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1445 : (lly < lry) ? lly : lry
1446 : (ury < lly) ? (ury < lry) ? ury : lry
1447 : (lly < lry) ? lly : lry;
1448 yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1449 : (lly > lry) ? lly : lry
1450 : (ury > lly) ? (ury > lry) ? ury : lry
1451 : (lly > lry) ? lly : lry;
1452 if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax))
1453 == splashClipAllOutside) {
1457 // compute Bresenham parameters for x and y scaling
1458 yp = h / scaledHeight;
1459 yq = h % scaledHeight;
1460 xp = w / scaledWidth;
1461 xq = w % scaledWidth;
1463 // allocate pixel buffer
1464 pixBuf = (SplashColor *)gmalloc((yp + 1) * w * sizeof(SplashColor));
1465 alphaBuf = (Guchar *)gmalloc((yp + 1) * w * sizeof(Guchar));
1467 // init y scale Bresenham
1471 for (y = 0; y < scaledHeight; ++y) {
1473 // y scale Bresenham
1476 if (yt >= scaledHeight) {
1481 // read row(s) from image
1482 n = (yp > 0) ? yStep : lastYStep;
1486 for (i = 0; i < n; ++i) {
1487 for (j = 0; j < w; ++j) {
1488 (*src)(srcData, p++, q++);
1494 // loop-invariant constants
1495 k1 = splashRound(xShear * ySign * y);
1498 if (clipRes != splashClipAllInside &&
1500 splashRound(yShear * k1) ==
1501 splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
1504 spanXMax = spanXMin + (scaledWidth - 1);
1507 spanXMin = spanXMax - (scaledWidth - 1);
1509 spanY = ty + ySign * y + splashRound(xShear * ySign * y);
1510 clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1511 if (clipRes2 == splashClipAllOutside) {
1518 // init x scale Bresenham
1522 for (x = 0; x < scaledWidth; ++x) {
1524 // x scale Bresenham
1527 if (xt >= scaledWidth) {
1533 x1 = xSign * x + k1;
1536 y1 = ySign * y + splashRound(yShear * x1);
1547 // compute the filtered pixel at (x,y) after the x and y scaling
1549 n = yStep > 0 ? yStep : 1;
1550 m = xStep > 0 ? xStep : 1;
1552 q = alphaBuf + xSrc;
1553 for (i = 0; i < splashMaxColorComps; ++i) {
1557 for (i = 0; i < n; ++i) {
1558 for (j = 0; j < m; ++j) {
1560 case splashModeMono1:
1561 pixAcc[0] += p->mono1;
1563 case splashModeMono8:
1564 pixAcc[0] += p->mono8;
1566 case splashModeRGB8:
1567 pixAcc[0] += splashRGB8R(p->rgb8);
1568 pixAcc[1] += splashRGB8G(p->rgb8);
1569 pixAcc[2] += splashRGB8B(p->rgb8);
1571 case splashModeBGR8Packed:
1572 pixAcc[0] += splashBGR8R(p->bgr8);
1573 pixAcc[1] += splashBGR8G(p->bgr8);
1574 pixAcc[2] += splashBGR8B(p->bgr8);
1583 alphaMul = 1 / (SplashCoord)(n * m);
1585 pixMul = (SplashCoord)alphaMul / 256.0;
1589 alpha = (SplashCoord)alphaAcc * alphaMul;
1591 //~ this should blend if 0 < alpha < 1
1594 // mono8 -> mono1 conversion, with halftoning
1596 pix.mono1 = state->screen->test(tx + x2, ty + y2,
1597 pixAcc[0] * pixMul);
1599 // no conversion, no halftoning
1601 switch (bitmap->mode) {
1602 case splashModeMono1:
1603 pix.mono1 = splashRound(pixAcc[0] * pixMul);
1605 case splashModeMono8:
1606 pix.mono8 = splashRound(pixAcc[0] * pixMul);
1608 case splashModeRGB8:
1609 pix.rgb8 = splashMakeRGB8(splashRound(pixAcc[0] * pixMul),
1610 splashRound(pixAcc[1] * pixMul),
1611 splashRound(pixAcc[2] * pixMul));
1613 case splashModeBGR8Packed:
1614 pix.bgr8 = splashMakeBGR8(splashRound(pixAcc[0] * pixMul),
1615 splashRound(pixAcc[1] * pixMul),
1616 splashRound(pixAcc[2] * pixMul));
1622 drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
1625 // x scale Bresenham
1636 void Splash::dumpPath(SplashPath *path) {
1639 for (i = 0; i < path->length; ++i) {
1640 printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s%s\n",
1641 i, path->pts[i].x, path->pts[i].y,
1642 (path->flags[i] & splashPathFirst) ? " first" : "",
1643 (path->flags[i] & splashPathLast) ? " last" : "",
1644 (path->flags[i] & splashPathClosed) ? " closed" : "",
1645 (path->flags[i] & splashPathCurve) ? " curve" : "",
1646 (path->flags[i] & splashPathArcCW) ? " arcCW" : "");