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