[poppler] poppler/Function.cc poppler/Function.h poppler/GfxState.cc poppler/GfxState.h poppler/SplashOutputDev.cc poppler/SplashOutputDev.h splash/Splash.cc splash/SplashPattern.h
Albert Astals Cid
aacid at kemper.freedesktop.org
Tue Feb 15 16:07:55 PST 2011
poppler/Function.cc | 122 --------
poppler/Function.h | 3
poppler/GfxState.cc | 634 +++++++++++++++++++++++++++++++++++++++------
poppler/GfxState.h | 90 ++++--
poppler/SplashOutputDev.cc | 253 +++++++++++++++--
poppler/SplashOutputDev.h | 67 +++-
splash/Splash.cc | 126 ++++++--
splash/SplashPattern.h | 7
8 files changed, 1006 insertions(+), 296 deletions(-)
New commits:
commit e1a56d73b066e7152ccf6ccf36206def7956cb00
Author: Albert Astals Cid <aacid at kde.org>
Date: Wed Feb 16 00:06:45 2011 +0000
Lots of rendering improvements by Thomas and Andrea
Function.cc: Stitching functions incorrectly reported 0 as output size.
Function.cc: Remove cache from PostScriptFunction
Function.cc: Make PSStack stack allocated
GfxState.cc & GfxState.h: Abstract GfxSimpleShading, add Matrix::norm
method, add simple caching, parameter range computation
SplashOutputDev.cc & SplashOutputDev..h & Splash.cc & SplashPattern.h:
Improve splash rendering, implement radial and abstract simple shadings
in splash
And maybe something more, look at the
Followup Bug 32349 & Poppler: More shading fun ;-)
thread for more info
diff --git a/poppler/Function.cc b/poppler/Function.cc
index 0a72278..b63ae81 100644
--- a/poppler/Function.cc
+++ b/poppler/Function.cc
@@ -16,6 +16,7 @@
// Copyright (C) 2006, 2008-2010 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2006 Jeff Muizelaar <jeff at infidigm.net>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -39,7 +40,6 @@
#include "Stream.h"
#include "Error.h"
#include "Function.h"
-#include "PopplerCache.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
@@ -678,6 +678,7 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set<int>
}
}
+ n = funcs[0]->getOutputSize();
ok = gTrue;
return;
@@ -1048,84 +1049,6 @@ void PSStack::roll(int n, int j) {
}
}
-class PostScriptFunctionKey : public PopplerCacheKey
-{
- public:
- PostScriptFunctionKey(int sizeA, double *inA, bool copyA)
- {
- init(sizeA, inA, copyA);
- }
-
- PostScriptFunctionKey(const PostScriptFunctionKey &key)
- {
- init(key.size, key.in, key.copied);
- }
-
- void init(int sizeA, double *inA, bool copyA)
- {
- copied = copyA;
- size = sizeA;
- if (copied) {
- in = new double[size];
- for (int i = 0; i < size; ++i) in[i] = inA[i];
- } else {
- in = inA;
- }
- }
-
- ~PostScriptFunctionKey()
- {
- if (copied) delete[] in;
- }
-
- bool operator==(const PopplerCacheKey &key) const
- {
- const PostScriptFunctionKey *k = static_cast<const PostScriptFunctionKey*>(&key);
- if (size == k->size) {
- bool equal = true;
- for (int i = 0; equal && i < size; ++i) {
- equal = in[i] == k->in[i];
- }
- return equal;
- } else {
- return false;
- }
- }
-
- bool copied;
- int size;
- double *in;
-};
-
-class PostScriptFunctionItem : public PopplerCacheItem
-{
- public:
- PostScriptFunctionItem(int sizeA, double *outA)
- {
- init(sizeA, outA);
- }
-
- PostScriptFunctionItem(const PostScriptFunctionItem &item)
- {
- init(item.size, item.out);
- }
-
- void init(int sizeA, double *outA)
- {
- size = sizeA;
- out = new double[size];
- for (int i = 0; i < size; ++i) out[i] = outA[i];
- }
-
- ~PostScriptFunctionItem()
- {
- delete[] out;
- }
-
- int size;
- double *out;
-};
-
PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
Stream *str;
int codePtr;
@@ -1134,9 +1057,7 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
code = NULL;
codeString = NULL;
codeSize = 0;
- stack = NULL;
ok = gFalse;
- cache = new PopplerCache(5);
//----- initialize the generic stuff
if (!init(dict)) {
@@ -1173,8 +1094,6 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
ok = gTrue;
- stack = new PSStack();
-
err2:
str->close();
err1:
@@ -1186,58 +1105,33 @@ PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
memcpy(code, func->code, codeSize * sizeof(PSObject));
codeString = func->codeString->copy();
- stack = new PSStack();
- memcpy(stack, func->stack, sizeof(PSStack));
-
- cache = new PopplerCache(func->cache->size());
- for (int i = 0; i < func->cache->numberOfItems(); ++i)
- {
- PostScriptFunctionKey *key = new PostScriptFunctionKey(*(PostScriptFunctionKey*)func->cache->key(i));
- PostScriptFunctionItem *item = new PostScriptFunctionItem(*(PostScriptFunctionItem*)func->cache->item(i));
- cache->put(key, item);
- }
}
PostScriptFunction::~PostScriptFunction() {
gfree(code);
delete codeString;
- delete stack;
- delete cache;
}
void PostScriptFunction::transform(double *in, double *out) {
+ PSStack stack;
int i;
- PostScriptFunctionKey key(m, in, false);
- PopplerCacheItem *item = cache->lookup(key);
- if (item) {
- PostScriptFunctionItem *it = static_cast<PostScriptFunctionItem *>(item);
- for (int i = 0; i < n; ++i) {
- out[i] = it->out[i];
- }
- return;
- }
-
- stack->clear();
for (i = 0; i < m; ++i) {
//~ may need to check for integers here
- stack->pushReal(in[i]);
+ stack.pushReal(in[i]);
}
- exec(stack, 0);
+ exec(&stack, 0);
for (i = n - 1; i >= 0; --i) {
- out[i] = stack->popNum();
+ out[i] = stack.popNum();
if (out[i] < range[i][0]) {
out[i] = range[i][0];
} else if (out[i] > range[i][1]) {
out[i] = range[i][1];
}
}
+ stack.clear();
- PostScriptFunctionKey *newKey = new PostScriptFunctionKey(m, in, true);
- PostScriptFunctionItem *newItem = new PostScriptFunctionItem(n, out);
- cache->put(newKey, newItem);
-
- // if (!stack->empty()) {
+ // if (!stack.empty()) {
// error(-1, "Extra values on stack at end of PostScript function");
// }
}
diff --git a/poppler/Function.h b/poppler/Function.h
index 3541775..d6ac893 100644
--- a/poppler/Function.h
+++ b/poppler/Function.h
@@ -15,6 +15,7 @@
//
// Copyright (C) 2009, 2010 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -239,10 +240,8 @@ private:
GooString *codeString;
PSObject *code;
- PSStack *stack;
int codeSize;
GBool ok;
- PopplerCache *cache;
};
#endif
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 78ac5a8..5716f26 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -18,10 +18,11 @@
// Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc at gnome.org>
// Copyright (C) 2006-2010 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2009 Koji Otani <sho at bbr.jp>
-// Copyright (C) 2009 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2009, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2009 Christian Persch <chpe at gnome.org>
// Copyright (C) 2010 PaweÅ Wiejacha <pawel.wiejacha at gmail.com>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -34,6 +35,7 @@
#pragma implementation
#endif
+#include <algorithm>
#include <stddef.h>
#include <math.h>
#include <string.h>
@@ -51,11 +53,11 @@
//------------------------------------------------------------------------
-GBool Matrix::invertTo(Matrix *other)
+GBool Matrix::invertTo(Matrix *other) const
{
double det;
- det = 1 / (m[0] * m[3] - m[1] * m[2]);
+ det = 1 / determinant();
other->m[0] = m[3] * det;
other->m[1] = -m[1] * det;
other->m[2] = -m[2] * det;
@@ -66,7 +68,7 @@ GBool Matrix::invertTo(Matrix *other)
return gTrue;
}
-void Matrix::transform(double x, double y, double *tx, double *ty)
+void Matrix::transform(double x, double y, double *tx, double *ty) const
{
double temp_x, temp_y;
@@ -77,6 +79,21 @@ void Matrix::transform(double x, double y, double *tx, double *ty)
*ty = temp_y;
}
+// Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
+double Matrix::norm() const
+{
+ double f, g, h, i, j;
+
+ i = m[0]*m[0] + m[1]*m[1];
+ j = m[2]*m[2] + m[3]*m[3];
+
+ f = 0.5 * (i + j);
+ g = 0.5 * (i - j);
+ h = m[0]*m[2] + m[1]*m[3];
+
+ return sqrt (f + hypot (g, h));
+}
+
//------------------------------------------------------------------------
struct GfxBlendModeInfo {
@@ -2708,22 +2725,17 @@ void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
}
//------------------------------------------------------------------------
-// GfxAxialShading
+// GfxUnivariateShading
//------------------------------------------------------------------------
-GfxAxialShading::GfxAxialShading(double x0A, double y0A,
- double x1A, double y1A,
- double t0A, double t1A,
- Function **funcsA, int nFuncsA,
- GBool extend0A, GBool extend1A):
- GfxShading(2)
+GfxUnivariateShading::GfxUnivariateShading(int typeA,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxShading(typeA)
{
int i;
- x0 = x0A;
- y0 = y0A;
- x1 = x1A;
- y1 = y1A;
t0 = t0A;
t1 = t1A;
nFuncs = nFuncsA;
@@ -2732,17 +2744,19 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A,
}
extend0 = extend0A;
extend1 = extend1A;
+
+ cacheSize = 0;
+ lastMatch = 0;
+ cacheBounds = NULL;
+ cacheCoeff = NULL;
+ cacheValues = NULL;
}
-GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading *shading):
GfxShading(shading)
{
int i;
- x0 = shading->x0;
- y0 = shading->y0;
- x1 = shading->x1;
- y1 = shading->y1;
t0 = shading->t0;
t1 = shading->t1;
nFuncs = shading->nFuncs;
@@ -2751,14 +2765,174 @@ GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
}
extend0 = shading->extend0;
extend1 = shading->extend1;
+
+ cacheSize = 0;
+ lastMatch = 0;
+ cacheBounds = NULL;
+ cacheCoeff = NULL;
+ cacheValues = NULL;
}
-GfxAxialShading::~GfxAxialShading() {
+GfxUnivariateShading::~GfxUnivariateShading() {
int i;
for (i = 0; i < nFuncs; ++i) {
delete funcs[i];
}
+
+ gfree (cacheBounds);
+}
+
+void GfxUnivariateShading::getColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+ int i, nComps;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ nComps = nFuncs * funcs[0]->getOutputSize();
+
+ if (cacheSize > 0) {
+ double x, ix, *l, *u, *upper;
+
+ if (cacheBounds[lastMatch - 1] >= t) {
+ upper = std::lower_bound (cacheBounds, cacheBounds + lastMatch - 1, t);
+ lastMatch = upper - cacheBounds;
+ lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
+ } else if (cacheBounds[lastMatch] < t) {
+ upper = std::lower_bound (cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t);
+ lastMatch = upper - cacheBounds;
+ lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
+ }
+
+ x = (t - cacheBounds[lastMatch-1]) * cacheCoeff[lastMatch];
+ ix = 1.0 - x;
+ u = cacheValues + lastMatch * nComps;
+ l = u - nComps;
+
+ for (i = 0; i < nComps; ++i) {
+ out[i] = ix * l[i] + x * u[i];
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out[i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&t, &out[i]);
+ }
+ }
+
+ for (i = 0; i < nComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+void GfxUnivariateShading::setupCache(const Matrix *ctm,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ double sMin, sMax, tMin, tMax, upperBound;
+ int i, j, nComps, maxSize;
+
+ gfree (cacheBounds);
+ cacheBounds = NULL;
+ cacheSize = 0;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ nComps = nFuncs * funcs[0]->getOutputSize();
+
+ getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
+ upperBound = ctm->norm() * getDistance(sMin, sMax);
+ maxSize = ceil(upperBound);
+ maxSize = std::max<int>(maxSize, 2);
+
+ {
+ double x[4], y[4];
+
+ ctm->transform(xMin, yMin, &x[0], &y[0]);
+ ctm->transform(xMax, yMin, &x[1], &y[1]);
+ ctm->transform(xMin, yMax, &x[2], &y[2]);
+ ctm->transform(xMax, yMax, &x[3], &y[3]);
+
+ xMin = xMax = x[0];
+ yMin = yMax = y[0];
+ for (i = 1; i < 4; i++) {
+ xMin = std::min<double>(xMin, x[i]);
+ yMin = std::min<double>(yMin, y[i]);
+ xMax = std::max<double>(xMax, x[i]);
+ yMax = std::max<double>(yMax, y[i]);
+ }
+ }
+
+ if (maxSize > (xMax-xMin) * (yMax-yMin)) {
+ return;
+ }
+
+ if (t0 < t1) {
+ tMin = t0 + sMin * (t1 - t0);
+ tMax = t0 + sMax * (t1 - t0);
+ } else {
+ tMin = t0 + sMax * (t1 - t0);
+ tMax = t0 + sMin * (t1 - t0);
+ }
+
+ cacheBounds = (double *)gmallocn(maxSize, sizeof(double) * (nComps + 2));
+ cacheCoeff = cacheBounds + maxSize;
+ cacheValues = cacheCoeff + maxSize;
+
+ if (cacheSize != 0) {
+ for (j = 0; j < cacheSize; ++j) {
+ cacheCoeff[j] = 1 / (cacheBounds[j+1] - cacheBounds[j]);
+ }
+ } else if (tMax != tMin) {
+ double step = (tMax - tMin) / (maxSize - 1);
+ double coeff = (maxSize - 1) / (tMax - tMin);
+
+ cacheSize = maxSize;
+
+ for (j = 0; j < cacheSize; ++j) {
+ cacheBounds[j] = tMin + j * step;
+ cacheCoeff[j] = coeff;
+
+ for (i = 0; i < nComps; ++i) {
+ cacheValues[j*nComps + i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&cacheBounds[j], &cacheValues[j*nComps + i]);
+ }
+ }
+ }
+
+ lastMatch = 1;
+}
+
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxUnivariateShading(2, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
+{
+ x0 = x0A;
+ y0 = y0A;
+ x1 = x1A;
+ y1 = y1A;
+}
+
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+ GfxUnivariateShading(shading)
+{
+ x0 = shading->x0;
+ y0 = shading->y0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+}
+
+GfxAxialShading::~GfxAxialShading() {
}
GfxAxialShading *GfxAxialShading::parse(Dict *dict, Gfx *gfx) {
@@ -2862,79 +3036,101 @@ GfxShading *GfxAxialShading::copy() {
return new GfxAxialShading(this);
}
-void GfxAxialShading::getColor(double t, GfxColor *color) {
- double out[gfxColorMaxComps];
- int i;
+double GfxAxialShading::getDistance(double sMin, double sMax) {
+ double xMin, yMin, xMax, yMax;
- // NB: there can be one function with n outputs or n functions with
- // one output each (where n = number of color components)
- for (i = 0; i < gfxColorMaxComps; ++i) {
- out[i] = 0;
- }
- for (i = 0; i < nFuncs; ++i) {
- funcs[i]->transform(&t, &out[i]);
- }
- for (i = 0; i < gfxColorMaxComps; ++i) {
- color->c[i] = dblToCol(out[i]);
- }
+ xMin = x0 + sMin * (x1 - x0);
+ yMin = y0 + sMin * (y1 - y0);
+ xMax = x0 + sMax * (x1 - x0);
+ yMax = y0 + sMax * (y1 - y0);
+
+ return hypot(xMax-xMin, yMax-yMin);
}
+void GfxAxialShading::getParameterRange(double *lower, double *upper,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ double pdx, pdy, invsqnorm, tdx, tdy, t, range[2];
+
+ // Linear gradients are orthogonal to the line passing through their
+ // extremes. Because of convexity, the parameter range can be
+ // computed as the convex hull (one the real line) of the parameter
+ // values of the 4 corners of the box.
+ //
+ // The parameter value t for a point (x,y) can be computed as:
+ //
+ // t = (p2 - p1) . (x,y) / |p2 - p1|^2
+ //
+ // t0 is the t value for the top left corner
+ // tdx is the difference between left and right corners
+ // tdy is the difference between top and bottom corners
+
+ pdx = x1 - x0;
+ pdy = y1 - y0;
+ invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+ pdx *= invsqnorm;
+ pdy *= invsqnorm;
+
+ t = (xMin - x0) * pdx + (yMin - y0) * pdy;
+ tdx = (xMax - xMin) * pdx;
+ tdy = (yMax - yMin) * pdy;
+
+ // Because of the linearity of the t value, tdx can simply be added
+ // the t0 to move along the top edge. After this, *lower and *upper
+ // represent the parameter range for the top edge, so extending it
+ // to include the whole box simply requires adding tdy to the
+ // correct extreme.
+
+ range[0] = range[1] = t;
+ if (tdx < 0)
+ range[0] += tdx;
+ else
+ range[1] += tdx;
+
+ if (tdy < 0)
+ range[0] += tdy;
+ else
+ range[1] += tdy;
+
+ *lower = std::max<double>(0., std::min<double>(1., range[0]));
+ *upper = std::max<double>(0., std::min<double>(1., range[1]));
+}
+
//------------------------------------------------------------------------
// GfxRadialShading
//------------------------------------------------------------------------
+#ifndef RADIAL_EPSILON
+#define RADIAL_EPSILON (1. / 1024 / 1024)
+#endif
+
GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
double x1A, double y1A, double r1A,
double t0A, double t1A,
Function **funcsA, int nFuncsA,
GBool extend0A, GBool extend1A):
- GfxShading(3)
+ GfxUnivariateShading(3, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
{
- int i;
-
x0 = x0A;
y0 = y0A;
r0 = r0A;
x1 = x1A;
y1 = y1A;
r1 = r1A;
- t0 = t0A;
- t1 = t1A;
- nFuncs = nFuncsA;
- for (i = 0; i < nFuncs; ++i) {
- funcs[i] = funcsA[i];
- }
- extend0 = extend0A;
- extend1 = extend1A;
}
GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
- GfxShading(shading)
+ GfxUnivariateShading(shading)
{
- int i;
-
x0 = shading->x0;
y0 = shading->y0;
r0 = shading->r0;
x1 = shading->x1;
y1 = shading->y1;
r1 = shading->r1;
- t0 = shading->t0;
- t1 = shading->t1;
- nFuncs = shading->nFuncs;
- for (i = 0; i < nFuncs; ++i) {
- funcs[i] = shading->funcs[i]->copy();
- }
- extend0 = shading->extend0;
- extend1 = shading->extend1;
}
GfxRadialShading::~GfxRadialShading() {
- int i;
-
- for (i = 0; i < nFuncs; ++i) {
- delete funcs[i];
- }
}
GfxRadialShading *GfxRadialShading::parse(Dict *dict, Gfx *gfx) {
@@ -3030,21 +3226,313 @@ GfxShading *GfxRadialShading::copy() {
return new GfxRadialShading(this);
}
-void GfxRadialShading::getColor(double t, GfxColor *color) {
- double out[gfxColorMaxComps];
- int i;
+double GfxRadialShading::getDistance(double sMin, double sMax) {
+ double xMin, yMin, rMin, xMax, yMax, rMax;
- // NB: there can be one function with n outputs or n functions with
- // one output each (where n = number of color components)
- for (i = 0; i < gfxColorMaxComps; ++i) {
- out[i] = 0;
+ xMin = x0 + sMin * (x1 - x0);
+ yMin = y0 + sMin * (y1 - y0);
+ rMin = r0 + sMin * (r1 - r0);
+
+ xMax = x0 + sMax * (x1 - x0);
+ yMax = y0 + sMax * (y1 - y0);
+ rMax = r0 + sMax * (r1 - r0);
+
+ return hypot(xMax-xMin, yMax-yMin) + fabs(rMax-rMin);
+}
+
+// extend range, adapted from cairo, radialExtendRange
+static GBool
+radialExtendRange (double range[2], double value, GBool valid)
+{
+ if (!valid)
+ range[0] = range[1] = value;
+ else if (value < range[0])
+ range[0] = value;
+ else if (value > range[1])
+ range[1] = value;
+
+ return gTrue;
+}
+
+inline void radialEdge(double num, double den, double delta, double lower, double upper,
+ double dr, double mindr, GBool &valid, double *range)
+{
+ if (fabs (den) >= RADIAL_EPSILON) {
+ double t_edge, v;
+ t_edge = (num) / (den);
+ v = t_edge * (delta);
+ if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))
+ valid = radialExtendRange (range, t_edge, valid);
}
- for (i = 0; i < nFuncs; ++i) {
- funcs[i]->transform(&t, &out[i]);
+}
+
+inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr,
+ double dr, double mindr, GBool &valid, double *range)
+{
+ b = (x) * dx + (y) * dy + cr * dr;
+ if (fabs (b) >= RADIAL_EPSILON) {
+ double t_corner;
+ double x2 = (x) * (x);
+ double y2 = (y) * (y);
+ double cr2 = (cr) * (cr);
+ double c = x2 + y2 - cr2;
+
+ t_corner = 0.5 * c / b;
+ if (t_corner * dr >= mindr)
+ valid = radialExtendRange (range, t_corner, valid);
+ }
+}
+
+inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr,
+ double inva, double dr, double mindr, GBool &valid, double *range)
+{
+ b = (x) * dx + (y) * dy + cr * dr;
+ c = (x) * (x) + (y) * (y) - cr * cr;
+ d = b * b - a * c;
+ if (d >= 0) {
+ double t_corner;
+
+ d = sqrt (d);
+ t_corner = (b + d) * inva;
+ if (t_corner * dr >= mindr)
+ valid = radialExtendRange (range, t_corner, valid);
+ t_corner = (b - d) * inva;
+ if (t_corner * dr >= mindr)
+ valid = radialExtendRange (range, t_corner, valid);
+ }
+}
+void GfxRadialShading::getParameterRange(double *lower, double *upper,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ double cx, cy, cr, dx, dy, dr;
+ double a, x_focus, y_focus;
+ double mindr, minx, miny, maxx, maxy;
+ double range[2];
+ GBool valid;
+
+ // A radial pattern is considered degenerate if it can be
+ // represented as a solid or clear pattern. This corresponds to one
+ // of the two cases:
+ //
+ // 1) The radii are both very small:
+ // |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
+ //
+ // 2) The two circles have about the same radius and are very
+ // close to each other (approximately a cylinder gradient that
+ // doesn't move with the parameter):
+ // |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
+
+ if (xMin >= xMax || yMin >=yMax ||
+ (fabs (r0 - r1) < RADIAL_EPSILON &&
+ (std::min<double>(r0, r1) < RADIAL_EPSILON ||
+ std::max<double>(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) {
+ *lower = *upper = 0;
+ return;
}
- for (i = 0; i < gfxColorMaxComps; ++i) {
- color->c[i] = dblToCol(out[i]);
+
+ range[0] = range[1] = 0;
+ valid = gFalse;
+
+ x_focus = y_focus = 0; // silence gcc
+
+ cx = x0;
+ cy = y0;
+ cr = r0;
+ dx = x1 - cx;
+ dy = y1 - cy;
+ dr = r1 - cr;
+
+ // translate by -(cx, cy) to simplify computations
+ xMin -= cx;
+ yMin -= cy;
+ xMax -= cx;
+ yMax -= cy;
+
+ // enlarge boundaries slightly to avoid rounding problems in the
+ // parameter range computation
+ xMin -= RADIAL_EPSILON;
+ yMin -= RADIAL_EPSILON;
+ xMax += RADIAL_EPSILON;
+ yMax += RADIAL_EPSILON;
+
+ // enlarge boundaries even more to avoid rounding problems when
+ // testing if a point belongs to the box
+ minx = xMin - RADIAL_EPSILON;
+ miny = yMin - RADIAL_EPSILON;
+ maxx = xMax + RADIAL_EPSILON;
+ maxy = yMax + RADIAL_EPSILON;
+
+ // we dont' allow negative radiuses, so we will be checking that
+ // t*dr >= mindr to consider t valid
+ mindr = -(cr + RADIAL_EPSILON);
+
+ // After the previous transformations, the start circle is centered
+ // in the origin and has radius cr. A 1-unit change in the t
+ // parameter corresponds to dx,dy,dr changes in the x,y,r of the
+ // circle (center coordinates, radius).
+ //
+ // To compute the minimum range needed to correctly draw the
+ // pattern, we start with an empty range and extend it to include
+ // the circles touching the bounding box or within it.
+
+ // Focus, the point where the circle has radius == 0.
+ //
+ // r = cr + t * dr = 0
+ // t = -cr / dr
+ //
+ // If the radius is constant (dr == 0) there is no focus (the
+ // gradient represents a cylinder instead of a cone).
+ if (fabs (dr) >= RADIAL_EPSILON) {
+ double t_focus;
+
+ t_focus = -cr / dr;
+ x_focus = t_focus * dx;
+ y_focus = t_focus * dy;
+ if (minx <= x_focus && x_focus <= maxx &&
+ miny <= y_focus && y_focus <= maxy)
+ {
+ valid = radialExtendRange (range, t_focus, valid);
+ }
}
+
+ // Circles externally tangent to box edges.
+ //
+ // All circles have center in (dx, dy) * t
+ //
+ // If the circle is tangent to the line defined by the edge of the
+ // box, then at least one of the following holds true:
+ //
+ // (dx*t) + (cr + dr*t) == x0 (left edge)
+ // (dx*t) - (cr + dr*t) == x1 (right edge)
+ // (dy*t) + (cr + dr*t) == y0 (top edge)
+ // (dy*t) - (cr + dr*t) == y1 (bottom edge)
+ //
+ // The solution is only valid if the tangent point is actually on
+ // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
+ // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
+ //
+ // For the first equation:
+ //
+ // (dx + dr) * t = x0 - cr
+ // t = (x0 - cr) / (dx + dr)
+ // y = dy * t
+ //
+ // in the code this becomes:
+ //
+ // t_edge = (num) / (den)
+ // v = (delta) * t_edge
+ //
+ // If the denominator in t is 0, the pattern is tangent to a line
+ // parallel to the edge under examination. The corner-case where the
+ // boundary line is the same as the edge is handled by the focus
+ // point case and/or by the a==0 case.
+
+ // circles tangent (externally) to left/right/top/bottom edge
+ radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range);
+ radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range);
+ radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range);
+ radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range);
+
+ // Circles passing through a corner.
+ //
+ // A circle passing through the point (x,y) satisfies:
+ //
+ // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
+ //
+ // If we set:
+ // a = dx^2 + dy^2 - dr^2
+ // b = x*dx + y*dy + cr*dr
+ // c = x^2 + y^2 - cr^2
+ // we have:
+ // a*t^2 - 2*b*t + c == 0
+
+ a = dx * dx + dy * dy - dr * dr;
+ if (fabs (a) < RADIAL_EPSILON * RADIAL_EPSILON) {
+ double b;
+
+ // Ensure that gradients with both a and dr small are
+ // considered degenerate.
+ // The floating point version of the degeneracy test implemented
+ // in _radial_pattern_is_degenerate() is:
+ //
+ // 1) The circles are practically the same size:
+ // |dr| < RADIAL_EPSILON
+ // AND
+ // 2a) The circles are both very small:
+ // min (r0, r1) < RADIAL_EPSILON
+ // OR
+ // 2b) The circles are very close to each other:
+ // max (|dx|, |dy|) < 2 * RADIAL_EPSILON
+ //
+ // Assuming that the gradient is not degenerate, we want to
+ // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
+ //
+ // If the gradient is not degenerate yet it has |dr| <
+ // RADIAL_EPSILON, (2b) is false, thus:
+ //
+ // max (|dx|, |dy|) >= 2*RADIAL_EPSILON
+ // which implies:
+ // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
+ //
+ // From the definition of a, we get:
+ // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
+ // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
+ // 3*RADIAL_EPSILON^2 < dr^2
+ //
+ // which is inconsistent with the hypotheses, thus |dr| <
+ // RADIAL_EPSILON is false or the gradient is degenerate.
+
+ assert (fabs (dr) >= RADIAL_EPSILON);
+
+ // If a == 0, all the circles are tangent to a line in the
+ // focus point. If this line is within the box extents, we
+ // should add the circle with infinite radius, but this would
+ // make the range unbounded. We will be limiting the range to
+ // [0,1] anyway, so we simply add the biggest legitimate
+ // circle (it happens for 0 or for 1).
+ if (dr < 0) {
+ valid = radialExtendRange (range, 0, valid);
+ } else {
+ valid = radialExtendRange (range, 1, valid);
+ }
+
+ // Nondegenerate, nonlimit circles passing through the corners.
+ //
+ // a == 0 && a*t^2 - 2*b*t + c == 0
+ //
+ // t = c / (2*b)
+ //
+ // The b == 0 case has just been handled, so we only have to
+ // compute this if b != 0.
+
+ // circles touching each corner
+ radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range);
+ radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range);
+ radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range);
+ radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range);
+ } else {
+ double inva, b, c, d;
+
+ inva = 1 / a;
+
+ // Nondegenerate, nonlimit circles passing through the corners.
+ //
+ // a != 0 && a*t^2 - 2*b*t + c == 0
+ //
+ // t = (b +- sqrt (b*b - a*c)) / a
+ //
+ // If the argument of sqrt() is negative, then no circle
+ // passes through the corner.
+
+ // circles touching each corner
+ radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
+ radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
+ radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
+ radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
+ }
+
+ *lower = std::max<double>(0., std::min<double>(1., range[0]));
+ *upper = std::max<double>(0., std::min<double>(1., range[1]));
}
//------------------------------------------------------------------------
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index b425b4a..8d853fa 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -19,6 +19,7 @@
// Copyright (C) 2009 Koji Otani <sho at bbr.jp>
// Copyright (C) 2009, 2010 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -49,8 +50,10 @@ class Matrix {
public:
double m[6];
- GBool invertTo(Matrix *other);
- void transform(double x, double y, double *tx, double *ty);
+ GBool invertTo(Matrix *other) const;
+ void transform(double x, double y, double *tx, double *ty) const;
+ double determinant() const { return m[0] * m[3] - m[1] * m[2]; }
+ double norm() const;
};
//------------------------------------------------------------------------
@@ -746,6 +749,51 @@ protected:
};
//------------------------------------------------------------------------
+// GfxUnivariateShading
+//------------------------------------------------------------------------
+
+class GfxUnivariateShading: public GfxShading {
+public:
+
+ GfxUnivariateShading(int typeA,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A);
+ GfxUnivariateShading(GfxUnivariateShading *shading);
+ virtual ~GfxUnivariateShading();
+
+ double getDomain0() { return t0; }
+ double getDomain1() { return t1; }
+ GBool getExtend0() { return extend0; }
+ GBool getExtend1() { return extend1; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double t, GfxColor *color);
+
+ void setupCache(const Matrix *ctm,
+ double xMin, double yMin,
+ double xMax, double yMax);
+
+ virtual void getParameterRange(double *lower, double *upper,
+ double xMin, double yMin,
+ double xMax, double yMax) = 0;
+
+ virtual double getDistance(double tMin, double tMax) = 0;
+
+private:
+
+ double t0, t1;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+ GBool extend0, extend1;
+
+ int cacheSize, lastMatch;
+ double *cacheBounds;
+ double *cacheCoeff;
+ double *cacheValues;
+};
+
+//------------------------------------------------------------------------
// GfxFunctionShading
//------------------------------------------------------------------------
@@ -782,7 +830,7 @@ private:
// GfxAxialShading
//------------------------------------------------------------------------
-class GfxAxialShading: public GfxShading {
+class GfxAxialShading: public GfxUnivariateShading {
public:
GfxAxialShading(double x0A, double y0A,
@@ -799,28 +847,23 @@ public:
void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
{ *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
- double getDomain0() { return t0; }
- double getDomain1() { return t1; }
- GBool getExtend0() { return extend0; }
- GBool getExtend1() { return extend1; }
- int getNFuncs() { return nFuncs; }
- Function *getFunc(int i) { return funcs[i]; }
- void getColor(double t, GfxColor *color);
+
+ virtual void getParameterRange(double *lower, double *upper,
+ double xMin, double yMin,
+ double xMax, double yMax);
+
+ virtual double getDistance(double tMin, double tMax);
private:
double x0, y0, x1, y1;
- double t0, t1;
- Function *funcs[gfxColorMaxComps];
- int nFuncs;
- GBool extend0, extend1;
};
//------------------------------------------------------------------------
// GfxRadialShading
//------------------------------------------------------------------------
-class GfxRadialShading: public GfxShading {
+class GfxRadialShading: public GfxUnivariateShading {
public:
GfxRadialShading(double x0A, double y0A, double r0A,
@@ -838,21 +881,16 @@ public:
void getCoords(double *x0A, double *y0A, double *r0A,
double *x1A, double *y1A, double *r1A)
{ *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
- double getDomain0() { return t0; }
- double getDomain1() { return t1; }
- GBool getExtend0() { return extend0; }
- GBool getExtend1() { return extend1; }
- int getNFuncs() { return nFuncs; }
- Function *getFunc(int i) { return funcs[i]; }
- void getColor(double t, GfxColor *color);
+
+ virtual void getParameterRange(double *lower, double *upper,
+ double xMin, double yMin,
+ double xMax, double yMax);
+
+ virtual double getDistance(double tMin, double tMax);
private:
double x0, y0, r0, x1, y1, r1;
- double t0, t1;
- Function *funcs[gfxColorMaxComps];
- int nFuncs;
- GBool extend0, extend1;
};
//------------------------------------------------------------------------
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 63a471c..fc838d3 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -28,6 +28,7 @@
// Copyright (C) 2010 PaweÅ Wiejacha <pawel.wiejacha at gmail.com>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -154,50 +155,193 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor
}
//------------------------------------------------------------------------
-// SplashAxialPattern
+// SplashUnivariatePattern
//------------------------------------------------------------------------
-SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA) {
+SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) {
Matrix ctm;
+ double xMin, yMin, xMax, yMax;
shading = shadingA;
state = stateA;
colorMode = colorModeA;
+
state->getCTM(&ctm);
ctm.invertTo(&ictm);
- shading->getCoords(&x0, &y0, &x1, &y1);
- dx = x1 - x0;
- dy = y1 - y0;
- mul = 1 / (dx * dx + dy * dy);
-
// get the function domain
t0 = shading->getDomain0();
t1 = shading->getDomain1();
+ dt = t1 - t0;
+
+ stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax);
}
-SplashAxialPattern::~SplashAxialPattern() {
+SplashUnivariatePattern::~SplashUnivariatePattern() {
}
-GBool SplashAxialPattern::getColor(int x, int y, SplashColorPtr c) {
+GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) {
GfxColor gfxColor;
- double tt;
- double xc, yc, xaxis;
+ double xc, yc, t;
+
+ ictm.transform(x, y, &xc, &yc);
+ if (! getParameter (xc, yc, &t))
+ return gFalse;
+
+ shading->getColor(t, &gfxColor);
+ convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor);
+ return gTrue;
+}
+
+GBool SplashUnivariatePattern::testPosition(int x, int y) {
+ double xc, yc, t;
ictm.transform(x, y, &xc, &yc);
- xaxis = ((xc - x0) * dx + (yc - y0) * dy) * mul;
- if (xaxis < 0 && shading->getExtend0()) {
- tt = t0;
- } else if (xaxis > 1 && shading->getExtend1()) {
- tt = t1;
- } else if (xaxis >= 0 && xaxis <= 1) {
- tt = t0 + (t1 -t0) * xaxis;
- } else
+ if (! getParameter (xc, yc, &t))
+ return gFalse;
+ return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0);
+}
+
+
+//------------------------------------------------------------------------
+// SplashRadialPattern
+//------------------------------------------------------------------------
+#define RADIAL_EPSILON (1. / 1024 / 1024)
+
+SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA):
+ SplashUnivariatePattern(colorModeA, stateA, shadingA)
+{
+ shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr);
+ dx -= x0;
+ dy -= y0;
+ dr -= r0;
+ a = dx*dx + dy*dy - dr*dr;
+ if (fabs(a) > RADIAL_EPSILON)
+ inva = 1.0 / a;
+}
+
+SplashRadialPattern::~SplashRadialPattern() {
+}
+
+GBool SplashRadialPattern::getParameter(double xs, double ys, SplashCoord *t) {
+ double b, c, s0, s1;
+
+ // We want to solve this system of equations:
+ //
+ // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2
+ // 2. xc(s) = x0 + s * (x1 - xo)
+ // 3. yc(s) = y0 + s * (y1 - yo)
+ // 4. rc(s) = r0 + s * (r1 - ro)
+ //
+ // To simplify the system a little, we translate
+ // our coordinates to have the origin in (x0,y0)
+
+ xs -= x0;
+ ys -= y0;
+
+ // Then we have to solve the equation:
+ // A*s^2 - 2*B*s + C = 0
+ // where
+ // A = dx^2 + dy^2 - dr^2
+ // B = xs*dx + ys*dy + r0*dr
+ // C = xs^2 + ys^2 - r0^2
+
+ b = xs*dx + ys*dy + r0*dr;
+ c = xs*xs + ys*ys - r0*r0;
+
+ if (fabs(a) <= RADIAL_EPSILON) {
+ // A is 0, thus the equation simplifies to:
+ // -2*B*s + C = 0
+ // If B is 0, we can either have no solution or an indeterminate
+ // equation, thus we behave as if we had an invalid solution
+ if (fabs(b) <= RADIAL_EPSILON)
+ return gFalse;
+
+ s0 = s1 = 0.5 * c / b;
+ } else {
+ double d;
+
+ d = b*b - a*c;
+ if (d < 0)
+ return gFalse;
+
+ d = sqrt (d);
+ s0 = b + d;
+ s1 = b - d;
+
+ // If A < 0, one of the two solutions will have negative radius,
+ // thus it will be ignored. Otherwise we know that s1 <= s0
+ // (because d >=0 implies b - d <= b + d), so if both are valid it
+ // will be the true solution.
+ s0 *= inva;
+ s1 *= inva;
+ }
+
+ if (r0 + s0 * dr >= 0) {
+ if (0 <= s0 && s0 <= 1) {
+ *t = t0 + dt * s0;
+ return gTrue;
+ } else if (s0 < 0 && shading->getExtend0()) {
+ *t = t0;
+ return gTrue;
+ } else if (s0 > 1 && shading->getExtend1()) {
+ *t = t1;
+ return gTrue;
+ }
+ }
+
+ if (r0 + s1 * dr >= 0) {
+ if (0 <= s1 && s1 <= 1) {
+ *t = t0 + dt * s1;
+ return gTrue;
+ } else if (s1 < 0 && shading->getExtend0()) {
+ *t = t0;
+ return gTrue;
+ } else if (s1 > 1 && shading->getExtend1()) {
+ *t = t1;
+ return gTrue;
+ }
+ }
+
+ return gFalse;
+}
+
+#undef RADIAL_EPSILON
+
+//------------------------------------------------------------------------
+// SplashAxialPattern
+//------------------------------------------------------------------------
+
+SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA):
+ SplashUnivariatePattern(colorModeA, stateA, shadingA)
+{
+ shadingA->getCoords(&x0, &y0, &x1, &y1);
+ dx = x1 - x0;
+ dy = y1 - y0;
+ mul = 1 / (dx * dx + dy * dy);
+}
+
+SplashAxialPattern::~SplashAxialPattern() {
+}
+
+GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
+ double s;
+
+ xc -= x0;
+ yc -= y0;
+
+ s = (xc * dx + yc * dy) * mul;
+ if (0 <= s && s <= 1) {
+ *t = t0 + dt * s;
+ } else if (s < 0 && shading->getExtend0()) {
+ *t = t0;
+ } else if (s > 1 && shading->getExtend1()) {
+ *t = t1;
+ } else {
return gFalse;
+ }
- shading->getColor(tt, &gfxColor);
- state->setFillColor(&gfxColor);
- convertGfxColor(c, colorMode, state->getFillColorSpace(), state->getFillColor());
return gTrue;
}
@@ -3317,6 +3461,7 @@ GBool SplashOutputDev::getVectorAntialias() {
}
void SplashOutputDev::setVectorAntialias(GBool vaa) {
+ vectorAntialias = vaa;
splash->setVectorAntialias(vaa);
}
#endif
@@ -3356,16 +3501,48 @@ GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTria
return gFalse;
}
-GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
+GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) {
double xMin, yMin, xMax, yMax;
SplashPath *path;
-
GBool vaa = getVectorAntialias();
- GBool retVal = gFalse;
// restore vector antialias because we support it here
setVectorAntialias(gTrue);
+
+ GBool retVal = gFalse;
// get the clip region bbox
- state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ if (pattern->getShading()->getHasBBox()) {
+ pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax);
+ } else {
+ state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ xMin = floor (xMin);
+ yMin = floor (yMin);
+ xMax = ceil (xMax);
+ yMax = ceil (yMax);
+
+ {
+ Matrix ctm, ictm;
+ double x[4], y[4];
+ int i;
+
+ state->getCTM(&ctm);
+ ctm.invertTo(&ictm);
+
+ ictm.transform(xMin, yMin, &x[0], &y[0]);
+ ictm.transform(xMax, yMin, &x[1], &y[1]);
+ ictm.transform(xMin, yMax, &x[2], &y[2]);
+ ictm.transform(xMax, yMax, &x[3], &y[3]);
+
+ xMin = xMax = x[0];
+ yMin = yMax = y[0];
+ for (i = 1; i < 4; i++) {
+ xMin = std::min<double>(xMin, x[i]);
+ yMin = std::min<double>(yMin, y[i]);
+ xMax = std::max<double>(xMax, x[i]);
+ yMax = std::max<double>(yMax, y[i]);
+ }
+ }
+ }
// fill the region
state->moveTo(xMin, yMin);
@@ -3375,12 +3552,28 @@ GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading
state->closePath();
path = convertPath(state, state->getPath());
- SplashPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
- retVal = (splash->shadedFill(path, shading->getHasBBox(), pattern) == splashOk);
- setVectorAntialias(vaa);
+ retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
state->clearPath();
- delete pattern;
+ setVectorAntialias(vaa);
delete path;
return retVal;
}
+
+GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
+ SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
+ GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
+
+ delete pattern;
+
+ return retVal;
+}
+
+GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) {
+ SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading);
+ GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
+
+ delete pattern;
+
+ return retVal;
+}
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index 47161c0..c8cb84f 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -14,10 +14,11 @@
// under GPL version 2 or later
//
// Copyright (C) 2005 Takashi Iwai <tiwai at suse.de>
-// Copyright (C) 2009, 2010 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2009-2011 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2009 Carlos Garcia Campos <carlosgc at gnome.org>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.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
@@ -53,28 +54,45 @@ struct SplashTransparencyGroup;
// Splash dynamic pattern
//------------------------------------------------------------------------
-class SplashAxialPattern: public SplashPattern {
+class SplashUnivariatePattern: public SplashPattern {
public:
- SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading);
-
- virtual SplashPattern *copy() { return new SplashAxialPattern(colorMode, state, shading); }
+ SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading);
- virtual ~SplashAxialPattern();
+ virtual ~SplashUnivariatePattern();
virtual GBool getColor(int x, int y, SplashColorPtr c);
+ virtual GBool testPosition(int x, int y);
+
virtual GBool isStatic() { return gFalse; }
-private:
+ virtual GBool getParameter(double xs, double ys, double *t) = 0;
+
+ virtual GfxUnivariateShading *getShading() { return shading; }
+
+protected:
Matrix ictm;
- double x0, y0, x1, y1;
- double dx, dy, mul;
- double t0, t1;
- GfxAxialShading *shading;
+ double t0, t1, dt;
+ GfxUnivariateShading *shading;
GfxState *state;
SplashColorMode colorMode;
- double *bbox;
+};
+
+class SplashAxialPattern: public SplashUnivariatePattern {
+public:
+
+ SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading);
+
+ virtual SplashPattern *copy() { return new SplashAxialPattern(colorMode, state, (GfxAxialShading *) shading); }
+
+ virtual ~SplashAxialPattern();
+
+ virtual GBool getParameter(double xs, double ys, double *t);
+
+private:
+ double x0, y0, x1, y1;
+ double dx, dy, mul;
};
// see GfxState.h, GfxGouraudTriangleShading
@@ -89,13 +107,15 @@ public:
virtual GBool getColor(int x, int y, SplashColorPtr c) { return gFalse; }
+ virtual GBool testPosition(int x, int y) { return gFalse; }
+
virtual GBool isStatic() { return gFalse; }
virtual GBool isParameterized() { return shading->isParameterized(); }
virtual int getNTriangles() { return shading->getNTriangles(); }
virtual void getTriangle(int i, double *x0, double *y0, double *color0,
double *x1, double *y1, double *color1,
- double *x2, double *y2, double *color2)
+ double *x2, double *y2, double *color2)
{ return shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); }
virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c);
@@ -106,6 +126,23 @@ private:
GBool bDirectColorTranslation;
};
+// see GfxState.h, GfxRadialShading
+class SplashRadialPattern: public SplashUnivariatePattern {
+public:
+
+ SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading);
+
+ virtual SplashPattern *copy() { return new SplashRadialPattern(colorMode, state, (GfxRadialShading *) shading); }
+
+ virtual ~SplashRadialPattern();
+
+ virtual GBool getParameter(double xs, double ys, double *t);
+
+private:
+ double x0, y0, r0, dx, dy, dr;
+ double a, inva;
+};
+
//------------------------------------------------------------------------
// number of Type 3 fonts to cache
@@ -133,7 +170,7 @@ public:
// radialShadedFill()? If this returns false, these shaded fills
// will be reduced to a series of other drawing operations.
virtual GBool useShadedFills(int type)
- { return (type == 2 || type == 4 || type == 5 ) ? gTrue : gFalse; }
+ { return (type >= 2 && type <= 5) ? gTrue : gFalse; }
// Does this device use upside-down coordinates?
// (Upside-down means (0,0) is the top left corner of the page.)
@@ -187,6 +224,7 @@ public:
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax);
+ virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax);
virtual GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading);
//----- path clipping
@@ -292,6 +330,7 @@ public:
void setFreeTypeHinting(GBool enable, GBool enableSlightHinting);
private:
+ GBool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax);
void setupScreenParams(double hDPI, double vDPI);
#if SPLASH_CMYK
diff --git a/splash/Splash.cc b/splash/Splash.cc
index bc317a6..bf24892 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -13,7 +13,7 @@
//
// Copyright (C) 2005-2010 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2005 Marco Pesenti Gritti <mpg at redhat.com>
-// Copyright (C) 2010 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2010, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
//
// To see a description of the changes please see the Changelog file that
@@ -253,30 +253,8 @@ inline void Splash::pipeRun(SplashPipe *pipe) {
// dynamic pattern
if (pipe->pattern) {
if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) {
- switch (bitmap->mode) {
- case splashModeMono1:
- if (!(pipe->destColorMask >>= 1))
- ++pipe->destColorPtr;
- break;
- case splashModeMono8:
- ++pipe->destColorPtr;
- break;
- case splashModeBGR8:
- case splashModeRGB8:
- pipe->destColorPtr += 3;
- break;
- case splashModeXBGR8:
-#if SPLASH_CMYK
- case splashModeCMYK8:
-#endif
- pipe->destColorPtr += 4;
- break;
- }
- if (pipe->destAlphaPtr) {
- ++pipe->destAlphaPtr;
- }
- ++pipe->x;
- return;
+ pipeIncX(pipe);
+ return;
}
}
@@ -2395,7 +2373,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
// loop-invariant constants
k1 = splashRound(xShear * ySign * y);
-
+
// clipping test
if (clipRes != splashClipAllInside &&
!rot &&
@@ -3097,7 +3075,7 @@ void Splash::compositeBackground(SplashColorPtr color) {
Guchar color3;
#endif
int x, y, mask;
-
+
if (unlikely(bitmap->alpha == NULL)) {
error(-1, "bitmap->alpha is NULL in Splash::compositeBackground");
return;
@@ -3282,7 +3260,7 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse);
if (vectorAntialias) {
- if (aaBuf == NULL)
+ if (aaBuf == NULL)
return gFalse; // fall back to old behaviour
drawAAPixelInit();
}
@@ -3950,19 +3928,25 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox,
int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
SplashClipResult clipRes;
- if (aaBuf == NULL) { // should not happen, but to be secure
+ if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure
return splashErrGeneric;
}
if (path->length == 0) {
return splashErrEmptyPath;
}
xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
- xPath->aaScale();
+ if (vectorAntialias) {
+ xPath->aaScale();
+ }
xPath->sort();
scanner = new SplashXPathScanner(xPath, gFalse);
// get the min and max x and y values
- scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
+ if (vectorAntialias) {
+ scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
+ } else {
+ scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+ }
// check clipping
if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
@@ -3977,12 +3961,82 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox,
pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias && !hasBBox, gFalse);
// draw the spans
- for (y = yMinI; y <= yMaxI; ++y) {
- scanner->renderAALine(aaBuf, &x0, &x1, y);
- if (clipRes != splashClipAllInside) {
- state->clip->clipAALine(aaBuf, &x0, &x1, y);
+ if (vectorAntialias) {
+ for (y = yMinI; y <= yMaxI; ++y) {
+ scanner->renderAALine(aaBuf, &x0, &x1, y);
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipAALine(aaBuf, &x0, &x1, y);
+ }
+#if splashAASize == 4
+ if (!hasBBox && y > yMinI && y < yMaxI) {
+ // correct shape on left side if clip is
+ // vertical through the middle of shading:
+ Guchar *p0, *p1, *p2, *p3;
+ Guchar c1, c2, c3, c4;
+ p0 = aaBuf->getDataPtr() + (x0 >> 1);
+ p1 = p0 + aaBuf->getRowSize();
+ p2 = p1 + aaBuf->getRowSize();
+ p3 = p2 + aaBuf->getRowSize();
+ if (x0 & 1) {
+ c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
+ } else {
+ c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
+ }
+ if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03
+ && c1 == c2 && c2 == c3 && c3 == c4 &&
+ pattern->testPosition(x0 - 1, y) )
+ {
+ Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0;
+ *p0 |= shapeCorrection;
+ *p1 |= shapeCorrection;
+ *p2 |= shapeCorrection;
+ *p3 |= shapeCorrection;
+ }
+ // correct shape on right side if clip is
+ // through the middle of shading:
+ p0 = aaBuf->getDataPtr() + (x1 >> 1);
+ p1 = p0 + aaBuf->getRowSize();
+ p2 = p1 + aaBuf->getRowSize();
+ p3 = p2 + aaBuf->getRowSize();
+ if (x1 & 1) {
+ c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
+ } else {
+ c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
+ }
+
+ if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c
+ && c1 == c2 && c2 == c3 && c3 == c4 &&
+ pattern->testPosition(x1 + 1, y) )
+ {
+ Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0;
+ *p0 |= shapeCorrection;
+ *p1 |= shapeCorrection;
+ *p2 |= shapeCorrection;
+ *p3 |= shapeCorrection;
+ }
+ }
+#endif
+ drawAALine(&pipe, x0, x1, y);
+ }
+ } else {
+ SplashClipResult clipRes2;
+ for (y = yMinI; y <= yMaxI; ++y) {
+ while (scanner->getNextSpan(y, &x0, &x1)) {
+ if (clipRes == splashClipAllInside) {
+ drawSpan(&pipe, x0, x1, y, gTrue);
+ } 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);
+ }
+ }
}
- drawAALine(&pipe, x0, x1, y);
}
}
opClipRes = clipRes;
diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h
index 09e9b1a..42c1660 100644
--- a/splash/SplashPattern.h
+++ b/splash/SplashPattern.h
@@ -11,7 +11,7 @@
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
-// Copyright (C) 2010 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2010, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
//
// 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
@@ -45,6 +45,9 @@ public:
// Return the color value for a specific pixel.
virtual GBool getColor(int x, int y, SplashColorPtr c) = 0;
+ // Test if x,y-position is inside pattern.
+ virtual GBool testPosition(int x, int y) = 0;
+
// Returns true if this pattern object will return the same color
// value for all pixels.
virtual GBool isStatic() = 0;
@@ -67,6 +70,8 @@ public:
virtual GBool getColor(int x, int y, SplashColorPtr c);
+ virtual GBool testPosition(int x, int y) { return gFalse; }
+
virtual GBool isStatic() { return gTrue; }
private:
More information about the poppler
mailing list