blob: 5b247ece34e10bf93133eb4e82078feb21dc8e97 [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-2023 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, 2020 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
// Copyright (C) 2020 Tobias Deiminger <haxtibal@posteo.de>
// Copyright (C) 2021, 2024 Even Rouault <even.rouault@spatialys.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 <cstdlib>
#include <cstring>
#include <climits>
#include <cassert>
#include <cmath>
#include "goo/gmem.h"
#include "goo/GooLikely.h"
#include "poppler/GfxState.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>
// the MSVC math.h doesn't define this
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
//------------------------------------------------------------------------
#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];
}
}
//------------------------------------------------------------------------
// 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);
}
}
inline void Splash::drawAAPixelInit()
{
aaBufY = -1;
}
inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y)
{
#if splashAASize == 4
static const 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(static_cast<int>(aaGamma[t] * pipe->shape));
(this->*pipe->run)(pipe);
}
}
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);
}
} 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);
} else {
pipeIncX(pipe);
}
}
}
}
inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine, unsigned char lineOpacity)
{
#if splashAASize == 4
static const 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(static_cast<int>((int)lineOpacity * (double)aaGamma[t])) : (int)aaGamma[t];
(this->*pipe->run)(pipe);
} else {
pipeIncX(pipe);
}
}
}
//------------------------------------------------------------------------
// Transform a point from user space to device space.
inline void Splash::transform(const 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;
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;
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::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 sop)
{
state->strokeOverprint = sop;
}
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(std::vector<SplashCoord> &&lineDash, SplashCoord lineDashPhase)
{
state->setLineDash(std::move(lineDash), 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);
}
}
SplashError Splash::stroke(SplashPath *path)
{
SplashPath *path2, *dPath;
SplashCoord d1, d2, t1, t2, w;
if (debugMode) {
printf("stroke [dash:%zu] [width:%.2f]:\n", state->lineDash.size(), (double)state->lineWidth);
dumpPath(path);
}
opClipRes = splashClipAllOutside;
if (path->length == 0) {
return splashErrEmptyPath;
}
path2 = flattenPath(path, state->matrix, state->flatness);
if (!state->lineDash.empty()) {
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 + (state->clip->getYMin() - seg->y0) * dxdy);
}
if (y1 > state->clip->getYMaxI()) {
y1 = state->clip->getYMaxI();
x1 = splashFloor(seg->x0 + (state->clip->getYMax() - 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 i, j, k;
lineDashTotal = 0;
for (SplashCoord dash : state->lineDash) {
lineDashTotal += dash;
}
// 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;
size_t lineDashStartIdx = 0;
if (lineDashStartPhase > 0) {
while (lineDashStartIdx < state->lineDash.size() && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
lineDashStartOn = !lineDashStartOn;
lineDashStartPhase -= state->lineDash[lineDashStartIdx];
++lineDashStartIdx;
}
if (unlikely(lineDashStartIdx == state->lineDash.size())) {
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;
size_t 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->lineDash.size()) {
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(static_cast<int>((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;
}
transform(state->matrix, xMax1, 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;
}
xMinI = splashFloor(xMin2);
yMinI = splashFloor(yMin2);
xMaxI = splashFloor(xMax2);
yMaxI = splashFloor(yMax2);
return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == splashClipAllOutside;
}
SplashError Splash::xorFill(SplashPath *path, bool eo)
{
SplashPipe pipe;
int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
SplashClipResult clipRes, clipRes2;
SplashBlendFunc origBlendFunc;
if (path->length == 0) {
return splashErrEmptyPath;
}
SplashXPath xPath(path, state->matrix, state->flatness, true);
xPath.sort();
SplashXPathScanner scanner(xPath, eo, state->clip->getYMinI(), state->clip->getYMaxI());
// 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) {
if (scanner.hasPartialClip()) {
clipRes = splashClipPartial;
}
origBlendFunc = state->blendFunc;
state->blendFunc = &blendXor;
pipeInit(&pipe, 0, yMinI, state->fillPattern, nullptr, 255, false, false);