Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pdf/splash/Splash.cc
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/splash/Splash.cc')
-rw-r--r--pdf/splash/Splash.cc1648
1 files changed, 1648 insertions, 0 deletions
diff --git a/pdf/splash/Splash.cc b/pdf/splash/Splash.cc
new file mode 100644
index 0000000..f86156d
--- /dev/null
+++ b/pdf/splash/Splash.cc
@@ -0,0 +1,1648 @@
+//========================================================================
+//
+// Splash.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "SplashErrorCodes.h"
+#include "SplashMath.h"
+#include "SplashBitmap.h"
+#include "SplashState.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashClip.h"
+#include "SplashFont.h"
+#include "SplashGlyphBitmap.h"
+#include "Splash.h"
+
+//------------------------------------------------------------------------
+// Splash
+//------------------------------------------------------------------------
+
+Splash::Splash(SplashBitmap *bitmapA) {
+ bitmap = bitmapA;
+ state = new SplashState(bitmap->width, bitmap->height);
+ debugMode = gFalse;
+}
+
+Splash::~Splash() {
+ while (state->next) {
+ restoreState();
+ }
+ delete state;
+}
+
+//------------------------------------------------------------------------
+// state read
+//------------------------------------------------------------------------
+
+
+SplashPattern *Splash::getStrokePattern() {
+ return state->strokePattern;
+}
+
+SplashPattern *Splash::getFillPattern() {
+ return state->fillPattern;
+}
+
+SplashScreen *Splash::getScreen() {
+ return state->screen;
+}
+
+SplashCoord Splash::getLineWidth() {
+ return state->lineWidth;
+}
+
+int Splash::getLineCap() {
+ return state->lineCap;
+}
+
+int Splash::getLineJoin() {
+ return state->lineJoin;
+}
+
+SplashCoord Splash::getMiterLimit() {
+ return state->miterLimit;
+}
+
+SplashCoord Splash::getFlatness() {
+ return state->flatness;
+}
+
+SplashCoord *Splash::getLineDash() {
+ return state->lineDash;
+}
+
+int Splash::getLineDashLength() {
+ return state->lineDashLength;
+}
+
+SplashCoord Splash::getLineDashPhase() {
+ return state->lineDashPhase;
+}
+
+SplashClip *Splash::getClip() {
+ return state->clip;
+}
+
+//------------------------------------------------------------------------
+// state write
+//------------------------------------------------------------------------
+
+void Splash::setStrokePattern(SplashPattern *strokePattern) {
+ state->setStrokePattern(strokePattern);
+}
+
+void Splash::setFillPattern(SplashPattern *fillPattern) {
+ state->setFillPattern(fillPattern);
+}
+
+void Splash::setScreen(SplashScreen *screen) {
+ state->setScreen(screen);
+}
+
+void Splash::setLineWidth(SplashCoord lineWidth) {
+ state->lineWidth = lineWidth;
+}
+
+void Splash::setLineCap(int lineCap) {
+ state->lineCap = lineCap;
+}
+
+void Splash::setLineJoin(int lineJoin) {
+ state->lineJoin = lineJoin;
+}
+
+void Splash::setMiterLimit(SplashCoord miterLimit) {
+ state->miterLimit = miterLimit;
+}
+
+void Splash::setFlatness(SplashCoord flatness) {
+ if (flatness < 1) {
+ state->flatness = 1;
+ } else {
+ state->flatness = flatness;
+ }
+}
+
+void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
+ SplashCoord lineDashPhase) {
+ state->setLineDash(lineDash, lineDashLength, lineDashPhase);
+}
+
+void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ state->clip->resetToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ return state->clip->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
+ return state->clip->clipToPath(path, state->flatness, eo);
+}
+
+//------------------------------------------------------------------------
+// state save/restore
+//------------------------------------------------------------------------
+
+void Splash::saveState() {
+ SplashState *newState;
+
+ newState = state->copy();
+ newState->next = state;
+ state = newState;
+}
+
+SplashError Splash::restoreState() {
+ SplashState *oldState;
+
+ if (!state->next) {
+ return splashErrNoSave;
+ }
+ oldState = state;
+ state = state->next;
+ delete oldState;
+ return splashOk;
+}
+
+//------------------------------------------------------------------------
+// drawing operations
+//------------------------------------------------------------------------
+
+void Splash::clear(SplashColor color) {
+ SplashMono1P *mono1;
+ SplashMono8 *mono8;
+ SplashRGB8 *rgb8;
+ SplashBGR8P *bgr8line, *bgr8;
+ SplashMono1 data;
+ int n, i, x, y;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ n = ((bitmap->width + 7) >> 3) * bitmap->height;
+ data = color.mono1 ? 0xff : 0x00;
+ for (i = 0, mono1 = bitmap->data.mono1; i < n; ++i, ++mono1) {
+ *mono1 = data;
+ }
+ break;
+ case splashModeMono8:
+ n = bitmap->width * bitmap->height;
+ for (i = 0, mono8 = bitmap->data.mono8; i < n; ++i, ++mono8) {
+ *mono8 = color.mono8;
+ }
+ break;
+ case splashModeRGB8:
+ n = bitmap->width * bitmap->height;
+ for (i = 0, rgb8 = bitmap->data.rgb8; i < n; ++i, ++rgb8) {
+ *rgb8 = color.rgb8;
+ }
+ break;
+ case splashModeBGR8Packed:
+ bgr8line = bitmap->data.bgr8;
+ for (y = 0; y < bitmap->height; ++y) {
+ bgr8 = bgr8line;
+ for (x = 0; x < bitmap->width; ++x) {
+ bgr8[2] = splashBGR8R(color.bgr8);
+ bgr8[1] = splashBGR8G(color.bgr8);
+ bgr8[0] = splashBGR8B(color.bgr8);
+ bgr8 += 3;
+ }
+ bgr8line += bitmap->rowSize;
+ }
+ break;
+ }
+}
+
+SplashError Splash::stroke(SplashPath *path) {
+ SplashXPath *xPath, *xPath2;
+
+ if (debugMode) {
+ printf("stroke [dash:%d] [width:%.2f]:\n",
+ state->lineDashLength, state->lineWidth);
+ dumpPath(path);
+ }
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ xPath = new SplashXPath(path, state->flatness, gFalse);
+ if (state->lineDashLength > 0) {
+ xPath2 = makeDashedPath(xPath);
+ delete xPath;
+ xPath = xPath2;
+ }
+ if (state->lineWidth <= 1) {
+ strokeNarrow(xPath);
+ } else {
+ strokeWide(xPath);
+ }
+ delete xPath;
+ return splashOk;
+}
+
+void Splash::strokeNarrow(SplashXPath *xPath) {
+ SplashXPathSeg *seg;
+ int x0, x1, x2, x3, y0, y1, x, y, t;
+ SplashCoord dx, dy, dxdy;
+ SplashClipResult clipRes;
+ int i;
+
+ for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+
+ x0 = splashFloor(seg->x0);
+ x1 = splashFloor(seg->x1);
+ y0 = splashFloor(seg->y0);
+ y1 = splashFloor(seg->y1);
+
+ // horizontal segment
+ if (y0 == y1) {
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if ((clipRes = state->clip->testSpan(x0, x1, y0))
+ != splashClipAllOutside) {
+ drawSpan(x0, x1, y0, state->strokePattern,
+ clipRes == splashClipAllInside);
+ }
+
+ // segment with |dx| > |dy|
+ } else if (splashAbs(seg->dxdy) > 1) {
+ dx = seg->x1 - seg->x0;
+ dy = seg->y1 - seg->y0;
+ dxdy = seg->dxdy;
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ t = x0; x0 = x1; x1 = t;
+ dx = -dx;
+ dy = -dy;
+ }
+ if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+ x0 <= x1 ? x1 : x0, y1))
+ != splashClipAllOutside) {
+ if (dx > 0) {
+ x2 = x0;
+ for (y = y0; y < y1; ++y) {
+ x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
+ drawSpan(x2, x3 - 1, y, state->strokePattern,
+ clipRes == splashClipAllInside);
+ x2 = x3;
+ }
+ drawSpan(x2, x1, y, state->strokePattern,
+ clipRes == splashClipAllInside);
+ } else {
+ x2 = x0;
+ for (y = y0; y < y1; ++y) {
+ x3 = splashFloor(seg->x0 + (y + 1 - seg->y0) * dxdy);
+ drawSpan(x3 + 1, x2, y, state->strokePattern,
+ clipRes == splashClipAllInside);
+ x2 = x3;
+ }
+ drawSpan(x1, x2, y, state->strokePattern,
+ clipRes == splashClipAllInside);
+ }
+ }
+
+ // segment with |dy| > |dx|
+ } else {
+ dxdy = seg->dxdy;
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+ x0 <= x1 ? x1 : x0, y1))
+ != splashClipAllOutside) {
+ for (y = y0; y <= y1; ++y) {
+ x = splashFloor(seg->x0 + (y - seg->y0) * dxdy);
+ drawPixel(x, y, state->strokePattern,
+ clipRes == splashClipAllInside);
+ }
+ }
+ }
+ }
+}
+
+void Splash::strokeWide(SplashXPath *xPath) {
+ SplashXPathSeg *seg, *seg2;
+ SplashPath *widePath;
+ SplashCoord d, dx, dy, wdx, wdy, dxPrev, dyPrev, wdxPrev, wdyPrev;
+ SplashCoord dotprod, miter;
+ int i, j;
+
+ dx = dy = wdx = wdy = 0; // make gcc happy
+ dxPrev = dyPrev = wdxPrev = wdyPrev = 0; // make gcc happy
+
+ for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+
+ // save the deltas for the previous segment; if this is the first
+ // segment on a subpath, compute the deltas for the last segment
+ // on the subpath (which may be used to draw a line join)
+ if (seg->flags & splashXPathFirst) {
+ for (j = i + 1, seg2 = &xPath->segs[j]; j < xPath->length; ++j, ++seg2) {
+ if (seg2->flags & splashXPathLast) {
+ d = splashDist(seg2->x0, seg2->y0, seg2->x1, seg2->y1);
+ if (d == 0) {
+ //~ not clear what the behavior should be for joins with d==0
+ dxPrev = 0;
+ dyPrev = 1;
+ } else {
+ d = 1 / d;
+ dxPrev = d * (seg2->x1 - seg2->x0);
+ dyPrev = d * (seg2->y1 - seg2->y0);
+ }
+ wdxPrev = 0.5 * state->lineWidth * dxPrev;
+ wdyPrev = 0.5 * state->lineWidth * dyPrev;
+ break;
+ }
+ }
+ } else {
+ dxPrev = dx;
+ dyPrev = dy;
+ wdxPrev = wdx;
+ wdyPrev = wdy;
+ }
+
+ // compute deltas for this line segment
+ d = splashDist(seg->x0, seg->y0, seg->x1, seg->y1);
+ if (d == 0) {
+ // we need to draw end caps on zero-length lines
+ //~ not clear what the behavior should be for splashLineCapButt with d==0
+ dx = 0;
+ dy = 1;
+ } else {
+ d = 1 / d;
+ dx = d * (seg->x1 - seg->x0);
+ dy = d * (seg->y1 - seg->y0);
+ }
+ wdx = 0.5 * state->lineWidth * dx;
+ wdy = 0.5 * state->lineWidth * dy;
+
+ // initialize the path (which will be filled)
+ widePath = new SplashPath();
+ widePath->moveTo(seg->x0 - wdy, seg->y0 + wdx);
+
+ // draw the start cap
+ if (seg->flags & splashXPathEnd0) {
+ switch (state->lineCap) {
+ case splashLineCapButt:
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ break;
+ case splashLineCapRound:
+ widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
+ break;
+ case splashLineCapProjecting:
+ widePath->lineTo(seg->x0 - wdx - wdy, seg->y0 + wdx - wdy);
+ widePath->lineTo(seg->x0 - wdx + wdy, seg->y0 - wdx - wdy);
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ break;
+ }
+ } else {
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ }
+
+ // draw the left side of the segment
+ widePath->lineTo(seg->x1 + wdy, seg->y1 - wdx);
+
+ // draw the end cap
+ if (seg->flags & splashXPathEnd1) {
+ switch (state->lineCap) {
+ case splashLineCapButt:
+ widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+ break;
+ case splashLineCapRound:
+ widePath->arcCWTo(seg->x1 - wdy, seg->y1 + wdx, seg->x1, seg->y1);
+ break;
+ case splashLineCapProjecting:
+ widePath->lineTo(seg->x1 + wdx + wdy, seg->y1 - wdx + wdy);
+ widePath->lineTo(seg->x1 + wdx - wdy, seg->y1 + wdx + wdy);
+ widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+ break;
+ }
+ } else {
+ widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx);
+ }
+
+ // draw the right side of the segment
+ widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+
+ // fill the segment
+ fillWithPattern(widePath, gTrue, state->strokePattern);
+ delete widePath;
+
+ // draw the line join
+ if (!(seg->flags & splashXPathEnd0)) {
+ widePath = NULL;
+ switch (state->lineJoin) {
+ case splashLineJoinMiter:
+ dotprod = -(dx * dxPrev + dy * dyPrev);
+ if (dotprod != 1) {
+ widePath = new SplashPath();
+ widePath->moveTo(seg->x0, seg->y0);
+ miter = 2 / (1 - dotprod);
+ if (splashSqrt(miter) <= state->miterLimit) {
+ miter = splashSqrt(miter - 1);
+ if (dy * dxPrev > dx * dyPrev) {
+ widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+ widePath->lineTo(seg->x0 + wdy - miter * wdx,
+ seg->y0 - wdx - miter * wdy);
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ } else {
+ widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+ widePath->lineTo(seg->x0 - wdy - miter * wdx,
+ seg->y0 + wdx - miter * wdy);
+ widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+ }
+ } else {
+ if (dy * dxPrev > dx * dyPrev) {
+ widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ } else {
+ widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+ widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+ }
+ }
+ }
+ break;
+ case splashLineJoinRound:
+ widePath = new SplashPath();
+ widePath->moveTo(seg->x0 + wdy, seg->y0 - wdx);
+ widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0);
+ break;
+ case splashLineJoinBevel:
+ widePath = new SplashPath();
+ widePath->moveTo(seg->x0, seg->y0);
+ if (dy * dxPrev > dx * dyPrev) {
+ widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev);
+ widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx);
+ } else {
+ widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev);
+ widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx);
+ }
+ break;
+ }
+ if (widePath) {
+ fillWithPattern(widePath, gTrue, state->strokePattern);
+ delete widePath;
+ }
+ }
+ }
+}
+
+SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) {
+ SplashXPath *dPath;
+ GBool lineDashStartOn, lineDashOn;
+ GBool atSegStart, atSegEnd, atDashStart, atDashEnd;
+ int lineDashStartIdx, lineDashIdx, subpathStart;
+ SplashCoord lineDashTotal, lineDashStartPhase, lineDashDist;
+ int segIdx;
+ SplashXPathSeg *seg;
+ SplashCoord sx0, sy0, sx1, sy1, ax0, ay0, ax1, ay1, dist;
+ int i;
+
+ dPath = new SplashXPath();
+
+ lineDashTotal = 0;
+ for (i = 0; i < state->lineDashLength; ++i) {
+ lineDashTotal += state->lineDash[i];
+ }
+ lineDashStartPhase = state->lineDashPhase;
+ i = splashFloor(lineDashStartPhase / lineDashTotal);
+ lineDashStartPhase -= i * lineDashTotal;
+ lineDashStartOn = gTrue;
+ lineDashStartIdx = 0;
+ while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
+ lineDashStartOn = !lineDashStartOn;
+ lineDashStartPhase -= state->lineDash[lineDashStartIdx];
+ ++lineDashStartIdx;
+ }
+
+ segIdx = 0;
+ seg = xPath->segs;
+ sx0 = seg->x0;
+ sy0 = seg->y0;
+ sx1 = seg->x1;
+ sy1 = seg->y1;
+ dist = splashDist(sx0, sy0, sx1, sy1);
+ lineDashOn = lineDashStartOn;
+ lineDashIdx = lineDashStartIdx;
+ lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+ atSegStart = gTrue;
+ atDashStart = gTrue;
+ subpathStart = dPath->length;
+
+ while (segIdx < xPath->length) {
+
+ ax0 = sx0;
+ ay0 = sy0;
+ if (dist <= lineDashDist) {
+ ax1 = sx1;
+ ay1 = sy1;
+ lineDashDist -= dist;
+ dist = 0;
+ atSegEnd = gTrue;
+ atDashEnd = lineDashDist == 0 || (seg->flags & splashXPathLast);
+ } else {
+ ax1 = sx0 + (lineDashDist / dist) * (sx1 - sx0);
+ ay1 = sy0 + (lineDashDist / dist) * (sy1 - sy0);
+ sx0 = ax1;
+ sy0 = ay1;
+ dist -= lineDashDist;
+ lineDashDist = 0;
+ atSegEnd = gFalse;
+ atDashEnd = gTrue;
+ }
+
+ if (lineDashOn) {
+ dPath->addSegment(ax0, ay0, ax1, ay1,
+ atDashStart, atDashEnd,
+ atDashStart, atDashEnd);
+ // end of closed subpath
+ if (atSegEnd &&
+ (seg->flags & splashXPathLast) &&
+ !(seg->flags & splashXPathEnd1)) {
+ dPath->segs[subpathStart].flags &= ~splashXPathEnd0;
+ dPath->segs[dPath->length - 1].flags &= ~splashXPathEnd1;
+ }
+ }
+
+ if (atDashEnd) {
+ lineDashOn = !lineDashOn;
+ if (++lineDashIdx == state->lineDashLength) {
+ lineDashIdx = 0;
+ }
+ lineDashDist = state->lineDash[lineDashIdx];
+ atDashStart = gTrue;
+ } else {
+ atDashStart = gFalse;
+ }
+ if (atSegEnd) {
+ if (++segIdx < xPath->length) {
+ ++seg;
+ sx0 = seg->x0;
+ sy0 = seg->y0;
+ sx1 = seg->x1;
+ sy1 = seg->y1;
+ dist = splashDist(sx0, sy0, sx1, sy1);
+ if (seg->flags & splashXPathFirst) {
+ lineDashOn = lineDashStartOn;
+ lineDashIdx = lineDashStartIdx;
+ lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+ atDashStart = gTrue;
+ subpathStart = dPath->length;
+ }
+ }
+ atSegStart = gTrue;
+ } else {
+ atSegStart = gFalse;
+ }
+ }
+
+ return dPath;
+}
+
+SplashError Splash::fill(SplashPath *path, GBool eo) {
+ if (debugMode) {
+ printf("fill [eo:%d]:\n", eo);
+ dumpPath(path);
+ }
+ return fillWithPattern(path, eo, state->fillPattern);
+}
+
+SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
+ SplashPattern *pattern) {
+ SplashXPath *xPath;
+ SplashXPathScanner *scanner;
+ int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
+ SplashClipResult clipRes, clipRes2;
+
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ xPath = new SplashXPath(path, state->flatness, gTrue);
+ xPath->sort();
+ scanner = new SplashXPathScanner(xPath, eo);
+
+ // get the min and max x and y values
+ scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+
+ // check clipping
+ if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+ != splashClipAllOutside) {
+
+ // draw the spans
+ for (y = yMinI; y <= yMaxI; ++y) {
+ while (scanner->getNextSpan(y, &x0, &x1)) {
+ if (clipRes == splashClipAllInside) {
+ drawSpan(x0, x1, y, pattern, gTrue);
+ } else {
+ clipRes2 = state->clip->testSpan(x0, x1, y);
+ drawSpan(x0, x1, y, pattern, clipRes2 == splashClipAllInside);
+ }
+ }
+ }
+ }
+
+ delete scanner;
+ delete xPath;
+ return splashOk;
+}
+
+SplashError Splash::xorFill(SplashPath *path, GBool eo) {
+ SplashXPath *xPath;
+ SplashXPathScanner *scanner;
+ int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
+ SplashClipResult clipRes, clipRes2;
+
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ xPath = new SplashXPath(path, state->flatness, gTrue);
+ xPath->sort();
+ scanner = new SplashXPathScanner(xPath, eo);
+
+ // get the min and max x and y values
+ scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+
+ // check clipping
+ if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+ != splashClipAllOutside) {
+
+ // draw the spans
+ for (y = yMinI; y <= yMaxI; ++y) {
+ while (scanner->getNextSpan(y, &x0, &x1)) {
+ if (clipRes == splashClipAllInside) {
+ xorSpan(x0, x1, y, state->fillPattern, gTrue);
+ } else {
+ clipRes2 = state->clip->testSpan(x0, x1, y);
+ xorSpan(x0, x1, y, state->fillPattern,
+ clipRes2 == splashClipAllInside);
+ }
+ }
+ }
+ }
+
+ delete scanner;
+ delete xPath;
+ return splashOk;
+}
+
+void Splash::drawPixel(int x, int y, SplashColor *color, GBool noClip) {
+ SplashMono1P *mono1;
+ SplashBGR8P *bgr8;
+
+ if (noClip || state->clip->test(x, y)) {
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
+ if (color->mono1) {
+ *mono1 |= 0x80 >> (x & 7);
+ } else {
+ *mono1 &= ~(0x80 >> (x & 7));
+ }
+ break;
+ case splashModeMono8:
+ bitmap->data.mono8[y * bitmap->width + x] = color->mono8;
+ break;
+ case splashModeRGB8:
+ bitmap->data.rgb8[y * bitmap->width + x] = color->rgb8;
+ break;
+ case splashModeBGR8Packed:
+ bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+ bgr8[2] = splashBGR8R(color->bgr8);
+ bgr8[1] = splashBGR8G(color->bgr8);
+ bgr8[0] = splashBGR8B(color->bgr8);
+ break;
+ }
+ }
+}
+
+void Splash::drawPixel(int x, int y, SplashPattern *pattern, GBool noClip) {
+ SplashColor color;
+ SplashMono1P *mono1;
+ SplashBGR8P *bgr8;
+
+ if (noClip || state->clip->test(x, y)) {
+ color = pattern->getColor(x, y);
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x >> 3)];
+ if (color.mono1) {
+ *mono1 |= 0x80 >> (x & 7);
+ } else {
+ *mono1 &= ~(0x80 >> (x & 7));
+ }
+ break;
+ case splashModeMono8:
+ bitmap->data.mono8[y * bitmap->width + x] = color.mono8;
+ break;
+ case splashModeRGB8:
+ bitmap->data.rgb8[y * bitmap->width + x] = color.rgb8;
+ break;
+ case splashModeBGR8Packed:
+ bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+ bgr8[2] = splashBGR8R(color.bgr8);
+ bgr8[1] = splashBGR8G(color.bgr8);
+ bgr8[0] = splashBGR8B(color.bgr8);
+ break;
+ }
+ }
+}
+
+void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern,
+ GBool noClip) {
+ SplashColor color;
+ SplashMono1P *mono1;
+ SplashMono8 *mono8;
+ SplashRGB8 *rgb8;
+ SplashBGR8P *bgr8;
+ SplashMono1 mask1;
+ int i, j, n;
+
+ n = x1 - x0 + 1;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
+ i = 0;
+ if ((j = x0 & 7)) {
+ mask1 = 0x80 >> j;
+ for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ if (color.mono1) {
+ *mono1 |= mask1;
+ } else {
+ *mono1 &= ~mask1;
+ }
+ }
+ mask1 >>= 1;
+ }
+ ++mono1;
+ }
+ while (i < n) {
+ mask1 = 0x80;
+ for (j = 0; j < 8 && i < n; ++i, ++j) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ if (color.mono1) {
+ *mono1 |= mask1;
+ } else {
+ *mono1 &= ~mask1;
+ }
+ }
+ mask1 >>= 1;
+ }
+ ++mono1;
+ }
+ break;
+
+ case splashModeMono8:
+ mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ *mono8 = color.mono8;
+ }
+ ++mono8;
+ }
+ break;
+
+ case splashModeRGB8:
+ rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ *rgb8 = color.rgb8;
+ }
+ ++rgb8;
+ }
+ break;
+
+ case splashModeBGR8Packed:
+ bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ bgr8[2] = splashBGR8R(color.bgr8);
+ bgr8[1] = splashBGR8G(color.bgr8);
+ bgr8[0] = splashBGR8B(color.bgr8);
+ }
+ bgr8 += 3;
+ }
+ break;
+ }
+}
+
+void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern,
+ GBool noClip) {
+ SplashColor color;
+ SplashMono1P *mono1;
+ SplashMono8 *mono8;
+ SplashRGB8 *rgb8;
+ SplashBGR8P *bgr8;
+ SplashMono1 mask1;
+ int i, j, n;
+
+ n = x1 - x0 + 1;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono1 = &bitmap->data.mono8[y * bitmap->rowSize + (x0 >> 3)];
+ i = 0;
+ if ((j = x0 & 7)) {
+ mask1 = 0x80 >> j;
+ for (j = x0 & 7; j < 8 && i < n; ++i, ++j) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ if (color.mono1) {
+ *mono1 ^= mask1;
+ }
+ }
+ mask1 >>= 1;
+ }
+ ++mono1;
+ }
+ while (i < n) {
+ mask1 = 0x80;
+ for (j = 0; j < 8 && i < n; ++i, ++j) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ if (color.mono1) {
+ *mono1 ^= mask1;
+ }
+ }
+ mask1 >>= 1;
+ }
+ ++mono1;
+ }
+ break;
+
+ case splashModeMono8:
+ mono8 = &bitmap->data.mono8[y * bitmap->width + x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ *mono8 ^= color.mono8;
+ }
+ ++mono8;
+ }
+ break;
+
+ case splashModeRGB8:
+ rgb8 = &bitmap->data.rgb8[y * bitmap->width + x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ *rgb8 ^= color.rgb8;
+ }
+ ++rgb8;
+ }
+ break;
+
+ case splashModeBGR8Packed:
+ bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x0];
+ for (i = 0; i < n; ++i) {
+ if (noClip || state->clip->test(x0 + i, y)) {
+ color = pattern->getColor(x0 + i, y);
+ bgr8[2] ^= splashBGR8R(color.bgr8);
+ bgr8[1] ^= splashBGR8G(color.bgr8);
+ bgr8[0] ^= splashBGR8B(color.bgr8);
+ }
+ bgr8 += 3;
+ }
+ break;
+ }
+}
+
+void Splash::getPixel(int x, int y, SplashColor *pixel) {
+ SplashBGR8P *bgr8;
+
+ if (y < 0 || y >= bitmap->height || x < 0 || x >= bitmap->width) {
+ return;
+ }
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ pixel->mono1 = (bitmap->data.mono1[y * bitmap->rowSize + (x >> 3)]
+ >> (7 - (x & 7))) & 1;
+ break;
+ case splashModeMono8:
+ pixel->mono8 = bitmap->data.mono8[y * bitmap->width + x];
+ break;
+ case splashModeRGB8:
+ pixel->rgb8 = bitmap->data.rgb8[y * bitmap->width + x];
+ break;
+ case splashModeBGR8Packed:
+ bgr8 = &bitmap->data.bgr8[y * bitmap->rowSize + 3 * x];
+ pixel->bgr8 = splashMakeBGR8(bgr8[2], bgr8[1], bgr8[0]);
+ break;
+ }
+}
+
+SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
+ int c, SplashFont *font) {
+ SplashGlyphBitmap glyph;
+ int x0, y0, xFrac, yFrac;
+ SplashError err;
+
+ if (debugMode) {
+ printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
+ x, y, c, c, c);
+ }
+ x0 = splashFloor(x);
+ xFrac = splashFloor((x - x0) * splashFontFraction);
+ y0 = splashFloor(y);
+ yFrac = splashFloor((y - y0) * splashFontFraction);
+ if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
+ return splashErrNoGlyph;
+ }
+ err = fillGlyph(x, y, &glyph);
+ if (glyph.freeData) {
+ gfree(glyph.data);
+ }
+ return err;
+}
+
+SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
+ SplashGlyphBitmap *glyph) {
+ int alpha, ialpha;
+ Guchar *p;
+ SplashColor fg;
+ SplashMono1P *mono1Ptr;
+ SplashMono8 *mono8Ptr;
+ SplashRGB8 *rgb8Ptr;
+ SplashBGR8P *bgr8Ptr;
+ SplashMono8 bgMono8;
+ int bgR, bgG, bgB;
+ SplashClipResult clipRes;
+ GBool noClip;
+ int x0, y0, x1, y1, xx, xx1, yy;
+
+ x0 = splashFloor(x);
+ y0 = splashFloor(y);
+
+ if ((clipRes = state->clip->testRect(x0 - glyph->x,
+ y0 - glyph->y,
+ x0 - glyph->x + glyph->w - 1,
+ y0 - glyph->y + glyph->h - 1))
+ != splashClipAllOutside) {
+ noClip = clipRes == splashClipAllInside;
+
+ //~ optimize this
+ if (glyph->aa) {
+ p = glyph->data;
+ for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
+ for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
+ alpha = *p++;
+ if (alpha > 0) {
+ if (noClip || state->clip->test(x1, y1)) {
+ ialpha = 255 - alpha;
+ fg = state->fillPattern->getColor(x1, y1);
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ if (alpha >= 0x80) {
+ mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
+ (x1 >> 3)];
+ if (fg.mono1) {
+ *mono1Ptr |= 0x80 >> (x1 & 7);
+ } else {
+ *mono1Ptr &= ~(0x80 >> (x1 & 7));
+ }
+ }
+ break;
+ case splashModeMono8:
+ mono8Ptr = &bitmap->data.mono8[y1 * bitmap->width + x1];
+ bgMono8 = *mono8Ptr;
+ // note: floor(x / 255) = x >> 8 (for 16-bit x)
+ *mono8Ptr = (alpha * fg.mono8 + ialpha * bgMono8) >> 8;
+ break;
+ case splashModeRGB8:
+ rgb8Ptr = &bitmap->data.rgb8[y1 * bitmap->width + x1];
+ bgR = splashRGB8R(*rgb8Ptr);
+ bgG = splashRGB8G(*rgb8Ptr);
+ bgB = splashRGB8B(*rgb8Ptr);
+ *rgb8Ptr = splashMakeRGB8((alpha * splashRGB8R(fg.rgb8) +
+ ialpha * bgR) >> 8,
+ (alpha * splashRGB8G(fg.rgb8) +
+ ialpha * bgG) >> 8,
+ (alpha * splashRGB8B(fg.rgb8) +
+ ialpha * bgB) >> 8);
+ break;
+ case splashModeBGR8Packed:
+ bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
+ bgr8Ptr[2] =
+ (alpha * splashBGR8R(fg.bgr8) + ialpha * bgr8Ptr[2]) >> 8;
+ bgr8Ptr[1] =
+ (alpha * splashBGR8G(fg.bgr8) + ialpha * bgr8Ptr[1]) >> 8;
+ bgr8Ptr[0] =
+ (alpha * splashBGR8B(fg.bgr8) + ialpha * bgr8Ptr[0]) >> 8;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ p = glyph->data;
+ for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
+ for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
+ alpha = *p++;
+ for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
+ if (alpha & 0x80) {
+ if (noClip || state->clip->test(x1, y1)) {
+ fg = state->fillPattern->getColor(x1, y1);
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono1Ptr = &bitmap->data.mono1[y1 * bitmap->rowSize +
+ (x1 >> 3)];
+ if (fg.mono1) {
+ *mono1Ptr |= 0x80 >> (x1 & 7);
+ } else {
+ *mono1Ptr &= ~(0x80 >> (x1 & 7));
+ }
+ break;
+ case splashModeMono8:
+ bitmap->data.mono8[y1 * bitmap->width + x1] = fg.mono8;
+ break;
+ case splashModeRGB8:
+ bitmap->data.rgb8[y1 * bitmap->width + x1] = fg.rgb8;
+ break;
+ case splashModeBGR8Packed:
+ bgr8Ptr = &bitmap->data.bgr8[y1 * bitmap->rowSize + 3 * x1];
+ bgr8Ptr[2] = splashBGR8R(fg.bgr8);
+ bgr8Ptr[1] = splashBGR8G(fg.bgr8);
+ bgr8Ptr[0] = splashBGR8B(fg.bgr8);
+ break;
+ }
+ }
+ }
+ alpha <<= 1;
+ }
+ }
+ }
+ }
+ }
+
+ return splashOk;
+}
+
+SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
+ int w, int h, SplashCoord *mat) {
+ GBool rot;
+ SplashCoord xScale, yScale, xShear, yShear;
+ int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
+ int ulx, uly, llx, lly, urx, ury, lrx, lry;
+ int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
+ int xMin, xMax, yMin, yMax;
+ SplashClipResult clipRes, clipRes2;
+ int yp, yq, yt, yStep, lastYStep;
+ int xp, xq, xt, xStep, xSrc;
+ int k1, spanXMin, spanXMax, spanY;
+ SplashMono1 *pixBuf;
+ SplashMono1 *p;
+ int pixAcc;
+ SplashCoord alpha;
+ SplashColor fg, bg, pix;
+ int x, y, x1, y1, x2, y2;
+ int n, m, i, j;
+
+ if (debugMode) {
+ printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+ w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+ }
+
+ // check for singular matrix
+ if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+ return splashErrSingularMatrix;
+ }
+
+ // compute scale, shear, rotation, translation parameters
+ rot = splashAbs(mat[1]) > splashAbs(mat[0]);
+ if (rot) {
+ xScale = -mat[1];
+ yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
+ xShear = -mat[3] / yScale;
+ yShear = -mat[0] / mat[1];
+ } else {
+ xScale = mat[0];
+ yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
+ xShear = mat[2] / yScale;
+ yShear = mat[1] / mat[0];
+ }
+ tx = splashRound(mat[4]);
+ ty = splashRound(mat[5]);
+ scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
+ scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
+ xSign = (xScale < 0) ? -1 : 1;
+ ySign = (yScale < 0) ? -1 : 1;
+
+ // clipping
+ ulx1 = 0;
+ uly1 = 0;
+ urx1 = xSign * (scaledWidth - 1);
+ ury1 = splashRound(yShear * urx1);
+ llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
+ lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
+ lrx1 = xSign * (scaledWidth - 1) +
+ splashRound(xShear * ySign * (scaledHeight - 1));
+ lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
+ if (rot) {
+ ulx = tx + uly1; uly = ty - ulx1;
+ urx = tx + ury1; ury = ty - urx1;
+ llx = tx + lly1; lly = ty - llx1;
+ lrx = tx + lry1; lry = ty - lrx1;
+ } else {
+ ulx = tx + ulx1; uly = ty + uly1;
+ urx = tx + urx1; ury = ty + ury1;
+ llx = tx + llx1; lly = ty + lly1;
+ lrx = tx + lrx1; lry = ty + lry1;
+ }
+ xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
+ : (llx < lrx) ? llx : lrx
+ : (urx < llx) ? (urx < lrx) ? urx : lrx
+ : (llx < lrx) ? llx : lrx;
+ xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
+ : (llx > lrx) ? llx : lrx
+ : (urx > llx) ? (urx > lrx) ? urx : lrx
+ : (llx > lrx) ? llx : lrx;
+ yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
+ : (lly < lry) ? lly : lry
+ : (ury < lly) ? (ury < lry) ? ury : lry
+ : (lly < lry) ? lly : lry;
+ yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
+ : (lly > lry) ? lly : lry
+ : (ury > lly) ? (ury > lry) ? ury : lry
+ : (lly > lry) ? lly : lry;
+ clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
+
+ // compute Bresenham parameters for x and y scaling
+ yp = h / scaledHeight;
+ yq = h % scaledHeight;
+ xp = w / scaledWidth;
+ xq = w % scaledWidth;
+
+ // allocate pixel buffer
+ pixBuf = (SplashMono1 *)gmalloc((yp + 1) * w * sizeof(SplashMono1));
+
+ // init y scale Bresenham
+ yt = 0;
+ lastYStep = 1;
+
+ for (y = 0; y < scaledHeight; ++y) {
+
+ // y scale Bresenham
+ yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+
+ // read row(s) from image
+ n = (yp > 0) ? yStep : lastYStep;
+ if (n > 0) {
+ p = pixBuf;
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < w; ++j) {
+ (*src)(srcData, p++);
+ }
+ }
+ }
+ lastYStep = yStep;
+
+ // loop-invariant constants
+ k1 = splashRound(xShear * ySign * y);
+
+ // clipping test
+ if (clipRes != splashClipAllInside &&
+ !rot &&
+ splashRound(yShear * k1) ==
+ splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
+ if (xSign > 0) {
+ spanXMin = tx + k1;
+ spanXMax = spanXMin + (scaledWidth - 1);
+ } else {
+ spanXMax = tx + k1;
+ spanXMin = spanXMax - (scaledWidth - 1);
+ }
+ spanY = ty + ySign * y + splashRound(xShear * ySign * y);
+ clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
+ if (clipRes2 == splashClipAllOutside) {
+ continue;
+ }
+ } else {
+ clipRes2 = clipRes;
+ }
+
+ // init x scale Bresenham
+ xt = 0;
+ xSrc = 0;
+
+ for (x = 0; x < scaledWidth; ++x) {
+
+ // x scale Bresenham
+ xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ // x shear
+ x1 = xSign * x + k1;
+
+ // y shear
+ y1 = ySign * y + splashRound(yShear * x1);
+
+ // rotation
+ if (rot) {
+ x2 = y1;
+ y2 = -x1;
+ } else {
+ x2 = x1;
+ y2 = y1;
+ }
+
+ // compute the alpha value for (x,y) after the x and y scaling
+ // operations
+ n = yStep > 0 ? yStep : 1;
+ m = xStep > 0 ? xStep : 1;
+ p = pixBuf + xSrc;
+ pixAcc = 0;
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < m; ++j) {
+ pixAcc += *p++;
+ }
+ p += w - m;
+ }
+
+ // blend fill color with background
+ if (pixAcc != 0) {
+ fg = state->fillPattern->getColor(tx + x2, ty + y2);
+ if (pixAcc == n * m) {
+ pix = fg;
+ } else {
+ getPixel(tx + x2, ty + y2, &bg);
+ alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m);
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ pix.mono1 = splashRound(alpha * fg.mono1 +
+ (1 - alpha) * bg.mono1);
+ break;
+ case splashModeMono8:
+ pix.mono8 = splashRound(alpha * fg.mono8 +
+ (1 - alpha) * bg.mono8);
+ break;
+ case splashModeRGB8:
+ pix.rgb8 = splashMakeRGB8(
+ splashRound(alpha * splashRGB8R(fg.rgb8) +
+ (1 - alpha) * splashRGB8R(bg.rgb8)),
+ splashRound(alpha * splashRGB8G(fg.rgb8) +
+ (1 - alpha) * splashRGB8G(bg.rgb8)),
+ splashRound(alpha * splashRGB8B(fg.rgb8) +
+ (1 - alpha) * splashRGB8B(bg.rgb8)));
+ case splashModeBGR8Packed:
+ pix.bgr8 = splashMakeBGR8(
+ splashRound(alpha * splashBGR8R(fg.bgr8) +
+ (1 - alpha) * splashBGR8R(bg.bgr8)),
+ splashRound(alpha * splashBGR8G(fg.bgr8) +
+ (1 - alpha) * splashBGR8G(bg.bgr8)),
+ splashRound(alpha * splashBGR8B(fg.bgr8) +
+ (1 - alpha) * splashBGR8B(bg.bgr8)));
+ break;
+ }
+ }
+ drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
+ }
+
+ // x scale Bresenham
+ xSrc += xStep;
+ }
+ }
+
+ // free memory
+ gfree(pixBuf);
+
+ return splashOk;
+}
+
+SplashError Splash::drawImage(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode,
+ int w, int h, SplashCoord *mat) {
+ GBool ok, rot, halftone;
+ SplashCoord xScale, yScale, xShear, yShear;
+ int tx, ty, scaledWidth, scaledHeight, xSign, ySign;
+ int ulx, uly, llx, lly, urx, ury, lrx, lry;
+ int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
+ int xMin, xMax, yMin, yMax;
+ SplashClipResult clipRes, clipRes2;
+ int yp, yq, yt, yStep, lastYStep;
+ int xp, xq, xt, xStep, xSrc;
+ int k1, spanXMin, spanXMax, spanY;
+ SplashColor *pixBuf, *p;
+ Guchar *alphaBuf, *q;
+ SplashColor pix;
+ SplashCoord pixAcc[splashMaxColorComps];
+ int alphaAcc;
+ SplashCoord pixMul, alphaMul, alpha;
+ int x, y, x1, y1, x2, y2;
+ int n, m, i, j;
+
+ if (debugMode) {
+ printf("drawImage: srcMode=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+ srcMode, w, h, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+ }
+
+ // check color modes
+ ok = gFalse; // make gcc happy
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ ok = srcMode == splashModeMono1 || srcMode == splashModeMono8;
+ break;
+ case splashModeMono8:
+ ok = srcMode == splashModeMono8;
+ break;
+ case splashModeRGB8:
+ ok = srcMode == splashModeRGB8;
+ break;
+ case splashModeBGR8Packed:
+ ok = srcMode == splashModeBGR8Packed;
+ break;
+ }
+ if (!ok) {
+ return splashErrModeMismatch;
+ }
+ halftone = bitmap->mode == splashModeMono1 && srcMode == splashModeMono8;
+
+ // check for singular matrix
+ if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
+ return splashErrSingularMatrix;
+ }
+
+ // compute scale, shear, rotation, translation parameters
+ rot = splashAbs(mat[1]) > splashAbs(mat[0]);
+ if (rot) {
+ xScale = -mat[1];
+ yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
+ xShear = -mat[3] / yScale;
+ yShear = -mat[0] / mat[1];
+ } else {
+ xScale = mat[0];
+ yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
+ xShear = mat[2] / yScale;
+ yShear = mat[1] / mat[0];
+ }
+ tx = splashRound(mat[4]);
+ ty = splashRound(mat[5]);
+ scaledWidth = abs(splashRound(mat[4] + xScale) - tx) + 1;
+ scaledHeight = abs(splashRound(mat[5] + yScale) - ty) + 1;
+ xSign = (xScale < 0) ? -1 : 1;
+ ySign = (yScale < 0) ? -1 : 1;
+
+ // clipping
+ ulx1 = 0;
+ uly1 = 0;
+ urx1 = xSign * (scaledWidth - 1);
+ ury1 = splashRound(yShear * urx1);
+ llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
+ lly1 = ySign * (scaledHeight - 1) + splashRound(yShear * llx1);
+ lrx1 = xSign * (scaledWidth - 1) +
+ splashRound(xShear * ySign * (scaledHeight - 1));
+ lry1 = ySign * (scaledHeight - 1) + splashRound(yShear * lrx1);
+ if (rot) {
+ ulx = tx + uly1; uly = ty - ulx1;
+ urx = tx + ury1; ury = ty - urx1;
+ llx = tx + lly1; lly = ty - llx1;
+ lrx = tx + lry1; lry = ty - lrx1;
+ } else {
+ ulx = tx + ulx1; uly = ty + uly1;
+ urx = tx + urx1; ury = ty + ury1;
+ llx = tx + llx1; lly = ty + lly1;
+ lrx = tx + lrx1; lry = ty + lry1;
+ }
+ xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
+ : (llx < lrx) ? llx : lrx
+ : (urx < llx) ? (urx < lrx) ? urx : lrx
+ : (llx < lrx) ? llx : lrx;
+ xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
+ : (llx > lrx) ? llx : lrx
+ : (urx > llx) ? (urx > lrx) ? urx : lrx
+ : (llx > lrx) ? llx : lrx;
+ yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
+ : (lly < lry) ? lly : lry
+ : (ury < lly) ? (ury < lry) ? ury : lry
+ : (lly < lry) ? lly : lry;
+ yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
+ : (lly > lry) ? lly : lry
+ : (ury > lly) ? (ury > lry) ? ury : lry
+ : (lly > lry) ? lly : lry;
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax))
+ == splashClipAllOutside) {
+ return splashOk;
+ }
+
+ // compute Bresenham parameters for x and y scaling
+ yp = h / scaledHeight;
+ yq = h % scaledHeight;
+ xp = w / scaledWidth;
+ xq = w % scaledWidth;
+
+ // allocate pixel buffer
+ pixBuf = (SplashColor *)gmalloc((yp + 1) * w * sizeof(SplashColor));
+ alphaBuf = (Guchar *)gmalloc((yp + 1) * w * sizeof(Guchar));
+
+ // init y scale Bresenham
+ yt = 0;
+ lastYStep = 1;
+
+ for (y = 0; y < scaledHeight; ++y) {
+
+ // y scale Bresenham
+ yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+
+ // read row(s) from image
+ n = (yp > 0) ? yStep : lastYStep;
+ if (n > 0) {
+ p = pixBuf;
+ q = alphaBuf;
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < w; ++j) {
+ (*src)(srcData, p++, q++);
+ }
+ }
+ }
+ lastYStep = yStep;
+
+ // loop-invariant constants
+ k1 = splashRound(xShear * ySign * y);
+
+ // clipping test
+ if (clipRes != splashClipAllInside &&
+ !rot &&
+ splashRound(yShear * k1) ==
+ splashRound(yShear * (xSign * (scaledWidth - 1) + k1))) {
+ if (xSign > 0) {
+ spanXMin = tx + k1;
+ spanXMax = spanXMin + (scaledWidth - 1);
+ } else {
+ spanXMax = tx + k1;
+ spanXMin = spanXMax - (scaledWidth - 1);
+ }
+ spanY = ty + ySign * y + splashRound(xShear * ySign * y);
+ clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
+ if (clipRes2 == splashClipAllOutside) {
+ continue;
+ }
+ } else {
+ clipRes2 = clipRes;
+ }
+
+ // init x scale Bresenham
+ xt = 0;
+ xSrc = 0;
+
+ for (x = 0; x < scaledWidth; ++x) {
+
+ // x scale Bresenham
+ xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ // x shear
+ x1 = xSign * x + k1;
+
+ // y shear
+ y1 = ySign * y + splashRound(yShear * x1);
+
+ // rotation
+ if (rot) {
+ x2 = y1;
+ y2 = -x1;
+ } else {
+ x2 = x1;
+ y2 = y1;
+ }
+
+ // compute the filtered pixel at (x,y) after the x and y scaling
+ // operations
+ n = yStep > 0 ? yStep : 1;
+ m = xStep > 0 ? xStep : 1;
+ p = pixBuf + xSrc;
+ q = alphaBuf + xSrc;
+ for (i = 0; i < splashMaxColorComps; ++i) {
+ pixAcc[i] = 0;
+ }
+ alphaAcc = 0;
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < m; ++j) {
+ switch (srcMode) {
+ case splashModeMono1:
+ pixAcc[0] += p->mono1;
+ break;
+ case splashModeMono8:
+ pixAcc[0] += p->mono8;
+ break;
+ case splashModeRGB8:
+ pixAcc[0] += splashRGB8R(p->rgb8);
+ pixAcc[1] += splashRGB8G(p->rgb8);
+ pixAcc[2] += splashRGB8B(p->rgb8);
+ break;
+ case splashModeBGR8Packed:
+ pixAcc[0] += splashBGR8R(p->bgr8);
+ pixAcc[1] += splashBGR8G(p->bgr8);
+ pixAcc[2] += splashBGR8B(p->bgr8);
+ break;
+ }
+ ++p;
+ alphaAcc += *q++;
+ }
+ p += w - m;
+ q += w - m;
+ }
+ alphaMul = 1 / (SplashCoord)(n * m);
+ if (halftone) {
+ pixMul = (SplashCoord)alphaMul / 256.0;
+ } else {
+ pixMul = alphaMul;
+ }
+ alpha = (SplashCoord)alphaAcc * alphaMul;
+
+ //~ this should blend if 0 < alpha < 1
+ if (alpha > 0.75) {
+
+ // mono8 -> mono1 conversion, with halftoning
+ if (halftone) {
+ pix.mono1 = state->screen->test(tx + x2, ty + y2,
+ pixAcc[0] * pixMul);
+
+ // no conversion, no halftoning
+ } else {
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ pix.mono1 = splashRound(pixAcc[0] * pixMul);
+ break;
+ case splashModeMono8:
+ pix.mono8 = splashRound(pixAcc[0] * pixMul);
+ break;
+ case splashModeRGB8:
+ pix.rgb8 = splashMakeRGB8(splashRound(pixAcc[0] * pixMul),
+ splashRound(pixAcc[1] * pixMul),
+ splashRound(pixAcc[2] * pixMul));
+ break;
+ case splashModeBGR8Packed:
+ pix.bgr8 = splashMakeBGR8(splashRound(pixAcc[0] * pixMul),
+ splashRound(pixAcc[1] * pixMul),
+ splashRound(pixAcc[2] * pixMul));
+ break;
+ }
+ }
+
+ // set pixel
+ drawPixel(tx + x2, ty + y2, &pix, clipRes2 == splashClipAllInside);
+ }
+
+ // x scale Bresenham
+ xSrc += xStep;
+ }
+ }
+
+ gfree(pixBuf);
+ gfree(alphaBuf);
+
+ return splashOk;
+}
+
+void Splash::dumpPath(SplashPath *path) {
+ int i;
+
+ for (i = 0; i < path->length; ++i) {
+ printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s%s\n",
+ i, path->pts[i].x, path->pts[i].y,
+ (path->flags[i] & splashPathFirst) ? " first" : "",
+ (path->flags[i] & splashPathLast) ? " last" : "",
+ (path->flags[i] & splashPathClosed) ? " closed" : "",
+ (path->flags[i] & splashPathCurve) ? " curve" : "",
+ (path->flags[i] & splashPathArcCW) ? " arcCW" : "");
+ }
+}