| //======================================================================== |
| // |
| // Splash.cc |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #ifdef USE_GCC_PRAGMAS |
| #pragma implementation |
| #endif |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include "goo/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; |
| SplashRGB8P *rgb8pline, *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8pline = bitmap->data.rgb8p; |
| for (y = 0; y < bitmap->height; ++y) { |
| rgb8p = rgb8pline; |
| for (x = 0; x < bitmap->width; ++x) { |
| rgb8p[0] = splashRGB8R(color.rgb8); |
| rgb8p[1] = splashRGB8G(color.rgb8); |
| rgb8p[2] = splashRGB8B(color.rgb8); |
| rgb8p += 3; |
| } |
| rgb8pline += bitmap->rowSize; |
| } |
| 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 || path->length == 1) { |
| 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; |
| SplashRGB8P *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x]; |
| rgb8p[0] = splashRGB8R(color->rgb8); |
| rgb8p[1] = splashRGB8G(color->rgb8); |
| rgb8p[2] = splashRGB8B(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; |
| SplashRGB8P *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x]; |
| rgb8p[0] = splashRGB8R(color.rgb8); |
| rgb8p[1] = splashRGB8G(color.rgb8); |
| rgb8p[2] = splashRGB8B(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; |
| SplashRGB8P *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8p = &bitmap->data.rgb8p[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); |
| rgb8p[0] = splashRGB8R(color.rgb8); |
| rgb8p[1] = splashRGB8G(color.rgb8); |
| rgb8p[2] = splashRGB8B(color.rgb8); |
| } |
| rgb8p += 3; |
| } |
| 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; |
| SplashRGB8P *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8p = &bitmap->data.rgb8p[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); |
| rgb8p[0] ^= splashRGB8R(color.rgb8); |
| rgb8p[1] ^= splashRGB8G(color.rgb8); |
| rgb8p[2] ^= splashRGB8B(color.rgb8); |
| } |
| rgb8p += 3; |
| } |
| 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) { |
| SplashRGB8P *rgb8p; |
| 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 splashModeRGB8Packed: |
| rgb8p = &bitmap->data.rgb8p[y * bitmap->rowSize + 3 * x]; |
| pixel->rgb8 = splashMakeRGB8(rgb8p[0], rgb8p[1], rgb8p[2]); |
| 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; |
| SplashRGB8P *rgb8pPtr; |
| 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 splashModeRGB8Packed: |
| rgb8pPtr = &bitmap->data.rgb8p[y1 * bitmap->rowSize + 3 * x1]; |
| rgb8pPtr[0] = |
| (alpha * splashRGB8R(fg.rgb8) + ialpha * rgb8pPtr[0]) >> 8; |
| rgb8pPtr[1] = |
| (alpha * splashRGB8G(fg.rgb8) + ialpha * rgb8pPtr[1]) >> 8; |
| rgb8pPtr[2] = |
| (alpha * splashRGB8B(fg.rgb8) + ialpha * rgb8pPtr[2]) >> 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 splashModeRGB8Packed: |
| rgb8pPtr = &bitmap->data.rgb8p[y1 * bitmap->rowSize + 3 * x1]; |
| rgb8pPtr[0] = splashRGB8R(fg.rgb8); |
| rgb8pPtr[1] = splashRGB8G(fg.rgb8); |
| rgb8pPtr[2] = splashRGB8B(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: |
| case splashModeRGB8Packed: |
| 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))); |
| break; |
| 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 splashModeRGB8Packed: |
| ok = srcMode == splashModeRGB8Packed; |
| 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: |
| case splashModeRGB8Packed: |
| 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: |
| case splashModeRGB8Packed: |
| 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" : ""); |
| } |
| } |