blob: 4b0fd3c7a6174ccea44fec5fec39bce667672757 [file] [log] [blame]
//========================================================================
//
// 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 splashModeRGB8Qt:
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[2];
*p++ = color[1];
*p++ = color[0];
*p++ = 255;
}
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 splashModeRGB8Qt:
p = &bitmap->data[y * bitmap->rowSize + 4 * x];
(*blendFunc)(color, p, blend, bitmap->mode);
p[0] = (alpha2 * blend[2] + ialpha2 * p[0]) >> 8;
p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8;
p[2] = (alpha2 * blend[0] + 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 splashModeRGB8Qt:
p = &bitmap->data[y * bitmap->rowSize + 4 * x];
p[0] = color[2];
p[1] = color[1];
p[2] = color[0];
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 splashModeRGB8Qt:
p = &bitmap->data[y * bitmap->rowSize + 4 * x];
(*blendFunc)(color, p, blend, bitmap->mode);
p[0] = (alpha2 * blend[2] + ialpha2 * p[0]) >> 8;
p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8;
p[2] = (alpha2 * blend[0] + 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 splashModeRGB8Qt:
p = &bitmap->data[y * bitmap->rowSize + 4 * x];
p[0] = color[2];
p[1] = color[1];
p[2] = color[0];
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 splashModeRGB8Qt:
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[2] + ialpha2 * p[0]) >> 8;
p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8;
p[2] = (alpha2 * blend[0] + ialpha2 * p[2]) >> 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;
if (!noClip) {
updateModX(x0 + i);
updateModY(y);
}
}
p += 4;
}
}
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 splashModeRGB8Qt:
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[2];
p[1] = color[1];
p[2] = color[0];
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];
if (!noClip) {
updateModX(x0 + i);
updateModY(y);
}
}
p += 4;
}
}
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 splashModeRGB8Qt:
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[2];
p[1] ^= color[1];
p[2] ^= color[0];
if (!noClip) {
updateModX(x0 + i);
updateModY(y);
}
}
p += 4;
}
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 * (float)state->fillAlpha *
softMask->data[y1 * softMask->rowSize + x1]);
} else {
alpha = (int)(alpha * (float)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 splashModeRGB8Qt:
pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1];
(*blendFunc)(fg, pix, blend, bitmap->mode);
pix[0] = (alpha * blend[2] + ialpha * pix[0]) >> 8;
pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8;
pix[2] = (alpha * blend[0] + 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]