blob: c3e54b84721f6386104932f48bec4552a85f0646 [file] [log] [blame]
//========================================================================
//
// Splash.cc
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2005-2019 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
// Copyright (C) 2010-2016 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
// Copyright (C) 2011-2013, 2015 William Bader <williambader@hotmail.com>
// Copyright (C) 2012 Markus Trippelsdorf <markus@trippelsdorf.de>
// Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2012 Matthias Kramm <kramm@quiss.org>
// Copyright (C) 2018, 2019 Stefan Brüns <stefan.bruens@rwth-aachen.de>
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <math.h>
#include "goo/gmem.h"
#include "goo/GooLikely.h"
#include "poppler/Error.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"
#include <algorithm>
//------------------------------------------------------------------------
#define splashAAGamma 1.5
// distance of Bezier control point from center for circle approximation
// = (4 * (sqrt(2) - 1) / 3) * r
#define bezierCircle ((SplashCoord)0.55228475)
#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
static inline unsigned char div255(int x) {
return (unsigned char)((x + (x >> 8) + 0x80) >> 8);
}
// Clip x to lie in [0, 255].
static inline unsigned char clip255(int x) {
return x < 0 ? 0 : x > 255 ? 255 : x;
}
template<typename T>
inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; }
// The PDF spec says that all pixels whose *centers* lie within the
// image target region get painted, so we want to round n+0.5 down to
// n. But this causes problems, e.g., with PDF files that fill a
// rectangle with black and then draw an image to the exact same
// rectangle, so we instead use the fill scan conversion rule.
// However, the correct rule works better for glyphs, so we also
// provide that option in fillImageMask.
#if 0
static inline int imgCoordMungeLower(SplashCoord x) {
return splashCeil(x + 0.5) - 1;
}
static inline int imgCoordMungeUpper(SplashCoord x) {
return splashCeil(x + 0.5) - 1;
}
#else
static inline int imgCoordMungeLower(SplashCoord x) {
return splashFloor(x);
}
static inline int imgCoordMungeUpper(SplashCoord x) {
return splashFloor(x) + 1;
}
static inline int imgCoordMungeLowerC(SplashCoord x, bool glyphMode) {
return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
}
static inline int imgCoordMungeUpperC(SplashCoord x, bool glyphMode) {
return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
}
#endif
// Used by drawImage and fillImageMask to divide the target
// quadrilateral into sections.
struct ImageSection {
int y0, y1; // actual y range
int ia0, ia1; // vertex indices for edge A
int ib0, ib1; // vertex indices for edge A
SplashCoord xa0, ya0, xa1, ya1; // edge A
SplashCoord dxdya; // slope of edge A
SplashCoord xb0, yb0, xb1, yb1; // edge B
SplashCoord dxdyb; // slope of edge B
};
//------------------------------------------------------------------------
// SplashPipe
//------------------------------------------------------------------------
#define splashPipeMaxStages 9
struct SplashPipe {
// pixel coordinates
int x, y;
// source pattern
SplashPattern *pattern;
// source alpha and color
unsigned char aInput;
bool usesShape;
SplashColorPtr cSrc;
SplashColor cSrcVal = {};
// non-isolated group alpha0
unsigned char *alpha0Ptr;
// knockout groups
bool knockout;
unsigned char knockoutOpacity;
// soft mask
SplashColorPtr softMaskPtr;
// destination alpha and color
SplashColorPtr destColorPtr;
int destColorMask;
unsigned char *destAlphaPtr;
// shape
unsigned char shape;
// result alpha and color
bool noTransparency;
SplashPipeResultColorCtrl resultColorCtrl;
// non-isolated group correction
bool nonIsolatedGroup;
// the "run" function
void (Splash::*run)(SplashPipe *pipe);
};
SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
splashPipeResultColorNoAlphaBlendMono,
splashPipeResultColorNoAlphaBlendMono,
splashPipeResultColorNoAlphaBlendRGB,
splashPipeResultColorNoAlphaBlendRGB,
splashPipeResultColorNoAlphaBlendRGB,
splashPipeResultColorNoAlphaBlendCMYK,
splashPipeResultColorNoAlphaBlendDeviceN
};
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
splashPipeResultColorAlphaNoBlendMono,
splashPipeResultColorAlphaNoBlendMono,
splashPipeResultColorAlphaNoBlendRGB,
splashPipeResultColorAlphaNoBlendRGB,
splashPipeResultColorAlphaNoBlendRGB,
splashPipeResultColorAlphaNoBlendCMYK,
splashPipeResultColorAlphaNoBlendDeviceN
};
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
splashPipeResultColorAlphaBlendMono,
splashPipeResultColorAlphaBlendMono,
splashPipeResultColorAlphaBlendRGB,
splashPipeResultColorAlphaBlendRGB,
splashPipeResultColorAlphaBlendRGB,
splashPipeResultColorAlphaBlendCMYK,
splashPipeResultColorAlphaBlendDeviceN
};
//------------------------------------------------------------------------
static void blendXor(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend, SplashColorMode cm) {
int i;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
blend[i] = src[i] ^ dest[i];
}
}
//------------------------------------------------------------------------
// 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;
}
}
//------------------------------------------------------------------------
// pipeline
//------------------------------------------------------------------------
inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
SplashPattern *pattern, SplashColorPtr cSrc,
unsigned char aInput, bool usesShape,
bool nonIsolatedGroup,
bool knockout, unsigned char knockoutOpacity) {
pipeSetXY(pipe, x, y);
pipe->pattern = nullptr;
// source color
if (pattern) {
if (pattern->isStatic()) {
pattern->getColor(x, y, pipe->cSrcVal);
} else {
pipe->pattern = pattern;
}
pipe->cSrc = pipe->cSrcVal;
} else {
pipe->cSrc = cSrc;
}
// source alpha
pipe->aInput = aInput;
pipe->usesShape = usesShape;
pipe->shape = 0;
// knockout
pipe->knockout = knockout;
pipe->knockoutOpacity = knockoutOpacity;
// result alpha
if (aInput == 255 && !state->softMask && !usesShape &&
!state->inNonIsolatedGroup && !nonIsolatedGroup) {
pipe->noTransparency = true;
} else {
pipe->noTransparency = false;
}
// result color
if (pipe->noTransparency) {
// the !state->blendFunc case is handled separately in pipeRun
pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
} else if (!state->blendFunc) {
pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
} else {
pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
}
// non-isolated group correction
pipe->nonIsolatedGroup = nonIsolatedGroup;
// select the 'run' function
pipe->run = &Splash::pipeRun;
if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleMono1;
} else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleMono8;
} else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleRGB8;
} else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleXBGR8;
} else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleBGR8;
} else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleCMYK8;
} else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunSimpleDeviceN8;
}
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
pipe->usesShape &&
!(state->inNonIsolatedGroup && alpha0Bitmap->alpha) &&
!state->blendFunc && !pipe->nonIsolatedGroup) {
if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAAMono1;
} else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAAMono8;
} else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAARGB8;
} else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAAXBGR8;
} else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAABGR8;
} else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAACMYK8;
} else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
pipe->run = &Splash::pipeRunAADeviceN8;
}
}
}
// general case
void Splash::pipeRun(SplashPipe *pipe) {
unsigned char aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
SplashColor cSrcNonIso, cDest, cBlend;
SplashColorPtr cSrc;
unsigned char cResult0, cResult1, cResult2, cResult3;
int t;
int cp, mask;
unsigned char cResult[SPOT_NCOMPS+4];
//----- source color
// static pattern: handled in pipeInit
// fixed color: handled in pipeInit
// dynamic pattern
if (pipe->pattern) {
if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) {
pipeIncX(pipe);
return;
}
if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) {
if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) {
unsigned int overprintMask = 15;
if (pipe->cSrcVal[0] == 0) {
overprintMask &= ~1;
}
if (pipe->cSrcVal[1] == 0) {
overprintMask &= ~2;
}
if (pipe->cSrcVal[2] == 0) {
overprintMask &= ~4;
}
if (pipe->cSrcVal[3] == 0) {
overprintMask &= ~8;
}
state->overprintMask = overprintMask;
}
}
}
if (pipe->noTransparency && !state->blendFunc) {
//----- write destination pixel
switch (bitmap->mode) {
case splashModeMono1:
cResult0 = state->grayTransfer[pipe->cSrc[0]];
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
*pipe->destColorPtr |= pipe->destColorMask;
} else {
*pipe->destColorPtr &= ~pipe->destColorMask;
}
if (!(pipe->destColorMask >>= 1)) {
pipe->destColorMask = 0x80;
++pipe->destColorPtr;
}
break;
case splashModeMono8:
*pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
break;
case splashModeRGB8:
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
break;
case splashModeXBGR8:
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
*pipe->destColorPtr++ = 255;
break;
case splashModeBGR8:
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
break;
case splashModeCMYK8:
if (state->overprintMask & 1) {
pipe->destColorPtr[0] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) :
state->cmykTransferC[pipe->cSrc[0]];
}
if (state->overprintMask & 2) {
pipe->destColorPtr[1] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) :
state->cmykTransferM[pipe->cSrc[1]];
}
if (state->overprintMask & 4) {
pipe->destColorPtr[2] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) :
state->cmykTransferY[pipe->cSrc[2]];
}
if (state->overprintMask & 8) {
pipe->destColorPtr[3] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) :
state->cmykTransferK[pipe->cSrc[3]];
}
pipe->destColorPtr += 4;
break;
case splashModeDeviceN8:
mask = 1;
for (cp = 0; cp < SPOT_NCOMPS + 4; cp ++) {
if (state->overprintMask & mask) {
pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
}
mask <<= 1;
}
pipe->destColorPtr += (SPOT_NCOMPS+4);
break;
}
if (pipe->destAlphaPtr) {
*pipe->destAlphaPtr++ = 255;
}
} else {
//----- read destination pixel
unsigned char *destColorPtr;
if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != nullptr) {
destColorPtr = alpha0Bitmap->data + (alpha0Y+pipe->y)*alpha0Bitmap->rowSize;
switch (bitmap->mode) {
case splashModeMono1:
destColorPtr += (alpha0X+pipe->x) / 8;
break;
case splashModeMono8:
destColorPtr += (alpha0X+pipe->x);
break;
case splashModeRGB8:
case splashModeBGR8:
destColorPtr += (alpha0X+pipe->x) * 3;
break;
case splashModeXBGR8:
case splashModeCMYK8:
destColorPtr += (alpha0X+pipe->x) * 4;
break;
case splashModeDeviceN8:
destColorPtr += (alpha0X+pipe->x) * (SPOT_NCOMPS + 4);
break;
}
} else {
destColorPtr = pipe->destColorPtr;
}
switch (bitmap->mode) {
case splashModeMono1:
cDest[0] = (*destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
break;
case splashModeMono8:
cDest[0] = *destColorPtr;
break;
case splashModeRGB8:
cDest[0] = destColorPtr[0];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[2];
break;
case splashModeXBGR8:
cDest[0] = destColorPtr[2];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[0];
cDest[3] = 255;
break;
case splashModeBGR8:
cDest[0] = destColorPtr[2];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[0];
break;
case splashModeCMYK8:
cDest[0] = destColorPtr[0];
cDest[1] = destColorPtr[1];
cDest[2] = destColorPtr[2];
cDest[3] = destColorPtr[3];
break;
case splashModeDeviceN8:
for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
cDest[cp] = destColorPtr[cp];
break;
}
if (pipe->destAlphaPtr) {
aDest = *pipe->destAlphaPtr;
} else {
aDest = 0xff;
}
//----- source alpha
if (state->softMask) {
if (pipe->usesShape) {
aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) *
pipe->shape);
} else {
aSrc = div255(pipe->aInput * *pipe->softMaskPtr++);
}
} else if (pipe->usesShape) {
aSrc = div255(pipe->aInput * pipe->shape);
} else {
aSrc = pipe->aInput;
}
//----- non-isolated group correction
if (pipe->nonIsolatedGroup) {
// This path is only used when Splash::composite() is called to
// composite a non-isolated group onto the backdrop. In this
// case, pipe->shape is the source (group) alpha.
if (pipe->shape == 0) {
// this value will be multiplied by zero later, so it doesn't
// matter what we use
cSrc = pipe->cSrc;
} else {
t = (aDest * 255) / pipe->shape - aDest;
switch (bitmap->mode) {
case splashModeDeviceN8:
for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
cSrcNonIso[cp] = clip255(pipe->cSrc[cp] +
((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
break;
case splashModeCMYK8:
for (cp = 0; cp < 4; cp++)
cSrcNonIso[cp] = clip255(pipe->cSrc[cp] +
((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
break;
case splashModeXBGR8:
cSrcNonIso[3] = 255;
// fallthrough
case splashModeRGB8:
case splashModeBGR8:
cSrcNonIso[2] = clip255(pipe->cSrc[2] +
((pipe->cSrc[2] - cDest[2]) * t) / 255);
cSrcNonIso[1] = clip255(pipe->cSrc[1] +
((pipe->cSrc[1] - cDest[1]) * t) / 255);
// fallthrough
case splashModeMono1:
case splashModeMono8:
cSrcNonIso[0] = clip255(pipe->cSrc[0] +
((pipe->cSrc[0] - cDest[0]) * t) / 255);
break;
}
cSrc = cSrcNonIso;
// knockout: remove backdrop color
if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) {
aDest = 0;
}
}
} else {
cSrc = pipe->cSrc;
}
//----- blend function
if (state->blendFunc) {
if (bitmap->mode == splashModeDeviceN8) {
for (int k = 4; k < 4 + SPOT_NCOMPS; k++) {
cBlend[k] = 0;
}
}
(*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
}
//----- result alpha and non-isolated group element correction
if (pipe->noTransparency) {
alphaI = alphaIm1 = aResult = 255;
} else {
aResult = aSrc + aDest - div255(aSrc * aDest);
// alphaI = alpha_i
// alphaIm1 = alpha_(i-1)
if (pipe->alpha0Ptr) {
alpha0 = *pipe->alpha0Ptr++;
alphaI = aResult + alpha0 - div255(aResult * alpha0);
alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
} else {
alphaI = aResult;
alphaIm1 = aDest;
}
}
//----- result color
cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
switch (pipe->resultColorCtrl) {
case splashPipeResultColorNoAlphaBlendMono:
cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] +
aDest * cBlend[0])];
break;
case splashPipeResultColorNoAlphaBlendRGB:
cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] +
aDest * cBlend[0])];
cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] +
aDest * cBlend[1])];
cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] +
aDest * cBlend[2])];
break;
case splashPipeResultColorNoAlphaBlendCMYK:
cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] +
aDest * cBlend[0])];
cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] +
aDest * cBlend[1])];
cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] +
aDest * cBlend[2])];
cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
aDest * cBlend[3])];
break;
case splashPipeResultColorNoAlphaBlendDeviceN:
for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] +
aDest * cBlend[cp])];
break;
case splashPipeResultColorAlphaNoBlendMono:
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
aSrc * cSrc[0]) / alphaI];
}
break;
case splashPipeResultColorAlphaNoBlendRGB:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
aSrc * cSrc[0]) / alphaI];
cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
aSrc * cSrc[1]) / alphaI];
cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
aSrc * cSrc[2]) / alphaI];
}
break;
case splashPipeResultColorAlphaNoBlendCMYK:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
aSrc * cSrc[0]) / alphaI];
cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
aSrc * cSrc[1]) / alphaI];
cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
aSrc * cSrc[2]) / alphaI];
cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
aSrc * cSrc[3]) / alphaI];
}
break;
case splashPipeResultColorAlphaNoBlendDeviceN:
if (alphaI == 0) {
for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = 0;
} else {
for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
aSrc * cSrc[cp]) / alphaI];
}
break;
case splashPipeResultColorAlphaBlendMono:
if (alphaI == 0) {
cResult0 = 0;
} else {
cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255) /
alphaI];
}
break;
case splashPipeResultColorAlphaBlendRGB:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
} else {
cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255) /
alphaI];
cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
aSrc * ((255 - alphaIm1) * cSrc[1] +
alphaIm1 * cBlend[1]) / 255) /
alphaI];
cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
aSrc * ((255 - alphaIm1) * cSrc[2] +
alphaIm1 * cBlend[2]) / 255) /
alphaI];
}
break;
case splashPipeResultColorAlphaBlendCMYK:
if (alphaI == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
aSrc * ((255 - alphaIm1) * cSrc[0] +
alphaIm1 * cBlend[0]) / 255) /
alphaI];
cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
aSrc * ((255 - alphaIm1) * cSrc[1] +
alphaIm1 * cBlend[1]) / 255) /
alphaI];
cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
aSrc * ((255 - alphaIm1) * cSrc[2] +
alphaIm1 * cBlend[2]) / 255) /
alphaI];
cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
aSrc * ((255 - alphaIm1) * cSrc[3] +
alphaIm1 * cBlend[3]) / 255) /
alphaI];
}
break;
case splashPipeResultColorAlphaBlendDeviceN:
if (alphaI == 0) {
for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = 0;
} else {
for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
aSrc * ((255 - alphaIm1) * cSrc[cp] +
alphaIm1 * cBlend[cp]) / 255) /
alphaI];
}
break;
}
//----- write destination pixel
switch (bitmap->mode) {
case splashModeMono1:
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
*pipe->destColorPtr |= pipe->destColorMask;
} else {
*pipe->destColorPtr &= ~pipe->destColorMask;
}
if (!(pipe->destColorMask >>= 1)) {
pipe->destColorMask = 0x80;
++pipe->destColorPtr;
}
break;
case splashModeMono8:
*pipe->destColorPtr++ = cResult0;
break;
case splashModeRGB8:
*pipe->destColorPtr++ = cResult0;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult2;
break;
case splashModeXBGR8:
*pipe->destColorPtr++ = cResult2;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult0;
*pipe->destColorPtr++ = 255;
break;
case splashModeBGR8:
*pipe->destColorPtr++ = cResult2;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult0;
break;
case splashModeCMYK8:
if (state->overprintMask & 1) {
pipe->destColorPtr[0] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
cResult0;
}
if (state->overprintMask & 2) {
pipe->destColorPtr[1] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
cResult1;
}
if (state->overprintMask & 4) {
pipe->destColorPtr[2] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
cResult2;
}
if (state->overprintMask & 8) {
pipe->destColorPtr[3] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
cResult3;
}
pipe->destColorPtr += 4;
break;
case splashModeDeviceN8:
mask = 1;
for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
if (state->overprintMask & mask) {
pipe->destColorPtr[cp] = cResult[cp];
}
mask <<=1;
}
pipe->destColorPtr += (SPOT_NCOMPS+4);
break;
}
if (pipe->destAlphaPtr) {
*pipe->destAlphaPtr++ = aResult;
}
}
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
void Splash::pipeRunSimpleMono1(SplashPipe *pipe) {
unsigned char cResult0;
//----- write destination pixel
cResult0 = state->grayTransfer[pipe->cSrc[0]];
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
*pipe->destColorPtr |= pipe->destColorMask;
} else {
*pipe->destColorPtr &= ~pipe->destColorMask;
}
if (!(pipe->destColorMask >>= 1)) {
pipe->destColorMask = 0x80;
++pipe->destColorPtr;
}
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleMono8(SplashPipe *pipe) {
//----- write destination pixel
*pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) {
//----- write destination pixel
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) {
//----- write destination pixel
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
*pipe->destColorPtr++ = 255;
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) {
//----- write destination pixel
*pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
*pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
*pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) {
//----- write destination pixel
if (state->overprintMask & 1) {
pipe->destColorPtr[0] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) :
state->cmykTransferC[pipe->cSrc[0]];
}
if (state->overprintMask & 2) {
pipe->destColorPtr[1] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) :
state->cmykTransferM[pipe->cSrc[1]];
}
if (state->overprintMask & 4) {
pipe->destColorPtr[2] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) :
state->cmykTransferY[pipe->cSrc[2]];
}
if (state->overprintMask & 8) {
pipe->destColorPtr[3] = (state->overprintAdditive) ?
std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) :
state->cmykTransferK[pipe->cSrc[3]];
}
pipe->destColorPtr += 4;
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) {
//----- write destination pixel
int mask = 1;
for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) {
if (state->overprintMask & mask) {
pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
}
mask <<=1;
}
pipe->destColorPtr += (SPOT_NCOMPS+4);
*pipe->destAlphaPtr++ = 255;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr
void Splash::pipeRunAAMono1(SplashPipe *pipe) {
unsigned char aSrc;
SplashColor cDest;
unsigned char cResult0;
//----- read destination pixel
cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result color
// note: aDest = alpha2 = aResult = 0xff
cResult0 = state->grayTransfer[(unsigned char)div255((0xff - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0])];
//----- write destination pixel
if (state->screen->test(pipe->x, pipe->y, cResult0)) {
*pipe->destColorPtr |= pipe->destColorMask;
} else {
*pipe->destColorPtr &= ~pipe->destColorMask;
}
if (!(pipe->destColorMask >>= 1)) {
pipe->destColorMask = 0x80;
++pipe->destColorPtr;
}
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr
void Splash::pipeRunAAMono8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult0;
//----- read destination pixel
cDest[0] = *pipe->destColorPtr;
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
//----- result color
if (alpha2 == 0) {
cResult0 = 0;
} else {
cResult0 = state->grayTransfer[(unsigned char)(((alpha2 - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0]) / alpha2)];
}
//----- write destination pixel
*pipe->destColorPtr++ = cResult0;
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr
void Splash::pipeRunAARGB8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult0, cResult1, cResult2;
//----- read destination alpha
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result color
if (aSrc == 255) {
cResult0 = state->rgbTransferR[pipe->cSrc[0]];
cResult1 = state->rgbTransferG[pipe->cSrc[1]];
cResult2 = state->rgbTransferB[pipe->cSrc[2]];
aResult = 255;
} else if (aSrc == 0 && aDest == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
aResult = 0;
} else {
//----- read destination pixel
cDest[0] = pipe->destColorPtr[0];
cDest[1] = pipe->destColorPtr[1];
cDest[2] = pipe->destColorPtr[2];
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0]) / alpha2)];
cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] +
aSrc * pipe->cSrc[1]) / alpha2)];
cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] +
aSrc * pipe->cSrc[2]) / alpha2)];
}
//----- write destination pixel
*pipe->destColorPtr++ = cResult0;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult2;
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr
void Splash::pipeRunAAXBGR8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult0, cResult1, cResult2;
//----- read destination alpha
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result color
if (aSrc == 255) {
cResult0 = state->rgbTransferR[pipe->cSrc[0]];
cResult1 = state->rgbTransferG[pipe->cSrc[1]];
cResult2 = state->rgbTransferB[pipe->cSrc[2]];
aResult = 255;
} else if (aSrc == 0 && aDest == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
aResult = 0;
} else {
//----- read destination color
cDest[0] = pipe->destColorPtr[2];
cDest[1] = pipe->destColorPtr[1];
cDest[2] = pipe->destColorPtr[0];
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0]) / alpha2)];
cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] +
aSrc * pipe->cSrc[1]) / alpha2)];
cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] +
aSrc * pipe->cSrc[2]) / alpha2)];
}
//----- write destination pixel
*pipe->destColorPtr++ = cResult2;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult0;
*pipe->destColorPtr++ = 255;
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr
void Splash::pipeRunAABGR8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult0, cResult1, cResult2;
//----- read destination alpha
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result color
if (aSrc == 255) {
cResult0 = state->rgbTransferR[pipe->cSrc[0]];
cResult1 = state->rgbTransferG[pipe->cSrc[1]];
cResult2 = state->rgbTransferB[pipe->cSrc[2]];
aResult = 255;
} else if (aSrc == 0 && aDest == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
aResult = 0;
} else {
//----- read destination color
cDest[0] = pipe->destColorPtr[2];
cDest[1] = pipe->destColorPtr[1];
cDest[2] = pipe->destColorPtr[0];
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0]) / alpha2)];
cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] +
aSrc * pipe->cSrc[1]) / alpha2)];
cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] +
aSrc * pipe->cSrc[2]) / alpha2)];
}
//----- write destination pixel
*pipe->destColorPtr++ = cResult2;
*pipe->destColorPtr++ = cResult1;
*pipe->destColorPtr++ = cResult0;
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr
void Splash::pipeRunAACMYK8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult0, cResult1, cResult2, cResult3;
//----- read destination pixel
cDest[0] = pipe->destColorPtr[0];
cDest[1] = pipe->destColorPtr[1];
cDest[2] = pipe->destColorPtr[2];
cDest[3] = pipe->destColorPtr[3];
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
//----- result color
if (alpha2 == 0) {
cResult0 = 0;
cResult1 = 0;
cResult2 = 0;
cResult3 = 0;
} else {
cResult0 = state->cmykTransferC[(unsigned char)(((alpha2 - aSrc) * cDest[0] +
aSrc * pipe->cSrc[0]) / alpha2)];
cResult1 = state->cmykTransferM[(unsigned char)(((alpha2 - aSrc) * cDest[1] +
aSrc * pipe->cSrc[1]) / alpha2)];
cResult2 = state->cmykTransferY[(unsigned char)(((alpha2 - aSrc) * cDest[2] +
aSrc * pipe->cSrc[2]) / alpha2)];
cResult3 = state->cmykTransferK[(unsigned char)(((alpha2 - aSrc) * cDest[3] +
aSrc * pipe->cSrc[3]) / alpha2)];
}
//----- write destination pixel
if (state->overprintMask & 1) {
pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ?
std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
cResult0;
}
if (state->overprintMask & 2) {
pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ?
std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
cResult1;
}
if (state->overprintMask & 4) {
pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ?
std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
cResult2;
}
if (state->overprintMask & 8) {
pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ?
std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
cResult3;
}
pipe->destColorPtr += 4;
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr
void Splash::pipeRunAADeviceN8(SplashPipe *pipe) {
unsigned char aSrc, aDest, alpha2, aResult;
SplashColor cDest;
unsigned char cResult[SPOT_NCOMPS+4];
int cp, mask;
//----- read destination pixel
for (cp=0; cp < SPOT_NCOMPS+4; cp++)
cDest[cp] = pipe->destColorPtr[cp];
aDest = *pipe->destAlphaPtr;
//----- source alpha
aSrc = div255(pipe->aInput * pipe->shape);
//----- result alpha and non-isolated group element correction
aResult = aSrc + aDest - div255(aSrc * aDest);
alpha2 = aResult;
//----- result color
if (alpha2 == 0) {
for (cp=0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = 0;
} else {
for (cp=0; cp < SPOT_NCOMPS+4; cp++)
cResult[cp] = state->deviceNTransfer[cp][(unsigned char)(((alpha2 - aSrc) * cDest[cp] +
aSrc * pipe->cSrc[cp]) / alpha2)];
}
//----- write destination pixel
mask = 1;
for (cp=0; cp < SPOT_NCOMPS+4; cp++) {
if (state->overprintMask & mask) {
pipe->destColorPtr[cp] = cResult[cp];
}
mask <<= 1;
}
pipe->destColorPtr += (SPOT_NCOMPS+4);
*pipe->destAlphaPtr++ = aResult;
++pipe->x;
}
inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
pipe->x = x;
pipe->y = y;
if (state->softMask) {
pipe->softMaskPtr =
&state->softMask->data[y * state->softMask->rowSize + x];
}
switch (bitmap->mode) {
case splashModeMono1:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
pipe->destColorMask = 0x80 >> (x & 7);
break;
case splashModeMono8:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
break;
case splashModeRGB8:
case splashModeBGR8:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
break;
case splashModeXBGR8:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
break;
case splashModeCMYK8:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
break;
case splashModeDeviceN8:
pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x];
break;
}
if (bitmap->alpha) {
pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
} else {
pipe->destAlphaPtr = nullptr;
}
if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
pipe->alpha0Ptr =
&alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
(alpha0X + x)];
} else {
pipe->alpha0Ptr = nullptr;
}
}
inline void Splash::pipeIncX(SplashPipe *pipe) {
++pipe->x;
if (state->softMask) {
++pipe->softMaskPtr;
}
switch (bitmap->mode) {
case splashModeMono1:
if (!(pipe->destColorMask >>= 1)) {
pipe->destColorMask = 0x80;
++pipe->destColorPtr;
}
break;
case splashModeMono8:
++pipe->destColorPtr;
break;
case splashModeRGB8:
case splashModeBGR8:
pipe->destColorPtr += 3;
break;
case splashModeXBGR8:
pipe->destColorPtr += 4;
break;
case splashModeCMYK8:
pipe->destColorPtr += 4;
break;
case splashModeDeviceN8:
pipe->destColorPtr += (SPOT_NCOMPS+4);
break;
}
if (pipe->destAlphaPtr) {
++pipe->destAlphaPtr;
}
if (pipe->alpha0Ptr) {
++pipe->alpha0Ptr;
}
}
inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, bool noClip) {
if (unlikely(y < 0))
return;
if (noClip || state->clip->test(x, y)) {
pipeSetXY(pipe, x, y);
(this->*pipe->run)(pipe);
updateModX(x);
updateModY(y);
}
}
inline void Splash::drawAAPixelInit() {
aaBufY = -1;
}
inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
#if splashAASize == 4
static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 };
int w;
#else
int xx, yy;
#endif
SplashColorPtr p;
int x0, x1, t;
if (x < 0 || x >= bitmap->width ||
y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
return;
}
// update aaBuf
if (y != aaBufY) {
memset(aaBuf->getDataPtr(), 0xff,
aaBuf->getRowSize() * aaBuf->getHeight());
x0 = 0;
x1 = bitmap->width - 1;
state->clip->clipAALine(aaBuf, &x0, &x1, y);
aaBufY = y;
}
// compute the shape value
#if splashAASize == 4
p = aaBuf->getDataPtr() + (x >> 1);
w = aaBuf->getRowSize();
if (x & 1) {
t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
} else {
t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
}
#else
t = 0;
for (yy = 0; yy < splashAASize; ++yy) {
for (xx = 0; xx < splashAASize; ++xx) {
p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
((x * splashAASize + xx) >> 3);
t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
}
}
#endif
// draw the pixel
if (t != 0) {
pipeSetXY(pipe, x, y);
pipe->shape = div255(aaGamma[t] * pipe->shape);
(this->*pipe->run)(pipe);
updateModX(x);
updateModY(y);
}
}
inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
bool noClip) {
int x;
if (noClip) {
pipeSetXY(pipe, x0, y);
for (x = x0; x <= x1; ++x) {
(this->*pipe->run)(pipe);
}
updateModX(x0);
updateModX(x1);
updateModY(y);
} else {
if (x0 < state->clip->getXMinI()) {
x0 = state->clip->getXMinI();
}
if (x1 > state->clip->getXMaxI()) {
x1 = state->clip->getXMaxI();
}
pipeSetXY(pipe, x0, y);
for (x = x0; x <= x1; ++x) {
if (state->clip->test(x, y)) {
(this->*pipe->run)(pipe);
updateModX(x);
updateModY(y);
} else {
pipeIncX(pipe);
}
}
}
}
inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine, unsigned char lineOpacity) {
#if splashAASize == 4
static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 };
SplashColorPtr p0, p1, p2, p3;
int t;
#else
SplashColorPtr p;
int xx, yy, t;
#endif
int x;
#if splashAASize == 4
p0 = aaBuf->getDataPtr() + (x0 >> 1);
p1 = p0 + aaBuf->getRowSize();
p2 = p1 + aaBuf->getRowSize();
p3 = p2 + aaBuf->getRowSize();
#endif
pipeSetXY(pipe, x0, y);
for (x = x0; x <= x1; ++x) {
// compute the shape value
#if splashAASize == 4
if (x & 1) {
t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
++p0; ++p1; ++p2; ++p3;
} else {
t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
}
#else
t = 0;
for (yy = 0; yy < splashAASize; ++yy) {
for (xx = 0; xx < splashAASize; ++xx) {
p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
((x * splashAASize + xx) >> 3);
t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
}
}
#endif
if (t != 0) {
pipe->shape = (adjustLine) ? div255((int) lineOpacity * (double)aaGamma[t]) : (double)aaGamma[t];
(this->*pipe->run)(pipe);
updateModX(x);
updateModY(y);
} else {
pipeIncX(pipe);
}
}
}
//------------------------------------------------------------------------
// Transform a point from user space to device space.
inline void Splash::transform(SplashCoord *matrix,
SplashCoord xi, SplashCoord yi,
SplashCoord *xo, SplashCoord *yo) {
// [ m[0] m[1] 0 ]
// [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
// [ m[4] m[5] 1 ]
*xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
*yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
}
//------------------------------------------------------------------------
// Splash
//------------------------------------------------------------------------
Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA,
SplashScreenParams *screenParams) {
int i;
bitmap = bitmapA;
vectorAntialias = vectorAntialiasA;
inShading = false;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenParams);
if (vectorAntialias) {
aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1, splashModeMono1, false);
for (i = 0; i <= splashAASize * splashAASize; ++i) {
aaGamma[i] = (unsigned char)splashRound(
splashPow((SplashCoord)i /
(SplashCoord)(splashAASize * splashAASize),
splashAAGamma) * 255);
}
} else {
aaBuf = nullptr;
}
minLineWidth = 0;
thinLineMode = splashThinLineDefault;
clearModRegion();
debugMode = false;
alpha0Bitmap = nullptr;
}
Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA,
SplashScreen *screenA) {
int i;
bitmap = bitmapA;
inShading = false;
vectorAntialias = vectorAntialiasA;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenA);
if (vectorAntialias) {
aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1, splashModeMono1, false);
for (i = 0; i <= splashAASize * splashAASize; ++i) {
aaGamma[i] = (unsigned char)splashRound(
splashPow((SplashCoord)i /
(SplashCoord)(splashAASize * splashAASize),
splashAAGamma) * 255);
}
} else {
aaBuf = nullptr;
}
minLineWidth = 0;
thinLineMode = splashThinLineDefault;
clearModRegion();
debugMode = false;
alpha0Bitmap = nullptr;
}
Splash::~Splash() {
while (state->next) {
restoreState();
}
delete state;
delete aaBuf;
}
//------------------------------------------------------------------------
// state read
//------------------------------------------------------------------------
SplashCoord *Splash::getMatrix() {
return state->matrix;
}
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;
}
bool Splash::getStrokeAdjust() {
return state->strokeAdjust;
}
SplashClip *Splash::getClip() {
return state->clip;
}
SplashBitmap *Splash::getSoftMask() {
return state->softMask;
}
bool Splash::getInNonIsolatedGroup() {
return state->inNonIsolatedGroup;
}
//------------------------------------------------------------------------
// state write
//------------------------------------------------------------------------
void Splash::setMatrix(SplashCoord *matrix) {
memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
}
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 = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha;
}
void Splash::setFillAlpha(SplashCoord alpha) {
state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha;
}
void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) {
state->patternStrokeAlpha = strokeAlpha;
state->patternFillAlpha = fillAlpha;
state->multiplyPatternAlpha = true;
}
void Splash::clearPatternAlpha() {
state->patternStrokeAlpha = 1;
state->patternFillAlpha = 1;
state->multiplyPatternAlpha = false;
}
void Splash::setFillOverprint(bool fop) {
state->fillOverprint = fop;
}
void Splash::setStrokeOverprint(bool gop) {
state->strokeOverprint = gop;
}
void Splash::setOverprintMode(int opm) {
state->overprintMode = opm;
}
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::setStrokeAdjust(bool strokeAdjust) {
state->strokeAdjust = strokeAdjust;
}
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, bool eo) {
return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
}
void Splash::setSoftMask(SplashBitmap *softMask) {
state->setSoftMask(softMask);
}
void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
int alpha0XA, int alpha0YA) {
alpha0Bitmap = alpha0BitmapA;
alpha0X = alpha0XA;
alpha0Y = alpha0YA;
state->inNonIsolatedGroup = true;
}
void Splash::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue,
unsigned char *gray) {
state->setTransfer(red, green, blue, gray);
}
void Splash::setOverprintMask(unsigned int overprintMask, bool additive) {
state->overprintMask = overprintMask;
state->overprintAdditive = additive;
}
//------------------------------------------------------------------------
// 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(SplashColorPtr color, unsigned char alpha) {
SplashColorPtr row, p;
unsigned char mono;
int x, y;
switch (bitmap->mode) {
case splashModeMono1:
mono = (color[0] & 0x80) ? 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 splashModeRGB8:
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];
}
row += bitmap->rowSize;
}
}
break;
case splashModeXBGR8:
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];
*p++ = 255;
}
row += bitmap->rowSize;
}
}
break;
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 splashModeCMYK8:
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;
case splashModeDeviceN8:
row = bitmap->data;
for (y = 0; y < bitmap->height; ++y) {
p = row;
for (x = 0; x < bitmap->width; ++x) {
for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
*p++ = color[cp];
}
row += bitmap->rowSize;
}
break;
}
if (bitmap->alpha) {
memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
}
updateModX(0);
updateModY(0);
updateModX(bitmap->width - 1);
updateModY(bitmap->height - 1);
}
SplashError Splash::stroke(SplashPath *path) {
SplashPath *path2, *dPath;
SplashCoord d1, d2, t1, t2, w;
if (debugMode) {
printf("stroke [dash:%d] [width:%.2f]:\n",
state->lineDashLength, (double)state->lineWidth);
dumpPath(path);
}
opClipRes = splashClipAllOutside;
if (path->length == 0) {
return splashErrEmptyPath;
}
path2 = flattenPath(path, state->matrix, state->flatness);
if (state->lineDashLength > 0) {
dPath = makeDashedPath(path2);
delete path2;
path2 = dPath;
if (path2->length == 0) {
delete path2;
return splashErrEmptyPath;
}
}
// transform a unit square, and take the half the max of the two
// diagonals; the product of this number and the line width is the
// (approximate) transformed line width
t1 = state->matrix[0] + state->matrix[2];
t2 = state->matrix[1] + state->matrix[3];
d1 = t1 * t1 + t2 * t2;
t1 = state->matrix[0] - state->matrix[2];
t2 = state->matrix[1] - state->matrix[3];
d2 = t1 * t1 + t2 * t2;
if (d2 > d1) {
d1 = d2;
}
d1 *= 0.5;
if (d1 > 0 &&
d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
w = minLineWidth / splashSqrt(d1);
strokeWide(path2, w);
} else if (bitmap->mode == splashModeMono1) {
// this gets close to Adobe's behavior in mono mode
if (d1 * state->lineWidth <= 2) {
strokeNarrow(path2);
} else {
strokeWide(path2, state->lineWidth);
}
} else {
if (state->lineWidth == 0) {
strokeNarrow(path2);
} else {
strokeWide(path2, state->lineWidth);
}
}
delete path2;
return splashOk;
}
void Splash::strokeNarrow(SplashPath *path) {
SplashPipe pipe;
SplashXPathSeg *seg;
int x0, x1, y0, y1, xa, xb, y;
SplashCoord dxdy;
SplashClipResult clipRes;
int nClipRes[3];
int i;
nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
SplashXPath xPath(path, state->matrix, state->flatness, false);
pipeInit(&pipe, 0, 0, state->strokePattern, nullptr,
(unsigned char)splashRound(state->strokeAlpha * 255),
false, false);
for (i = 0, seg = xPath.segs; i < xPath.length; ++i, ++seg) {
if (seg->y0 <= seg->y1) {
y0 = splashFloor(seg->y0);
y1 = splashFloor(seg->y1);
x0 = splashFloor(seg->x0);
x1 = splashFloor(seg->x1);
} else {
y0 = splashFloor(seg->y1);
y1 = splashFloor(seg->y0);
x0 = splashFloor(seg->x1);
x1 = splashFloor(seg->x0);
}
if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
x0 <= x1 ? x1 : x0, y1))
!= splashClipAllOutside) {
if (y0 == y1) {
if (x0 <= x1) {
drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
} else {
drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
}
} else {
dxdy = seg->dxdy;
if (y0 < state->clip->getYMinI()) {
y0 = state->clip->getYMinI();
x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
}
if (y1 > state->clip->getYMaxI()) {
y1 = state->clip->getYMaxI();
x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
}
if (x0 <= x1) {
xa = x0;
for (y = y0; y <= y1; ++y) {
if (y < y1) {
xb = splashFloor(seg->x0 +
((SplashCoord)y + 1 - seg->y0) * dxdy);
} else {
xb = x1 + 1;
}
if (xa == xb) {
drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
} else {
drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
}
xa = xb;
}
} else {
xa = x0;
for (y = y0; y <= y1; ++y) {
if (y < y1) {
xb = splashFloor(seg->x0 +
((SplashCoord)y + 1 - seg->y0) * dxdy);
} else {
xb = x1 - 1;
}
if (xa == xb) {
drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
} else {
drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
}
xa = xb;
}
}
}
}
++nClipRes[clipRes];
}
if (nClipRes[splashClipPartial] ||
(nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
opClipRes = splashClipPartial;
} else if (nClipRes[splashClipAllInside]) {
opClipRes = splashClipAllInside;
} else {
opClipRes = splashClipAllOutside;
}
}
void Splash::strokeWide(SplashPath *path, SplashCoord w) {
SplashPath *path2;
path2 = makeStrokePath(path, w, false);
fillWithPattern(path2, false, state->strokePattern, state->strokeAlpha);
delete path2;
}
SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
SplashCoord flatness) {
SplashPath *fPath;
SplashCoord flatness2;
unsigned char flag;
int i;
fPath = new SplashPath();
flatness2 = flatness * flatness;
i = 0;
while (i < path->length) {
flag = path->flags[i];
if (flag & splashPathFirst) {
fPath->moveTo(path->pts[i].x, path->pts[i].y);
++i;
} else {
if (flag & splashPathCurve) {
flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
path->pts[i ].x, path->pts[i ].y,
path->pts[i+1].x, path->pts[i+1].y,
path->pts[i+2].x, path->pts[i+2].y,
matrix, flatness2, fPath);
i += 3;
} else {
fPath->lineTo(path->pts[i].x, path->pts[i].y);
++i;
}
if (path->flags[i-1] & splashPathClosed) {
fPath->close();
}
}
}
return fPath;
}
void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1,
SplashCoord x2, SplashCoord y2,
SplashCoord x3, SplashCoord y3,
SplashCoord *matrix, SplashCoord flatness2,
SplashPath *fPath) {
SplashCoord cx[splashMaxCurveSplits + 1][3];
SplashCoord cy[splashMaxCurveSplits + 1][3];
int cNext[splashMaxCurveSplits + 1];
SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
int p1, p2, p3;
// initial segment
p1 = 0;
p2 = splashMaxCurveSplits;
cx[p1][0] = x0; cy[p1][0] = y0;
cx[p1][1] = x1; cy[p1][1] = y1;
cx[p1][2] = x2; cy[p1][2] = y2;
cx[p2][0] = x3; cy[p2][0] = y3;
cNext[p1] = p2;
while (p1 < splashMaxCurveSplits) {
// get the next segment
xl0 = cx[p1][0]; yl0 = cy[p1][0];
xx1 = cx[p1][1]; yy1 = cy[p1][1];
xx2 = cx[p1][2]; yy2 = cy[p1][2];
p2 = cNext[p1];
xr3 = cx[p2][0]; yr3 = cy[p2][0];
// compute the distances (in device space) from the control points
// to the midpoint of the straight line (this is a bit of a hack,
// but it's much faster than computing the actual distances to the
// line)
transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
transform(matrix, xx1, yy1, &tx, &ty);
dx = tx - mx;
dy = ty - my;
d1 = dx*dx + dy*dy;
transform(matrix, xx2, yy2, &tx, &ty);
dx = tx - mx;
dy = ty - my;
d2 = dx*dx + dy*dy;
// if the curve is flat enough, or no more subdivisions are
// allowed, add the straight line segment
if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
fPath->lineTo(xr3, yr3);
p1 = p2;
// otherwise, subdivide the curve
} else {
xl1 = splashAvg(xl0, xx1);
yl1 = splashAvg(yl0, yy1);
xh = splashAvg(xx1, xx2);
yh = splashAvg(yy1, yy2);
xl2 = splashAvg(xl1, xh);
yl2 = splashAvg(yl1, yh);
xr2 = splashAvg(xx2, xr3);
yr2 = splashAvg(yy2, yr3);
xr1 = splashAvg(xh, xr2);
yr1 = splashAvg(yh, yr2);
xr0 = splashAvg(xl2, xr1);
yr0 = splashAvg(yl2, yr1);
// add the new subdivision points
p3 = (p1 + p2) / 2;
cx[p1][1] = xl1; cy[p1][1] = yl1;
cx[p1][2] = xl2; cy[p1][2] = yl2;
cNext[p1] = p3;
cx[p3][0] = xr0; cy[p3][0] = yr0;
cx[p3][1] = xr1; cy[p3][1] = yr1;
cx[p3][2] = xr2; cy[p3][2] = yr2;
cNext[p3] = p2;
}
}
}
SplashPath *Splash::makeDashedPath(SplashPath *path) {
SplashPath *dPath;
SplashCoord lineDashTotal;
SplashCoord lineDashStartPhase, lineDashDist, segLen;
SplashCoord x0, y0, x1, y1, xa, ya;
bool lineDashStartOn, lineDashOn, newPath;
int lineDashStartIdx, lineDashIdx;
int i, j, k;
lineDashTotal = 0;
for (i = 0; i < state->lineDashLength; ++i) {
lineDashTotal += state->lineDash[i];
}
// Acrobat simply draws nothing if the dash array is [0]
if (lineDashTotal == 0) {
return new SplashPath();
}
lineDashStartPhase = state->lineDashPhase;
i = splashFloor(lineDashStartPhase / lineDashTotal);
lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
lineDashStartOn = true;
lineDashStartIdx = 0;
if (lineDashStartPhase > 0) {
while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
lineDashStartOn = !lineDashStartOn;
lineDashStartPhase -= state->lineDash[lineDashStartIdx];
++lineDashStartIdx;
}
if (unlikely(lineDashStartIdx == state->lineDashLength)) {
return new SplashPath();
}
}
dPath = new SplashPath();
// process each subpath
i = 0;
while (i < path->length) {
// find the end of the subpath
for (j = i;
j < path->length - 1 && !(path->flags[j] & splashPathLast);
++j) ;
// initialize the dash parameters
lineDashOn = lineDashStartOn;
lineDashIdx = lineDashStartIdx;
lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
// process each segment of the subpath
newPath = true;
for (k = i; k < j; ++k) {
// grab the segment
x0 = path->pts[k].x;
y0 = path->pts[k].y;
x1 = path->pts[k+1].x;
y1 = path->pts[k+1].y;
segLen = splashDist(x0, y0, x1, y1);
// process the segment
while (segLen > 0) {
if (lineDashDist >= segLen) {
if (lineDashOn) {
if (newPath) {
dPath->moveTo(x0, y0);
newPath = false;
}
dPath->lineTo(x1, y1);
}
lineDashDist -= segLen;
segLen = 0;
} else {
xa = x0 + (lineDashDist / segLen) * (x1 - x0);
ya = y0 + (lineDashDist / segLen) * (y1 - y0);
if (lineDashOn) {
if (newPath) {
dPath->moveTo(x0, y0);
newPath = false;
}
dPath->lineTo(xa, ya);
}
x0 = xa;
y0 = ya;
segLen -= lineDashDist;
lineDashDist = 0;
}
// get the next entry in the dash array
if (lineDashDist <= 0) {
lineDashOn = !lineDashOn;
if (++lineDashIdx == state->lineDashLength) {
lineDashIdx = 0;
}
lineDashDist = state->lineDash[lineDashIdx];
newPath = true;
}
}
}
i = j + 1;
}
if (dPath->length == 0) {
bool allSame = true;
for (i = 0; allSame && i < path->length - 1; ++i) {
allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y;
}
if (allSame) {
x0 = path->pts[0].x;
y0 = path->pts[0].y;
dPath->moveTo(x0, y0);
dPath->lineTo(x0, y0);
}
}
return dPath;
}
SplashError Splash::fill(SplashPath *path, bool eo) {
if (debugMode) {
printf("fill [eo:%d]:\n", eo);
dumpPath(path);
}
return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
}
inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA,
SplashCoord *xMaxA, SplashCoord *yMaxA) {
SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty;
// make compiler happy:
xMinFP = xMaxFP = yMinFP = yMaxFP = 0;
for (int i = 0; i < path->length; ++i) {
transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty);
if (i == 0) {
xMinFP = xMaxFP = tx;
yMinFP = yMaxFP = ty;
} else {
if (tx < xMinFP) xMinFP = tx;
if (tx > xMaxFP) xMaxFP = tx;
if (ty < yMinFP) yMinFP = ty;
if (ty > yMaxFP) yMaxFP = ty;
}
}
*xMinA = xMinFP;
*yMinA = yMinFP;
*xMaxA = xMaxFP;
*yMaxA = yMaxFP;
}
SplashError Splash::fillWithPattern(SplashPath *path, bool eo,
SplashPattern *pattern,
SplashCoord alpha) {
SplashPipe pipe = {};
int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
SplashClipResult clipRes, clipRes2;
bool adjustLine = false;
int linePosI = 0;
if (path->length == 0) {
return splashErrEmptyPath;
}
if (pathAllOutside(path)) {
opClipRes = splashClipAllOutside;
return splashOk;
}
// add stroke adjustment hints for filled rectangles -- this only
// applies to paths that consist of a single subpath
// (this appears to match Acrobat's behavior)
if (state->strokeAdjust && !path->hints) {
int n;
n = path->getLength();
if (n == 4 &&
!(path->flags[0] & splashPathClosed) &&
!(path->flags[1] & splashPathLast) &&
!(path->flags[2] & splashPathLast)) {
path->close(true);
path->addStrokeAdjustHint(0, 2, 0, 4);
path->addStrokeAdjustHint(1, 3, 0, 4);
} else if (n == 5 &&
(path->flags[0] & splashPathClosed) &&
!(path->flags[1] & splashPathLast) &&
!(path->flags[2] & splashPathLast) &&
!(path->flags[3] & splashPathLast)) {
path->addStrokeAdjustHint(0, 2, 0, 4);
path->addStrokeAdjustHint(1, 3, 0, 4);
}
}
if (thinLineMode != splashThinLineDefault) {
if (state->clip->getXMinI() == state->clip->getXMaxI()) {
linePosI = state->clip->getXMinI();
adjustLine = true;
} else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) {
adjustLine = true;
linePosI = splashFloor(state->clip->getXMin() + state->lineWidth);
} else if (state->clip->getYMinI() == state->clip->getYMaxI()) {
linePosI = state->clip->getYMinI();
adjustLine = true;
} else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) {
adjustLine = true;
linePosI = splashFloor(state->clip->getYMin() + state->lineWidth);
}
}
SplashXPath xPath(path, state->matrix, state->flatness, true,
adjustLine, linePosI);
if (vectorAntialias && !inShading) {
xPath.aaScale();
}
xPath.sort();
yMinI = state->clip->getYMinI();
yMaxI = state->clip->getYMaxI();
if (vectorAntialias && !inShading) {
yMinI = yMinI * splashAASize;
yMaxI = (yMaxI + 1) * splashAASize - 1;
}
SplashXPathScanner scanner(&xPath, eo, yMinI, yMaxI);
// get the min and max x and y values
if (vectorAntialias && !inShading) {
scanner.getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
} else {
scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
}
if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) {
SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP;
getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP);
delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP;
if (delta < 0.2) {
opClipRes = splashClipAllOutside;
return splashOk;
}
}
// check clipping
if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
!= splashClipAllOutside) {
if (scanner.hasPartialClip()) {
clipRes = splashClipPartial;
}
pipeInit(&pipe, 0, yMinI, pattern, nullptr, (unsigned char)splashRound(alpha * 255),
vectorAntialias && !inShading, false);
// draw the spans
if (vectorAntialias && !inShading) {
for (y = yMinI; y <= yMaxI; ++y) {
scanner.renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
if (clipRes != splashClipAllInside) {
state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
}
unsigned char lineShape = 255;
bool doAdjustLine = false;
if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) {
// compute line shape for thin lines:
SplashCoord mx, my, delta;
transform(state->matrix, 0, 0, &mx, &my);
transform(state->matrix, state->lineWidth, 0, &delta, &my);
doAdjustLine = true;
lineShape = clip255((delta - mx) * 255);
}
drawAALine(&pipe, x0, x1, y, doAdjustLine, lineShape);
}
} else {
for (y = yMinI; y <= yMaxI; ++y) {
SplashXPathScanIterator iterator(scanner, y);
while (iterator.getNextSpan(&x0, &x1)) {
if (clipRes == splashClipAllInside) {
drawSpan(&pipe, x0, x1, y, true);
} else {
// limit the x range
if (x0 < state->clip->getXMinI()) {
x0 = state->clip->getXMinI();
}
if (x1 > state->clip->getXMaxI()) {
x1 = state->clip->getXMaxI();
}
clipRes2 = state->clip->testSpan(x0, x1, y);
drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
}
}
}
}
}
opClipRes = clipRes;
return splashOk;
}
bool Splash::pathAllOutside(SplashPath *path) {
SplashCoord xMin1, yMin1, xMax1, yMax1;
SplashCoord xMin2, yMin2, xMax2, yMax2;
SplashCoord x, y;
int xMinI, yMinI, xMaxI, yMaxI;
int i;
xMin1 = xMax1 = path->pts[0].x;
yMin1 = yMax1 = path->pts[0].y;
for (i = 1; i < path->length; ++i) {
if (path->pts[i].x < xMin1) {
xMin1 = path->pts[i].x;
} else if (path->pts[i].x > xMax1) {
xMax1 = path->pts[i].x;
}
if (path->pts[i].y < yMin1) {
yMin1 = path->pts[i].y;
} else if (path->pts[i].y > yMax1) {
yMax1 = path->pts[i].y;
}
}
transform(state->matrix, xMin1, yMin1, &x, &y);
xMin2 = xMax2 = x;
yMin2 = yMax2 = y;
transform(state->matrix, xMin1, yMax1, &x, &y);
if (x < xMin2) {
xMin2 = x;
} else if (x > xMax2) {
xMax2 = x;
}
if (y < yMin2) {
yMin2 = y;
} else if (y > yMax2) {
yMax2 = y;
}
transform(state->matrix, xMax1, yMin1, &x, &y);
if (x < xMin2) {
xMin2 = x;
} else if (x > xMax2) {
xMax2 = x;
}
if (y < yMin2) {
yMin2 = y;
} else if (y > yMax2) {
yMax2 = y;