| //======================================================================== |
| // |
| // 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 "SplashFont.h" |
| #include "SplashGlyphBitmap.h" |
| #include "Splash.h" |
| |
| //------------------------------------------------------------------------ |
| |
| static void blendNormal(SplashColorPtr src, SplashColorPtr dest, |
| SplashColorPtr blend, SplashColorMode cm) { |
| int i; |
| |
| for (i = 0; i < splashColorModeNComps[cm]; ++i) { |
| blend[i] = src[i]; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // Splash |
| //------------------------------------------------------------------------ |
| |
| Splash::Splash(SplashBitmap *bitmapA) { |
| bitmap = bitmapA; |
| state = new SplashState(bitmap->width, bitmap->height); |
| softMask = NULL; |
| clearModRegion(); |
| debugMode = gFalse; |
| } |
| |
| Splash::~Splash() { |
| while (state->next) { |
| restoreState(); |
| } |
| delete state; |
| if (softMask) { |
| delete softMask; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // state read |
| //------------------------------------------------------------------------ |
| |
| SplashPattern *Splash::getStrokePattern() { |
| return state->strokePattern; |
| } |
| |
| SplashPattern *Splash::getFillPattern() { |
| return state->fillPattern; |
| } |
| |
| SplashScreen *Splash::getScreen() { |
| return state->screen; |
| } |
| |
| SplashBlendFunc Splash::getBlendFunc() { |
| return state->blendFunc; |
| } |
| |
| SplashCoord Splash::getStrokeAlpha() { |
| return state->strokeAlpha; |
| } |
| |
| SplashCoord Splash::getFillAlpha() { |
| return state->fillAlpha; |
| } |
| |
| 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::setBlendFunc(SplashBlendFunc func) { |
| state->blendFunc = func; |
| } |
| |
| void Splash::setStrokeAlpha(SplashCoord alpha) { |
| state->strokeAlpha = alpha; |
| } |
| |
| void Splash::setFillAlpha(SplashCoord alpha) { |
| state->fillAlpha = alpha; |
| } |
| |
| 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; |
| } |
| |
| //------------------------------------------------------------------------ |
| // soft mask |
| //------------------------------------------------------------------------ |
| |
| void Splash::setSoftMask(SplashBitmap *softMaskA) { |
| if (softMask) { |
| delete softMask; |
| } |
| softMask = softMaskA; |
| } |
| |
| //------------------------------------------------------------------------ |
| // modified region |
| //------------------------------------------------------------------------ |
| |
| void Splash::clearModRegion() { |
| modXMin = bitmap->getWidth(); |
| modYMin = bitmap->getHeight(); |
| modXMax = -1; |
| modYMax = -1; |
| } |
| |
| inline void Splash::updateModX(int x) { |
| if (x < modXMin) { |
| modXMin = x; |
| } |
| if (x > modXMax) { |
| modXMax = x; |
| } |
| } |
| |
| inline void Splash::updateModY(int y) { |
| if (y < modYMin) { |
| modYMin = y; |
| } |
| if (y > modYMax) { |
| modYMax = y; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // drawing operations |
| //------------------------------------------------------------------------ |
| |
| void Splash::clear(SplashColorPtr color) { |
| SplashColorPtr row, p; |
| Guchar mono; |
| int x, y; |
| |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| mono = color[0] ? 0xff : 0x00; |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| mono, -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); |
| } |
| break; |
| case splashModeMono8: |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| color[0], -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); |
| } |
| break; |
| case splashModeAMono8: |
| if (color[0] == color[1]) { |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| color[0], -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); |
| } |
| } else { |
| row = bitmap->data; |
| for (y = 0; y < bitmap->height; ++y) { |
| p = row; |
| for (x = 0; x < bitmap->width; ++x) { |
| *p++ = color[0]; |
| *p++ = color[1]; |
| } |
| row += bitmap->rowSize; |
| } |
| } |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| if (color[0] == color[1] && color[1] == color[2]) { |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| color[0], -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); |
| } |
| } else { |
| row = bitmap->data; |
| for (y = 0; y < bitmap->height; ++y) { |
| p = row; |
| for (x = 0; x < bitmap->width; ++x) { |
| *p++ = color[0]; |
| *p++ = color[1]; |
| *p++ = color[2]; |
| } |
| row += bitmap->rowSize; |
| } |
| } |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| color[0], -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); |
| } |
| } else { |
| row = bitmap->data; |
| for (y = 0; y < bitmap->height; ++y) { |
| p = row; |
| for (x = 0; x < bitmap->width; ++x) { |
| *p++ = color[0]; |
| *p++ = color[1]; |
| *p++ = color[2]; |
| *p++ = color[3]; |
| } |
| row += bitmap->rowSize; |
| } |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| if (color[0] == color[1] && color[1] == color[2] && |
| color[2] == color[3] && color[3] == color[4]) { |
| if (bitmap->rowSize < 0) { |
| memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), |
| color[0], -bitmap->rowSize * bitmap->height); |
| } else { |
| memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); |
| } |
| } else { |
| row = bitmap->data; |
| for (y = 0; y < bitmap->height; ++y) { |
| p = row; |
| for (x = 0; x < bitmap->width; ++x) { |
| *p++ = color[0]; |
| *p++ = color[1]; |
| *p++ = color[2]; |
| *p++ = color[3]; |
| *p++ = color[4]; |
| } |
| row += bitmap->rowSize; |
| } |
| } |
| break; |
| #endif |
| } |
| |
| updateModX(0); |
| updateModY(0); |
| updateModX(bitmap->width - 1); |
| updateModY(bitmap->height - 1); |
| } |
| |
| SplashError Splash::stroke(SplashPath *path) { |
| SplashXPath *xPath, *xPath2; |
| |
| if (debugMode) { |
| printf("stroke [dash:%d] [width:%.2f]:\n", |
| state->lineDashLength, (double)state->lineWidth); |
| dumpPath(path); |
| } |
| opClipRes = splashClipAllOutside; |
| if (path->length == 0) { |
| return splashErrEmptyPath; |
| } |
| xPath = new SplashXPath(path, state->flatness, gFalse); |
| if (xPath->length == 0) { |
| delete xPath; |
| return splashErrEmptyPath; |
| } |
| 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 nClipRes[3]; |
| 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, state->strokeAlpha, |
| 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; |
| x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); |
| drawSpan(x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| x2 = x3; |
| for (y = y0 + 1; y <= y1 - 1; ++y) { |
| x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); |
| drawSpan(x2, x3 - 1, y, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| x2 = x3; |
| } |
| drawSpan(x2, x2 <= x1 ? x1 : x2, y1, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| } else { |
| x2 = x0; |
| x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); |
| drawSpan((x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| x2 = x3; |
| for (y = y0 + 1; y <= y1 - 1; ++y) { |
| x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); |
| drawSpan(x3 + 1, x2, y, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| x2 = x3; |
| } |
| drawSpan(x1, (x1 <= x2) ? x2 : x1, y1, state->strokePattern, |
| state->strokeAlpha, clipRes == splashClipAllInside); |
| } |
| } |
| |
| // segment with |dy| > |dx| |
| } else { |
| dxdy = seg->dxdy; |
| if (y0 > y1) { |
| t = x0; x0 = x1; x1 = t; |
| t = y0; y0 = y1; y1 = t; |
| } |
| if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, |
| x0 <= x1 ? x1 : x0, y1)) |
| != splashClipAllOutside) { |
| drawPixel(x0, y0, state->strokePattern, state->strokeAlpha, |
| clipRes == splashClipAllInside); |
| for (y = y0 + 1; y <= y1 - 1; ++y) { |
| x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy); |
| drawPixel(x, y, state->strokePattern, state->strokeAlpha, |
| clipRes == splashClipAllInside); |
| } |
| drawPixel(x1, y1, state->strokePattern, state->strokeAlpha, |
| clipRes == splashClipAllInside); |
| } |
| } |
| ++nClipRes[clipRes]; |
| } |
| if (nClipRes[splashClipPartial] || |
| (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { |
| opClipRes = splashClipPartial; |
| } else if (nClipRes[splashClipAllInside]) { |
| opClipRes = splashClipAllInside; |
| } else { |
| opClipRes = splashClipAllOutside; |
| } |
| } |
| |
| 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 = (SplashCoord)1 / d; |
| dxPrev = d * (seg2->x1 - seg2->x0); |
| dyPrev = d * (seg2->y1 - seg2->y0); |
| } |
| wdxPrev = (SplashCoord)0.5 * state->lineWidth * dxPrev; |
| wdyPrev = (SplashCoord)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 = (SplashCoord)1 / d; |
| dx = d * (seg->x1 - seg->x0); |
| dy = d * (seg->y1 - seg->y0); |
| } |
| wdx = (SplashCoord)0.5 * state->lineWidth * dx; |
| wdy = (SplashCoord)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, state->strokeAlpha); |
| delete widePath; |
| |
| // draw the line join |
| if (!(seg->flags & splashXPathEnd0)) { |
| widePath = NULL; |
| switch (state->lineJoin) { |
| case splashLineJoinMiter: |
| dotprod = -(dx * dxPrev + dy * dyPrev); |
| if (splashAbs(splashAbs(dotprod) - 1) > 0.01) { |
| widePath = new SplashPath(); |
| widePath->moveTo(seg->x0, seg->y0); |
| miter = (SplashCoord)2 / ((SplashCoord)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, |
| state->strokeAlpha); |
| 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 -= (SplashCoord)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, state->fillAlpha); |
| } |
| |
| SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, |
| SplashPattern *pattern, |
| SplashCoord alpha) { |
| 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) { |
| |
| // limit the y range |
| if (yMinI < state->clip->getYMin()) { |
| yMinI = state->clip->getYMin(); |
| } |
| if (yMaxI > state->clip->getYMax()) { |
| yMaxI = state->clip->getYMax(); |
| } |
| |
| // draw the spans |
| for (y = yMinI; y <= yMaxI; ++y) { |
| while (scanner->getNextSpan(y, &x0, &x1)) { |
| if (clipRes == splashClipAllInside) { |
| drawSpan(x0, x1, y, pattern, alpha, gTrue); |
| } else { |
| // limit the x range |
| if (x0 < state->clip->getXMin()) { |
| x0 = state->clip->getXMin(); |
| } |
| if (x1 > state->clip->getXMax()) { |
| x1 = state->clip->getXMax(); |
| } |
| clipRes2 = state->clip->testSpan(x0, x1, y); |
| drawSpan(x0, x1, y, pattern, alpha, clipRes2 == splashClipAllInside); |
| } |
| } |
| } |
| } |
| opClipRes = clipRes; |
| |
| 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) { |
| |
| // limit the y range |
| if (yMinI < state->clip->getYMin()) { |
| yMinI = state->clip->getYMin(); |
| } |
| if (yMaxI > state->clip->getYMax()) { |
| yMaxI = state->clip->getYMax(); |
| } |
| |
| // 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 { |
| // limit the x range |
| if (x0 < state->clip->getXMin()) { |
| x0 = state->clip->getXMin(); |
| } |
| if (x1 > state->clip->getXMax()) { |
| x1 = state->clip->getXMax(); |
| } |
| clipRes2 = state->clip->testSpan(x0, x1, y); |
| xorSpan(x0, x1, y, state->fillPattern, |
| clipRes2 == splashClipAllInside); |
| } |
| } |
| } |
| } |
| opClipRes = clipRes; |
| |
| delete scanner; |
| delete xPath; |
| return splashOk; |
| } |
| |
| void Splash::drawPixel(int x, int y, SplashColorPtr color, |
| SplashCoord alpha, GBool noClip) { |
| SplashBlendFunc blendFunc; |
| SplashColorPtr p; |
| SplashColor dest, blend; |
| int alpha2, ialpha2; |
| Guchar t; |
| |
| if (noClip || state->clip->test(x, y)) { |
| if (alpha != 1 || softMask || state->blendFunc) { |
| blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; |
| if (softMask) { |
| alpha2 = (int)(alpha * softMask->data[y * softMask->rowSize + x]); |
| } else { |
| alpha2 = (int)(alpha * 255); |
| } |
| ialpha2 = 255 - alpha2; |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; |
| dest[0] = (*p >> (7 - (x & 7))) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= 0x80 >> (x & 7); |
| } else { |
| *p &= ~(0x80 >> (x & 7)); |
| } |
| break; |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| // note: floor(x / 255) = x >> 8 (for 16-bit x) |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| break; |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; |
| break; |
| #endif |
| } |
| } else { |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; |
| if (color[0]) { |
| *p |= 0x80 >> (x & 7); |
| } else { |
| *p &= ~(0x80 >> (x & 7)); |
| } |
| break; |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x]; |
| p[0] = color[0]; |
| break; |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| p[4] = color[4]; |
| break; |
| #endif |
| } |
| } |
| updateModX(x); |
| updateModY(y); |
| } |
| } |
| |
| void Splash::drawPixel(int x, int y, SplashPattern *pattern, |
| SplashCoord alpha, GBool noClip) { |
| SplashBlendFunc blendFunc; |
| SplashColor color; |
| SplashColorPtr p; |
| SplashColor dest, blend; |
| int alpha2, ialpha2; |
| Guchar t; |
| |
| if (noClip || state->clip->test(x, y)) { |
| if (alpha != 1 || softMask || state->blendFunc) { |
| blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; |
| pattern->getColor(x, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * softMask->data[y * softMask->rowSize + x]); |
| } else { |
| alpha2 = (int)(alpha * 255); |
| } |
| ialpha2 = 255 - alpha2; |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; |
| dest[0] = (*p >> (7 - (x & 7))) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= 0x80 >> (x & 7); |
| } else { |
| *p &= ~(0x80 >> (x & 7)); |
| } |
| break; |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| // note: floor(x / 255) = x >> 8 (for 16-bit x) |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| break; |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x]; |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; |
| break; |
| #endif |
| } |
| } else { |
| pattern->getColor(x, y, color); |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; |
| if (color[0]) { |
| *p |= 0x80 >> (x & 7); |
| } else { |
| *p &= ~(0x80 >> (x & 7)); |
| } |
| break; |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x]; |
| p[0] = color[0]; |
| break; |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x]; |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| p[4] = color[4]; |
| break; |
| #endif |
| } |
| } |
| updateModX(x); |
| updateModY(y); |
| } |
| } |
| |
| void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern, |
| SplashCoord alpha, GBool noClip) { |
| SplashBlendFunc blendFunc; |
| SplashColor color; |
| SplashColorPtr p; |
| SplashColor dest, blend; |
| Guchar mask, t; |
| int alpha2, ialpha2; |
| int i, j, n; |
| |
| n = x1 - x0 + 1; |
| |
| if (noClip) { |
| updateModX(x0); |
| updateModX(x1); |
| updateModY(y); |
| } |
| |
| if (alpha != 1 || softMask || state->blendFunc) { |
| blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; |
| if (softMask) { |
| alpha2 = ialpha2 = 0; // make gcc happy |
| } else { |
| alpha2 = (int)(alpha * 255); |
| ialpha2 = 255 - alpha2; |
| } |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; |
| i = 0; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| if ((j = x0 & 7)) { |
| mask = 0x80 >> j; |
| for (; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| dest[0] = (*p >> (7 - j)) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| while (i < n) { |
| mask = 0x80; |
| for (j = 0; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| dest[0] = (*p >> (7 - j)) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| } else { |
| if ((j = x0 & 7)) { |
| mask = 0x80 >> j; |
| for (; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| dest[0] = (*p >> (7 - j)) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| while (i < n) { |
| mask = 0x80; |
| for (j = 0; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| dest[0] = (*p >> (7 - j)) & 1; |
| (*blendFunc)(color, dest, blend, bitmap->mode); |
| t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; |
| if (t) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| } |
| break; |
| |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| *p = (alpha2 * blend[0] + ialpha2 * *p) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| ++p; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| *p = (alpha2 * blend[0] + ialpha2 * *p) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| ++p; |
| } |
| } |
| break; |
| |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 2; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 2; |
| } |
| } |
| break; |
| |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 3; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 3; |
| } |
| } |
| break; |
| |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (softMask) { |
| alpha2 = (int)(alpha * |
| softMask->data[y * softMask->rowSize + x0 + i]); |
| ialpha2 = 255 - alpha2; |
| } |
| (*blendFunc)(color, p, blend, bitmap->mode); |
| p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; |
| p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; |
| p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; |
| p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; |
| p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } |
| break; |
| #endif |
| } |
| |
| } else { |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; |
| i = 0; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| if ((j = x0 & 7)) { |
| mask = 0x80 >> j; |
| for (; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (color[0]) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| while (i < n) { |
| mask = 0x80; |
| for (j = 0; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| if (color[0]) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| } else { |
| if ((j = x0 & 7)) { |
| mask = 0x80 >> j; |
| for (; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (color[0]) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| while (i < n) { |
| mask = 0x80; |
| for (j = 0; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (color[0]) { |
| *p |= mask; |
| } else { |
| *p &= ~mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| } |
| break; |
| |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| *p = color[0]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| ++p; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| *p = color[0]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| ++p; |
| } |
| } |
| break; |
| |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| p[0] = color[0]; |
| p[1] = color[1]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 2; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] = color[0]; |
| p[1] = color[1]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 2; |
| } |
| } |
| break; |
| |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 3; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 3; |
| } |
| } |
| break; |
| |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; |
| if (pattern->isStatic()) { |
| pattern->getColor(0, 0, color); |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| p[4] = color[4]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] = color[0]; |
| p[1] = color[1]; |
| p[2] = color[2]; |
| p[3] = color[3]; |
| p[4] = color[4]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| } |
| break; |
| #endif |
| } |
| } |
| } |
| |
| void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern, |
| GBool noClip) { |
| SplashColor color; |
| SplashColorPtr p; |
| Guchar mask; |
| int i, j, n; |
| |
| n = x1 - x0 + 1; |
| |
| if (noClip) { |
| updateModX(x0); |
| updateModX(x1); |
| updateModY(y); |
| } |
| |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; |
| i = 0; |
| if ((j = x0 & 7)) { |
| mask = 0x80 >> j; |
| for (; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (color[0]) { |
| *p ^= mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| while (i < n) { |
| mask = 0x80; |
| for (j = 0; j < 8 && i < n; ++i, ++j) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| if (color[0]) { |
| *p ^= mask; |
| } |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| mask >>= 1; |
| } |
| ++p; |
| } |
| break; |
| |
| case splashModeMono8: |
| p = &bitmap->data[y * bitmap->rowSize + x0]; |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| *p ^= color[0]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| ++p; |
| } |
| break; |
| |
| case splashModeAMono8: |
| p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] ^= color[0]; |
| p[1] ^= color[1]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 2; |
| } |
| break; |
| |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] ^= color[0]; |
| p[1] ^= color[1]; |
| p[2] ^= color[2]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 3; |
| } |
| break; |
| |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] ^= color[0]; |
| p[1] ^= color[1]; |
| p[2] ^= color[2]; |
| p[3] ^= color[3]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; |
| for (i = 0; i < n; ++i) { |
| if (noClip || state->clip->test(x0 + i, y)) { |
| pattern->getColor(x0 + i, y, color); |
| p[0] ^= color[0]; |
| p[1] ^= color[1]; |
| p[2] ^= color[2]; |
| p[3] ^= color[3]; |
| p[4] ^= color[4]; |
| if (!noClip) { |
| updateModX(x0 + i); |
| updateModY(y); |
| } |
| } |
| p += 4; |
| } |
| break; |
| #endif |
| } |
| } |
| |
| 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", |
| (double)x, (double)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) { |
| SplashBlendFunc blendFunc; |
| int alpha0, alpha, ialpha; |
| Guchar *p; |
| SplashColor fg, dest, blend; |
| SplashColorPtr pix; |
| SplashClipResult clipRes; |
| GBool noClip; |
| Guchar t; |
| 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; |
| |
| if (noClip) { |
| updateModX(x0 - glyph->x); |
| updateModX(x0 - glyph->x + glyph->w - 1); |
| updateModY(y0 - glyph->y); |
| updateModY(y0 - glyph->y + glyph->h - 1); |
| } |
| |
| //~ optimize this |
| if (state->fillAlpha != 1 || softMask || state->blendFunc) { |
| blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; |
| 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 (softMask) { |
| alpha = (int)(alpha * state->fillAlpha * |
| softMask->data[y1 * softMask->rowSize + x1]); |
| } else { |
| alpha = (int)(alpha * state->fillAlpha); |
| } |
| if (alpha > 0) { |
| if (noClip || state->clip->test(x1, y1)) { |
| ialpha = 255 - alpha; |
| state->fillPattern->getColor(x1, y1, fg); |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; |
| dest[0] = (*pix >> (7 - (x1 & 7))) & 1; |
| (*blendFunc)(fg, dest, blend, bitmap->mode); |
| t = (alpha * blend[0] + ialpha * dest[0]) >> 8; |
| if (t) { |
| *pix |= 0x80 >> (x1 & 7); |
| } else { |
| *pix &= ~(0x80 >> (x1 & 7)); |
| } |
| break; |
| case splashModeMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| // note: floor(x / 255) = x >> 8 (for 16-bit x) |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| break; |
| case splashModeAMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; |
| pix[4] = (alpha * blend[4] + ialpha * pix[4]) >> 8; |
| break; |
| #endif |
| } |
| if (!noClip) { |
| updateModX(x1); |
| updateModY(y1); |
| } |
| } |
| } |
| } |
| } |
| |
| } 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) { |
| alpha0 = *p++; |
| for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) { |
| if (alpha0 & 0x80) { |
| if (noClip || state->clip->test(x1, y1)) { |
| if (softMask) { |
| alpha = (int)(state->fillAlpha * |
| softMask->data[y1 * softMask->rowSize + x1]); |
| } else { |
| alpha = (int)(state->fillAlpha * 255); |
| } |
| ialpha = 255 - alpha; |
| state->fillPattern->getColor(x1, y1, fg); |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; |
| dest[0] = (*pix >> (7 - (x1 & 7))) & 1; |
| (*blendFunc)(fg, dest, blend, bitmap->mode); |
| t = (alpha * blend[0] + ialpha * dest[0]) >> 8; |
| if (t) { |
| *pix |= 0x80 >> (x1 & 7); |
| } else { |
| *pix &= ~(0x80 >> (x1 & 7)); |
| } |
| break; |
| case splashModeMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| // note: floor(x / 255) = x >> 8 (for 16-bit x) |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| break; |
| case splashModeAMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; |
| (*blendFunc)(fg, pix, blend, bitmap->mode); |
| pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; |
| pix[4] = (alpha * blend[4] + ialpha * pix[4]) >> 8; |
| break; |
| #endif |
| } |
| if (!noClip) { |
| updateModX(x1); |
| updateModY(y1); |
| } |
| } |
| } |
| alpha0 <<= 1; |
| } |
| } |
| } |
| } |
| |
| } else { |
| 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; |
| state->fillPattern->getColor(x1, y1, fg); |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| if (alpha >= 0x80) { |
| pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; |
| if (fg[0]) { |
| *pix |= 0x80 >> (x1 & 7); |
| } else { |
| *pix &= ~(0x80 >> (x1 & 7)); |
| } |
| } |
| break; |
| case splashModeMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + x1]; |
| // note: floor(x / 255) = x >> 8 (for 16-bit x) |
| pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; |
| break; |
| case splashModeAMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; |
| pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; |
| pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; |
| pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * fg[3] + ialpha * pix[3]) >> 8; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; |
| pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; |
| pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; |
| pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; |
| pix[3] = (alpha * fg[3] + ialpha * pix[3]) >> 8; |
| pix[4] = (alpha * fg[4] + ialpha * pix[4]) >> 8; |
| break; |
| #endif |
| } |
| if (!noClip) { |
| updateModX(x1); |
| updateModY(y1); |
| } |
| } |
| } |
| } |
| } |
| |
| } 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) { |
| alpha0 = *p++; |
| for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) { |
| if (alpha0 & 0x80) { |
| if (noClip || state->clip->test(x1, y1)) { |
| state->fillPattern->getColor(x1, y1, fg); |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; |
| if (fg[0]) { |
| *pix |= 0x80 >> (x1 & 7); |
| } else { |
| *pix &= ~(0x80 >> (x1 & 7)); |
| } |
| break; |
| case splashModeMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + x1]; |
| pix[0] = fg[0]; |
| break; |
| case splashModeAMono8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; |
| pix[0] = fg[0]; |
| pix[1] = fg[1]; |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; |
| pix[0] = fg[0]; |
| pix[1] = fg[1]; |
| pix[2] = fg[2]; |
| break; |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; |
| pix[0] = fg[0]; |
| pix[1] = fg[1]; |
| pix[2] = fg[2]; |
| pix[3] = fg[3]; |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; |
| pix[0] = fg[0]; |
| pix[1] = fg[1]; |
| pix[2] = fg[2]; |
| pix[3] = fg[3]; |
| pix[4] = fg[4]; |
| break; |
| #endif |
| } |
| if (!noClip) { |
| updateModX(x1); |
| updateModY(y1); |
| } |
| } |
| } |
| alpha0 <<= 1; |
| } |
| } |
| } |
| } |
| } |
| } |
| opClipRes = clipRes; |
| |
| return splashOk; |
| } |
| |
| SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, |
| int w, int h, SplashCoord *mat) { |
| GBool rot; |
| SplashCoord xScale, yScale, xShear, yShear, yShear1; |
| int tx, tx2, ty, ty2, 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; |
| SplashColorPtr pixBuf, p; |
| int pixAcc; |
| SplashCoord alpha; |
| int x, y, x1, x2, y2; |
| SplashCoord y1; |
| int n, m, i, j; |
| |
| if (debugMode) { |
| printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", |
| w, h, (double)mat[0], (double)mat[1], (double)mat[2], |
| (double)mat[3], (double)mat[4], (double)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]; |
| } |
| // the +/-0.01 in these computations is to avoid floating point |
| // precision problems which can lead to gaps between image stripes |
| // (it can cause image stripes to overlap, but that's a much less |
| // visible problem) |
| if (xScale >= 0) { |
| tx = splashRound(mat[4] - 0.01); |
| tx2 = splashRound(mat[4] + xScale + 0.01) - 1; |
| } else { |
| tx = splashRound(mat[4] + 0.01) - 1; |
| tx2 = splashRound(mat[4] + xScale - 0.01); |
| } |
| scaledWidth = abs(tx2 - tx) + 1; |
| if (scaledWidth == 0) { |
| // technically, this should draw nothing, but it generally seems |
| // better to draw a one-pixel-wide stripe rather than throwing it |
| // away |
| scaledWidth = 1; |
| } |
| if (yScale >= 0) { |
| ty = splashRound(mat[5] - 0.01); |
| ty2 = splashRound(mat[5] + yScale + 0.01) - 1; |
| } else { |
| ty = splashRound(mat[5] + 0.01) - 1; |
| ty2 = splashRound(mat[5] + yScale - 0.01); |
| } |
| scaledHeight = abs(ty2 - ty) + 1; |
| if (scaledHeight == 0) { |
| // technically, this should draw nothing, but it generally seems |
| // better to draw a one-pixel-wide stripe rather than throwing it |
| // away |
| scaledHeight = 1; |
| } |
| xSign = (xScale < 0) ? -1 : 1; |
| ySign = (yScale < 0) ? -1 : 1; |
| yShear1 = (SplashCoord)xSign * yShear; |
| |
| // clipping |
| ulx1 = 0; |
| uly1 = 0; |
| urx1 = xSign * (scaledWidth - 1); |
| ury1 = (int)(yShear * urx1); |
| llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); |
| lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); |
| lrx1 = xSign * (scaledWidth - 1) + |
| splashRound(xShear * ySign * (scaledHeight - 1)); |
| lry1 = ySign * (scaledHeight - 1) + (int)(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); |
| opClipRes = clipRes; |
| |
| // compute Bresenham parameters for x and y scaling |
| yp = h / scaledHeight; |
| yq = h % scaledHeight; |
| xp = w / scaledWidth; |
| xq = w % scaledWidth; |
| |
| // allocate pixel buffer |
| pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w); |
| |
| // 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) { |
| (*src)(srcData, p); |
| p += w; |
| } |
| } |
| lastYStep = yStep; |
| |
| // loop-invariant constants |
| k1 = splashRound(xShear * ySign * y); |
| |
| // clipping test |
| if (clipRes != splashClipAllInside && |
| !rot && |
| (int)(yShear * k1) == |
| (int)(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 + (int)(yShear * k1); |
| clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); |
| if (clipRes2 == splashClipAllOutside) { |
| continue; |
| } |
| } else { |
| clipRes2 = clipRes; |
| } |
| |
| // init x scale Bresenham |
| xt = 0; |
| xSrc = 0; |
| |
| // x shear |
| x1 = k1; |
| |
| // y shear |
| y1 = (SplashCoord)ySign * y + yShear * x1; |
| // this is a kludge: if yShear1 is negative, then (int)y1 would |
| // change immediately after the first pixel, which is not what we |
| // want |
| if (yShear1 < 0) { |
| y1 += 0.999; |
| } |
| |
| // loop-invariant constants |
| n = yStep > 0 ? yStep : 1; |
| |
| for (x = 0; x < scaledWidth; ++x) { |
| |
| // x scale Bresenham |
| xStep = xp; |
| xt += xq; |
| if (xt >= scaledWidth) { |
| xt -= scaledWidth; |
| ++xStep; |
| } |
| |
| // rotation |
| if (rot) { |
| x2 = (int)y1; |
| y2 = -x1; |
| } else { |
| x2 = x1; |
| y2 = (int)y1; |
| } |
| |
| // compute the alpha value for (x,y) after the x and y scaling |
| // operations |
| 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) { |
| if (pixAcc == n * m) { |
| drawPixel(tx + x2, ty + y2, state->fillPattern, state->fillAlpha, |
| clipRes2 == splashClipAllInside); |
| } else { |
| alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m); |
| drawPixel(tx + x2, ty + y2, state->fillPattern, |
| state->fillAlpha * alpha, |
| clipRes2 == splashClipAllInside); |
| } |
| } |
| |
| // x scale Bresenham |
| xSrc += xStep; |
| |
| // x shear |
| x1 += xSign; |
| |
| // y shear |
| y1 += yShear1; |
| } |
| } |
| |
| // 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, srcAlpha; |
| SplashCoord xScale, yScale, xShear, yShear, yShear1; |
| int tx, tx2, ty, ty2, 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; |
| SplashColorPtr pixBuf, p; |
| SplashColor pix; |
| #if SPLASH_CMYK |
| int pixAcc0, pixAcc1, pixAcc2, pixAcc3; |
| #else |
| int pixAcc0, pixAcc1, pixAcc2; |
| #endif |
| int alphaAcc; |
| SplashCoord pixMul, alphaMul, alpha; |
| int x, y, x1, x2, y2; |
| SplashCoord y1; |
| int nComps, 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, (double)mat[0], (double)mat[1], (double)mat[2], |
| (double)mat[3], (double)mat[4], (double)mat[5]); |
| } |
| |
| // check color modes |
| ok = gFalse; // make gcc happy |
| nComps = 0; // make gcc happy |
| halftone = gFalse; |
| srcAlpha = gFalse; |
| switch (bitmap->mode) { |
| case splashModeMono1: |
| ok = srcMode == splashModeMono1 || srcMode == splashModeMono8 || |
| srcMode == splashModeAMono8; |
| halftone = srcMode == splashModeMono8 || srcMode == splashModeAMono8; |
| srcAlpha = srcMode == splashModeAMono8; |
| nComps = srcAlpha ? 2 : 1; |
| break; |
| case splashModeMono8: |
| ok = srcMode == splashModeMono8 || srcMode == splashModeAMono8; |
| srcAlpha = srcMode == splashModeAMono8; |
| nComps = srcAlpha ? 2 : 1; |
| break; |
| case splashModeAMono8: |
| //~ not implemented yet |
| ok = gFalse; |
| nComps = 2; |
| break; |
| case splashModeRGB8: |
| ok = srcMode == splashModeRGB8 || srcMode == splashModeARGB8; |
| srcAlpha = srcMode == splashModeARGB8; |
| nComps = srcAlpha ? 4 : 3; |
| break; |
| case splashModeBGR8: |
| ok = srcMode == splashModeBGR8 || srcMode == splashModeBGRA8; |
| srcAlpha = srcMode == splashModeBGRA8; |
| nComps = srcAlpha ? 4 : 3; |
| break; |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| ok = srcMode == splashModeCMYK8 || srcMode == splashModeACMYK8; |
| srcAlpha = srcMode == splashModeACMYK8; |
| nComps = srcAlpha ? 5 : 4; |
| break; |
| #endif |
| case splashModeARGB8: |
| case splashModeBGRA8: |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| #endif |
| //~ not implemented yet |
| ok = gFalse; |
| nComps = 4; |
| break; |
| } |
| if (!ok) { |
| return splashErrModeMismatch; |
| } |
| |
| // 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]; |
| } |
| // the +/-0.01 in these computations is to avoid floating point |
| // precision problems which can lead to gaps between image stripes |
| // (it can cause image stripes to overlap, but that's a much less |
| // visible problem) |
| if (xScale >= 0) { |
| tx = splashRound(mat[4] - 0.01); |
| tx2 = splashRound(mat[4] + xScale + 0.01) - 1; |
| } else { |
| tx = splashRound(mat[4] + 0.01) - 1; |
| tx2 = splashRound(mat[4] + xScale - 0.01); |
| } |
| scaledWidth = abs(tx2 - tx) + 1; |
| if (scaledWidth == 0) { |
| // technically, this should draw nothing, but it generally seems |
| // better to draw a one-pixel-wide stripe rather than throwing it |
| // away |
| scaledWidth = 1; |
| } |
| if (yScale >= 0) { |
| ty = splashRound(mat[5] - 0.01); |
| ty2 = splashRound(mat[5] + yScale + 0.01) - 1; |
| } else { |
| ty = splashRound(mat[5] + 0.01) - 1; |
| ty2 = splashRound(mat[5] + yScale - 0.01); |
| } |
| scaledHeight = abs(ty2 - ty) + 1; |
| if (scaledHeight == 0) { |
| // technically, this should draw nothing, but it generally seems |
| // better to draw a one-pixel-wide stripe rather than throwing it |
| // away |
| scaledHeight = 1; |
| } |
| xSign = (xScale < 0) ? -1 : 1; |
| ySign = (yScale < 0) ? -1 : 1; |
| yShear1 = (SplashCoord)xSign * yShear; |
| |
| // clipping |
| ulx1 = 0; |
| uly1 = 0; |
| urx1 = xSign * (scaledWidth - 1); |
| ury1 = (int)(yShear * urx1); |
| llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); |
| lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); |
| lrx1 = xSign * (scaledWidth - 1) + |
| splashRound(xShear * ySign * (scaledHeight - 1)); |
| lry1 = ySign * (scaledHeight - 1) + (int)(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); |
| opClipRes = clipRes; |
| if (clipRes == 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 = (SplashColorPtr)gmalloc((yp + 1) * w * nComps); |
| |
| pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy |
| #if SPLASH_CMYK |
| pixAcc3 = 0; // make gcc happy |
| #endif |
| |
| if (srcAlpha) { |
| |
| // 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) { |
| (*src)(srcData, p); |
| p += w * nComps; |
| } |
| } |
| lastYStep = yStep; |
| |
| // loop-invariant constants |
| k1 = splashRound(xShear * ySign * y); |
| |
| // clipping test |
| if (clipRes != splashClipAllInside && |
| !rot && |
| (int)(yShear * k1) == |
| (int)(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 + (int)(yShear * k1); |
| clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); |
| if (clipRes2 == splashClipAllOutside) { |
| continue; |
| } |
| } else { |
| clipRes2 = clipRes; |
| } |
| |
| // init x scale Bresenham |
| xt = 0; |
| xSrc = 0; |
| |
| // x shear |
| x1 = k1; |
| |
| // y shear |
| y1 = (SplashCoord)ySign * y + yShear * x1; |
| // this is a kludge: if yShear1 is negative, then (int)y1 would |
| // change immediately after the first pixel, which is not what |
| // we want |
| if (yShear1 < 0) { |
| y1 += 0.999; |
| } |
| |
| // loop-invariant constants |
| n = yStep > 0 ? yStep : 1; |
| |
| for (x = 0; x < scaledWidth; ++x) { |
| |
| // x scale Bresenham |
| xStep = xp; |
| xt += xq; |
| if (xt >= scaledWidth) { |
| xt -= scaledWidth; |
| ++xStep; |
| } |
| |
| // rotation |
| if (rot) { |
| x2 = (int)y1; |
| y2 = -x1; |
| } else { |
| x2 = x1; |
| y2 = (int)y1; |
| } |
| |
| // compute the filtered pixel at (x,y) after the x and y scaling |
| // operations |
| m = xStep > 0 ? xStep : 1; |
| alphaAcc = 0; |
| switch (srcMode) { |
| case splashModeAMono8: |
| p = pixBuf + xSrc * 2; |
| pixAcc0 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| alphaAcc += *p++; |
| pixAcc0 += *p++; |
| } |
| p += 2 * (w - m); |
| } |
| break; |
| case splashModeARGB8: |
| p = pixBuf + xSrc * 4; |
| pixAcc0 = pixAcc1 = pixAcc2 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| alphaAcc += *p++; |
| pixAcc0 += *p++; |
| pixAcc1 += *p++; |
| pixAcc2 += *p++; |
| } |
| p += 4 * (w - m); |
| } |
| break; |
| case splashModeBGRA8: |
| p = pixBuf + xSrc * 4; |
| pixAcc0 = pixAcc1 = pixAcc2 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| pixAcc0 += *p++; |
| pixAcc1 += *p++; |
| pixAcc2 += *p++; |
| alphaAcc += *p++; |
| } |
| p += 4 * (w - m); |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeACMYK8: |
| p = pixBuf + xSrc * 5; |
| pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| alphaAcc += *p++; |
| pixAcc0 += *p++; |
| pixAcc1 += *p++; |
| pixAcc2 += *p++; |
| pixAcc3 += *p++; |
| } |
| p += 5 * (w - m); |
| } |
| break; |
| #endif |
| default: // make gcc happy |
| break; |
| } |
| pixMul = (SplashCoord)1 / (SplashCoord)(n * m); |
| alphaMul = pixMul * (1.0 / 256.0); |
| alpha = (SplashCoord)alphaAcc * alphaMul; |
| |
| if (alpha > 0) { |
| // mono8 -> mono1 conversion, with halftoning |
| if (halftone) { |
| pix[0] = state->screen->test(tx + x2, ty + y2, |
| (SplashCoord)pixAcc0 * pixMul * (1.0 / 256.0)); |
| |
| // no conversion, no halftoning |
| } else { |
| switch (bitmap->mode) { |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); |
| // fall through |
| #endif |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); |
| pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); |
| // fall through |
| case splashModeMono1: |
| case splashModeMono8: |
| pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); |
| break; |
| default: // make gcc happy |
| break; |
| } |
| } |
| |
| // set pixel |
| drawPixel(tx + x2, ty + y2, pix, alpha * state->fillAlpha, |
| clipRes2 == splashClipAllInside); |
| } |
| |
| // x scale Bresenham |
| xSrc += xStep; |
| |
| // x shear |
| x1 += xSign; |
| |
| // y shear |
| y1 += yShear1; |
| } |
| } |
| |
| } else { |
| |
| // 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) { |
| (*src)(srcData, p); |
| p += w * nComps; |
| } |
| } |
| lastYStep = yStep; |
| |
| // loop-invariant constants |
| k1 = splashRound(xShear * ySign * y); |
| |
| // clipping test |
| if (clipRes != splashClipAllInside && |
| !rot && |
| (int)(yShear * k1) == |
| (int)(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 + (int)(yShear * k1); |
| clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); |
| if (clipRes2 == splashClipAllOutside) { |
| continue; |
| } |
| } else { |
| clipRes2 = clipRes; |
| } |
| |
| // init x scale Bresenham |
| xt = 0; |
| xSrc = 0; |
| |
| // x shear |
| x1 = k1; |
| |
| // y shear |
| y1 = (SplashCoord)ySign * y + yShear * x1; |
| // this is a kludge: if yShear1 is negative, then (int)y1 would |
| // change immediately after the first pixel, which is not what |
| // we want |
| if (yShear1 < 0) { |
| y1 += 0.999; |
| } |
| |
| // loop-invariant constants |
| n = yStep > 0 ? yStep : 1; |
| |
| for (x = 0; x < scaledWidth; ++x) { |
| |
| // x scale Bresenham |
| xStep = xp; |
| xt += xq; |
| if (xt >= scaledWidth) { |
| xt -= scaledWidth; |
| ++xStep; |
| } |
| |
| // rotation |
| if (rot) { |
| x2 = (int)y1; |
| y2 = -x1; |
| } else { |
| x2 = x1; |
| y2 = (int)y1; |
| } |
| |
| // compute the filtered pixel at (x,y) after the x and y scaling |
| // operations |
| m = xStep > 0 ? xStep : 1; |
| switch (srcMode) { |
| case splashModeMono1: |
| case splashModeMono8: |
| p = pixBuf + xSrc; |
| pixAcc0 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| pixAcc0 += *p++; |
| } |
| p += w - m; |
| } |
| break; |
| case splashModeRGB8: |
| case splashModeBGR8: |
| p = pixBuf + xSrc * 3; |
| pixAcc0 = pixAcc1 = pixAcc2 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| pixAcc0 += *p++; |
| pixAcc1 += *p++; |
| pixAcc2 += *p++; |
| } |
| p += 3 * (w - m); |
| } |
| break; |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| p = pixBuf + xSrc * 4; |
| pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; |
| for (i = 0; i < n; ++i) { |
| for (j = 0; j < m; ++j) { |
| pixAcc0 += *p++; |
| pixAcc1 += *p++; |
| pixAcc2 += *p++; |
| pixAcc3 += *p++; |
| } |
| p += 4 * (w - m); |
| } |
| break; |
| #endif |
| default: // make gcc happy |
| break; |
| } |
| pixMul = (SplashCoord)1 / (SplashCoord)(n * m); |
| |
| // mono8 -> mono1 conversion, with halftoning |
| if (halftone) { |
| pix[0] = state->screen->test(tx + x2, ty + y2, |
| (SplashCoord)pixAcc0 * pixMul * (1.0 / 256.0)); |
| |
| // no conversion, no halftoning |
| } else { |
| switch (bitmap->mode) { |
| #if SPLASH_CMYK |
| case splashModeCMYK8: |
| pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); |
| // fall through |
| #endif |
| case splashModeRGB8: |
| case splashModeBGR8: |
| pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); |
| pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); |
| // fall through |
| case splashModeMono1: |
| case splashModeMono8: |
| pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); |
| break; |
| default: // make gcc happy |
| break; |
| } |
| } |
| |
| // set pixel |
| drawPixel(tx + x2, ty + y2, pix, state->fillAlpha, |
| clipRes2 == splashClipAllInside); |
| |
| // x scale Bresenham |
| xSrc += xStep; |
| |
| // x shear |
| x1 += xSign; |
| |
| // y shear |
| y1 += yShear1; |
| } |
| } |
| |
| } |
| |
| gfree(pixBuf); |
| |
| 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, (double)path->pts[i].x, (double)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" : ""); |
| } |
| } |
| |
| void Splash::dumpXPath(SplashXPath *path) { |
| int i; |
| |
| for (i = 0; i < path->length; ++i) { |
| printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n", |
| i, (double)path->segs[i].x0, (double)path->segs[i].y0, |
| (double)path->segs[i].x1, (double)path->segs[i].y1, |
| (path->segs[i].flags & splashXPathFirst) ? "F" : " ", |
| (path->segs[i].flags & splashXPathLast) ? "L" : " ", |
| (path->segs[i].flags & splashXPathEnd0) ? "0" : " ", |
| (path->segs[i].flags & splashXPathEnd1) ? "1" : " ", |
| (path->segs[i].flags & splashXPathHoriz) ? "H" : " ", |
| (path->segs[i].flags & splashXPathVert) ? "V" : " ", |
| (path->segs[i].flags & splashXPathFlip) ? "P" : " "); |
| } |
| } |