Dear Albert,
thank you for your time to perform the regression tests!
I have fixed the bug; it was a data type problem.
Attached you find the fixed version.
The file
bugfix_shadingtype4567_incremental.patch
is relative to the version you used for the regression tests.
The file
bugfix_shadingtype4567_poppler0.14.patch
is relative to poppler-0.14.0-3-gb2427d0 .
Thank you for considering my contributions.
Best regards
Christian
Am 25.07.2010 16:56, schrieb Albert Astals Cid:
A Dissabte, 3 de juliol de 2010, Christian Feuersaenger va escriure:
Hello Albert,
Hi
I've managed to fix a bug in the Shading Type 6/7 (Coons& cubic tensor
patches) implementation.
The bugfix is small and stable (in my eyes); the poppler-0.14 branch
doesn't implement support for parameterized patch shadings. I modified
the existing implementation accordingly with relatively few changes.
Attached you find the patch file and the updated test.pdf to see the
improvement.
The file type4567patch.... also includes the patch of my previous mail
(they only share the same refinement threshold).
The patch should work relative to poppler-0.14.0-3-gb2427d0 .
This patch causes a regression in the attached pdf (the blue area disappears)
Albert
Best regards
Christian
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler
diff --git a/.gitignore b/.gitignore
index d000352..be47f98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ poppler.pc
stamp-h1
gtk-doc.make
*.o
+.*.sw?
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 221caef..cf1958e 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -99,18 +99,26 @@
#define radialColorDelta (dblToCol(1 / 256.0))
// Max recursive depth for a Gouraud triangle shading fill.
+//
+// Triangles will be split at most gouraudMaxDepth times (each time into 4
+// smaller ones). That makes pow(4,gouraudMaxDepth) many triangles for
+// every triangle.
#define gouraudMaxDepth 6
// Max delta allowed in any color component for a Gouraud triangle
// shading fill.
-#define gouraudColorDelta (dblToCol(1 / 256.0))
+#define gouraudColorDelta (dblToCol(3. / 256.0))
+
+// Gouraud triangle: if the three color parameters differ by at more than this percend of
+// the total color parameter range, the triangle will be refined
+#define gouraudParameterizedColorDelta 5e-3
// Max recursive depth for a patch mesh shading fill.
#define patchMaxDepth 6
// Max delta allowed in any color component for a patch mesh shading
// fill.
-#define patchColorDelta (dblToCol(1 / 256.0))
+#define patchColorDelta (dblToCol((3. / 256.0)))
//------------------------------------------------------------------------
// Operator table
@@ -3087,22 +3095,63 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
double x0, y0, x1, y1, x2, y2;
- GfxColor color0, color1, color2;
int i;
- for (i = 0; i < shading->getNTriangles(); ++i) {
- shading->getTriangle(i, &x0, &y0, &color0,
- &x1, &y1, &color1,
- &x2, &y2, &color2);
- gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
- shading->getColorSpace()->getNComps(), 0);
- }
+ // preallocate a path.
+ // Overwriting the coordinates saves about 10% at runtime for larger
+ // shadings.
+ state->moveTo(0., 0.);
+ state->lineTo(1., 0.);
+ state->lineTo(0., 1.);
+ state->closePath();
+
+ GfxState::ReusablePathIterator *reusablePath = state->getReusablePath();
+
+// Idea:
+// - state->getReferenceToReusablePath()
+// the return value has the setX/setY methods, the standard GfxState doesn't
+// - state should not delete the current path until
+// - state->freeReusablePath()
+// but that's overhead and I am not highly motivated.
+//
+
+ if (shading->isParameterized()) {
+ // work with parameterized values:
+ double color0, color1, color2;
+ // a relative threshold:
+ const double refineColorThreshold = gouraudParameterizedColorDelta *
+ (shading->getParameterDomainMax() - shading->getParameterDomainMin());
+ for (i = 0; i < shading->getNTriangles(); ++i) {
+ shading->getTriangle(i, &x0, &y0, &color0,
+ &x1, &y1, &color1,
+ &x2, &y2, &color2);
+ gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath);
+ }
+
+ } else {
+ // this always produces output -- even for parameterized ranges.
+ // But it ignores the parameterized color map (the function).
+ //
+ // Note that using this code in for parameterized shadings might be
+ // correct in circumstances (namely if the function is linear in the actual
+ // triangle), but in general, it will simply be wrong.
+ GfxColor color0, color1, color2;
+ for (i = 0; i < shading->getNTriangles(); ++i) {
+ shading->getTriangle(i, &x0, &y0, &color0,
+ &x1, &y1, &color1,
+ &x2, &y2, &color2);
+ gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath);
+ }
+ }
+
+ delete reusablePath;
}
+
void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
double x1, double y1, GfxColor *color1,
double x2, double y2, GfxColor *color2,
- int nComps, int depth) {
+ int nComps, int depth, GfxState::ReusablePathIterator* path) {
double x01, y01, x12, y12, x20, y20;
GfxColor color01, color12, color20;
int i;
@@ -3114,15 +3163,17 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
}
}
if (i == nComps || depth == gouraudMaxDepth) {
+
state->setFillColor(color0);
out->updateFillColor(state);
- state->moveTo(x0, y0);
- state->lineTo(x1, y1);
- state->lineTo(x2, y2);
- state->closePath();
- if (!contentIsHidden())
- out->fill(state);
- state->clearPath();
+
+ path->reset(); assert(!path->isEnd());
+ path->setCoord(x0,y0); path->next(); assert(!path->isEnd());
+ path->setCoord(x1,y1); path->next(); assert(!path->isEnd());
+ path->setCoord(x2,y2); path->next(); assert(!path->isEnd());
+ path->setCoord(x0,y0); path->next(); assert( path->isEnd());
+
+ out->fill(state);
} else {
x01 = 0.5 * (x0 + x1);
y01 = 0.5 * (y0 + y1);
@@ -3130,23 +3181,78 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
y12 = 0.5 * (y1 + y2);
x20 = 0.5 * (x2 + x0);
y20 = 0.5 * (y2 + y0);
- //~ if the shading has a Function, this should interpolate on the
- //~ function parameter, not on the color components
for (i = 0; i < nComps; ++i) {
- color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
- color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
- color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
+ color01.c[i] = (color0->c[i] + color1->c[i]) / 2.;
+ color12.c[i] = (color1->c[i] + color2->c[i]) / 2.;
+ color20.c[i] = (color2->c[i] + color0->c[i]) / 2.;
}
gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
- x20, y20, &color20, nComps, depth + 1);
+ x20, y20, &color20, nComps, depth + 1, path);
gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
- x12, y12, &color12, nComps, depth + 1);
+ x12, y12, &color12, nComps, depth + 1, path);
gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
- x20, y20, &color20, nComps, depth + 1);
+ x20, y20, &color20, nComps, depth + 1, path);
gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
- x2, y2, color2, nComps, depth + 1);
+ x2, y2, color2, nComps, depth + 1, path);
}
}
+void Gfx::gouraudFillTriangle(double x0, double y0, double color0,
+ double x1, double y1, double color1,
+ double x2, double y2, double color2,
+ double refineColorThreshold, int depth, GfxGouraudTriangleShading* shading, GfxState::ReusablePathIterator* path ) {
+ const double meanColor = (color0 + color1 + color2) / 3;
+
+ const bool isFineEnough =
+ fabs(color0 - meanColor) < refineColorThreshold &&
+ fabs(color1 - meanColor) < refineColorThreshold &&
+ fabs(color2 - meanColor) < refineColorThreshold;
+
+ if (isFineEnough || depth == gouraudMaxDepth) {
+ GfxColor color;
+
+ shading->getParameterizedColor(meanColor, &color);
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+
+ path->reset(); assert(!path->isEnd());
+ path->setCoord(x0,y0); path->next(); assert(!path->isEnd());
+ path->setCoord(x1,y1); path->next(); assert(!path->isEnd());
+ path->setCoord(x2,y2); path->next(); assert(!path->isEnd());
+ path->setCoord(x0,y0); path->next(); assert( path->isEnd());
+
+ out->fill(state);
+ } else {
+ double x01, y01, x12, y12, x20, y20;
+ double color01, color12, color20;
+ x01 = 0.5 * (x0 + x1);
+ y01 = 0.5 * (y0 + y1);
+ x12 = 0.5 * (x1 + x2);
+ y12 = 0.5 * (y1 + y2);
+ x20 = 0.5 * (x2 + x0);
+ y20 = 0.5 * (y2 + y0);
+ color01 = (color0 + color1) / 2.;
+ color12 = (color1 + color2) / 2.;
+ color20 = (color2 + color0) / 2.;
+ ++depth;
+ gouraudFillTriangle(x0, y0, color0,
+ x01, y01, color01,
+ x20, y20, color20,
+ refineColorThreshold, depth, shading, path);
+ gouraudFillTriangle(x01, y01, color01,
+ x1, y1, color1,
+ x12, y12, color12,
+ refineColorThreshold, depth, shading, path);
+ gouraudFillTriangle(x01, y01, color01,
+ x12, y12, color12,
+ x20, y20, color20,
+ refineColorThreshold, depth, shading, path);
+ gouraudFillTriangle(x20, y20, color20,
+ x12, y12, color12,
+ x2, y2, color2,
+ refineColorThreshold, depth, shading, path);
+ }
+}
+
void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
int start, i;
@@ -3160,32 +3266,73 @@ void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
} else {
start = 0;
}
+ /*
+ * Parameterized shadings take one parameter [t_0,t_e]
+ * and map it into the color space.
+ *
+ * Consequently, all color values are stored as doubles.
+ *
+ * These color values are interpreted as parameters for parameterized
+ * shadings and as colorspace entities otherwise.
+ *
+ * The only difference is that color space entities are stored into
+ * DOUBLE arrays, not into arrays of type GfxColorComp.
+ */
+ int colorComps = shading->getColorSpace()->getNComps();
+ double refineColorThreshold;
+ if( shading->isParameterized() ) {
+ refineColorThreshold = gouraudParameterizedColorDelta *
+ (shading->getParameterDomainMax() - shading->getParameterDomainMin());
+
+ } else {
+ refineColorThreshold = patchColorDelta;
+ }
+
for (i = 0; i < shading->getNPatches(); ++i) {
- fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
- start);
+ fillPatch(shading->getPatch(i),
+ colorComps,
+ shading->isParameterized() ? 1 : colorComps,
+ refineColorThreshold,
+ start,
+ shading);
}
}
-void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+
+void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, GfxPatchMeshShading *shading ) {
GfxPatch patch00, patch01, patch10, patch11;
double xx[4][8], yy[4][8];
double xxm, yym;
int i;
- for (i = 0; i < nComps; ++i) {
- if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
- > patchColorDelta ||
- abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
- > patchColorDelta ||
- abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
- > patchColorDelta ||
- abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
- > patchColorDelta) {
+ for (i = 0; i < patchColorComps; ++i) {
+ // these comparisons are done in double arithmetics.
+ //
+ // For non-parameterized shadings, they are done in color space
+ // components.
+ if (fabs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
+ > refineColorThreshold ||
+ fabs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
+ > refineColorThreshold ||
+ fabs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
+ > refineColorThreshold ||
+ fabs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
+ > refineColorThreshold) {
break;
}
}
- if (i == nComps || depth == patchMaxDepth) {
- state->setFillColor(&patch->color[0][0]);
+ if (i == patchColorComps || depth == patchMaxDepth) {
+ GfxColor flatColor;
+ if( shading->isParameterized() ) {
+ shading->getParameterizedColor( patch->color[0][0].c[0], &flatColor );
+
+ } else {
+ for( i = 0; i<colorComps; ++i ) {
+ // simply cast to the desired type; that's all what is needed.
+ flatColor.c[i] = GfxColorComp(patch->color[0][0].c[i]);
+ }
+ }
+ state->setFillColor(&flatColor);
out->updateFillColor(state);
state->moveTo(patch->x[0][0], patch->y[0][0]);
state->curveTo(patch->x[0][1], patch->y[0][1],
@@ -3263,9 +3410,7 @@ void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
patch11.x[3][i-4] = xx[3][i];
patch11.y[3][i-4] = yy[3][i];
}
- //~ if the shading has a Function, this should interpolate on the
- //~ function parameter, not on the color components
- for (i = 0; i < nComps; ++i) {
+ for (i = 0; i < patchColorComps; ++i) {
patch00.color[0][0].c[i] = patch->color[0][0].c[i];
patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
patch->color[0][1].c[i]) / 2;
@@ -3288,10 +3433,10 @@ void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
}
- fillPatch(&patch00, nComps, depth + 1);
- fillPatch(&patch10, nComps, depth + 1);
- fillPatch(&patch01, nComps, depth + 1);
- fillPatch(&patch11, nComps, depth + 1);
+ fillPatch(&patch00, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
+ fillPatch(&patch10, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
+ fillPatch(&patch01, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
+ fillPatch(&patch11, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading);
}
}
diff --git a/poppler/Gfx.h b/poppler/Gfx.h
index f1a8964..5ef5270 100644
--- a/poppler/Gfx.h
+++ b/poppler/Gfx.h
@@ -299,9 +299,13 @@ private:
void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
double x1, double y1, GfxColor *color1,
double x2, double y2, GfxColor *color2,
- int nComps, int depth);
+ int nComps, int depth, GfxState::ReusablePathIterator* path);
+ void gouraudFillTriangle(double x0, double y0, double color0,
+ double x1, double y1, double color1,
+ double x2, double y2, double color2,
+ double refineColorThreshold, int depth, GfxGouraudTriangleShading* shading, GfxState::ReusablePathIterator* path);
void doPatchMeshShFill(GfxPatchMeshShading *shading);
- void fillPatch(GfxPatch *patch, int nComps, int depth);
+ void fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, GfxPatchMeshShading *shading );
void doEndPath();
// path clipping operators
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 12e14d6..d6431d0 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -3362,6 +3362,8 @@ void GfxGouraudTriangleShading::getTriangle(
double out[gfxColorMaxComps];
int v, j;
+ assert(!isParameterized());
+
v = triangles[i][0];
*x0 = vertices[v].x;
*y0 = vertices[v].y;
@@ -3406,6 +3408,40 @@ void GfxGouraudTriangleShading::getTriangle(
}
}
+void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+
+ for (int j = 0; j < nFuncs; ++j) {
+ funcs[j]->transform(&t, &out[j]);
+ }
+ for (int j = 0; j < gfxColorMaxComps; ++j) {
+ color->c[j] = dblToCol(out[j]);
+ }
+}
+
+void GfxGouraudTriangleShading::getTriangle(
+ int i,
+ double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2) {
+ int v;
+
+ assert(isParameterized());
+
+ v = triangles[i][0];
+ *x0 = vertices[v].x;
+ *y0 = vertices[v].y;
+ *color0 = colToDbl(vertices[v].color.c[0]);
+ v = triangles[i][1];
+ *x1 = vertices[v].x;
+ *y1 = vertices[v].y;
+ *color1 = colToDbl(vertices[v].color.c[0]);
+ v = triangles[i][2];
+ *x2 = vertices[v].x;
+ *y2 = vertices[v].y;
+ *color2 = colToDbl(vertices[v].color.c[0]);
+}
+
//------------------------------------------------------------------------
// GfxPatchMeshShading
//------------------------------------------------------------------------
@@ -3463,7 +3499,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
Guint flag;
double x[16], y[16];
Guint xi, yi;
- GfxColorComp c[4][gfxColorMaxComps];
+ double c[4][gfxColorMaxComps];
Guint ci[4];
GfxShadingBitBuf *bitBuf;
Object obj1, obj2;
@@ -3585,7 +3621,14 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
if (!bitBuf->getBits(compBits, &ci[j])) {
break;
}
- c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);
+ // store it into a double array.
+ // That's necessary for parameterized shadings....
+ c[i][j] = cMin[j] + cMul[j] * (double)ci[j];
+ if( nFuncsA == 0 ) {
+ // ... and colorspace values can also be stored into doubles.
+ // They will be casted later.
+ c[i][j] = dblToCol(c[i][j]);
+ }
}
if (j < nComps) {
break;
@@ -3962,6 +4005,17 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
return NULL;
}
+void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+
+ for (int j = 0; j < nFuncs; ++j) {
+ funcs[j]->transform(&t, &out[j]);
+ }
+ for (int j = 0; j < gfxColorMaxComps; ++j) {
+ color->c[j] = dblToCol(out[j]);
+ }
+}
+
GfxShading *GfxPatchMeshShading::copy() {
return new GfxPatchMeshShading(this);
}
@@ -4519,6 +4573,51 @@ void GfxPath::offset(double dx, double dy) {
//------------------------------------------------------------------------
// GfxState
//------------------------------------------------------------------------
+GfxState::ReusablePathIterator::ReusablePathIterator( GfxPath *path )
+ :
+ path(path),
+ subPathOff(0),
+ coordOff(0),
+ numCoords(0),
+ curSubPath(NULL)
+{
+ if( path->getNumSubpaths() ) {
+ curSubPath = path->getSubpath(subPathOff);
+ numCoords = curSubPath->getNumPoints();
+ }
+}
+
+bool
+GfxState::ReusablePathIterator::isEnd() {
+ return coordOff >= numCoords;
+}
+
+void
+GfxState::ReusablePathIterator::next() {
+ ++coordOff;
+ if( coordOff == numCoords ) {
+ ++subPathOff;
+ if( subPathOff < path->getNumSubpaths() ) {
+ coordOff=0;
+ curSubPath = path->getSubpath(subPathOff);
+ numCoords = curSubPath->getNumPoints();
+ }
+ }
+}
+
+void
+GfxState::ReusablePathIterator::setCoord( double x, double y ) {
+ curSubPath->setX( coordOff, x );
+ curSubPath->setY( coordOff, y );
+}
+
+void
+GfxState::ReusablePathIterator::reset() {
+ coordOff = 0;
+ subPathOff = 0;
+ curSubPath = path->getSubpath(0);
+ numCoords = curSubPath->getNumPoints();
+}
GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
int rotateA, GBool upsideDown) {
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index 7dccfd5..748bb1d 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -35,6 +35,8 @@
#include "Object.h"
#include "Function.h"
+#include <assert.h>
+
class Array;
class Gfx;
class GfxFont;
@@ -876,10 +878,37 @@ public:
virtual GfxShading *copy();
int getNTriangles() { return nTriangles; }
+
+ bool isParameterized() const { return nFuncs > 0; }
+
+ /**
+ * @precondition isParameterized() == true
+ */
+ double getParameterDomainMin() const { assert(isParameterized()); return funcs[0]->getDomainMin(0); }
+
+ /**
+ * @precondition isParameterized() == true
+ */
+ double getParameterDomainMax() const { assert(isParameterized()); return funcs[0]->getDomainMax(0); }
+
+ /**
+ * @precondition isParameterized() == false
+ */
void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
double *x1, double *y1, GfxColor *color1,
double *x2, double *y2, GfxColor *color2);
+ /**
+ * Variant for functions.
+ *
+ * @precondition isParameterized() == true
+ */
+ void getTriangle(int i, double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2);
+
+ void getParameterizedColor(double t, GfxColor *color);
+
private:
GfxGouraudVertex *vertices;
@@ -894,10 +923,32 @@ private:
// GfxPatchMeshShading
//------------------------------------------------------------------------
+/**
+ * A tensor product cubic bezier patch consisting of 4x4 points and 4 color
+ * values.
+ *
+ * See the Shading Type 7 specifications. Note that Shading Type 6 is also
+ * represented using GfxPatch.
+ */
struct GfxPatch {
+ /**
+ * Represents a single color value for the patch.
+ */
+ struct ColorValue {
+ /**
+ * For parameterized patches, only element 0 is valid; it contains
+ * the single parameter.
+ *
+ * For non-parameterized patches, c contains all color components
+ * as decoded from the input stream. In this case, you will need to
+ * use dblToCol() before assigning them to GfxColor.
+ */
+ double c[gfxColorMaxComps];
+ };
+
double x[4][4];
double y[4][4];
- GfxColor color[2][2];
+ ColorValue color[2][2];
};
class GfxPatchMeshShading: public GfxShading {
@@ -915,6 +966,20 @@ public:
int getNPatches() { return nPatches; }
GfxPatch *getPatch(int i) { return &patches[i]; }
+ bool isParameterized() const { return nFuncs > 0; }
+
+ /**
+ * @precondition isParameterized() == true
+ */
+ double getParameterDomainMin() const { assert(isParameterized()); return funcs[0]->getDomainMin(0); }
+
+ /**
+ * @precondition isParameterized() == true
+ */
+ double getParameterDomainMax() const { assert(isParameterized()); return funcs[0]->getDomainMax(0); }
+
+ void getParameterizedColor(double t, GfxColor *color);
+
private:
GfxPatch *patches;
@@ -1002,6 +1067,9 @@ public:
double getY(int i) { return y[i]; }
GBool getCurve(int i) { return curve[i]; }
+ void setX(int i, double a) { x[i] = a; }
+ void setY(int i, double a) { y[i] = a; }
+
// Get last point.
double getLastX() { return x[n-1]; }
double getLastY() { return y[n-1]; }
@@ -1096,6 +1164,60 @@ private:
class GfxState {
public:
+ /**
+ * When GfxState::getReusablePath() is invoked, the currently active
+ * path is taken per reference and its coordinates can be re-edited.
+ *
+ * A ReusablePathIterator is intented to reduce overhead when the same
+ * path type is used a lot of times, only with different coordinates. It
+ * allows just to update the coordinates (occuring in the same order as
+ * in the original path).
+ */
+ class ReusablePathIterator {
+ GfxPath *path;
+ int subPathOff;
+
+ int coordOff;
+ int numCoords;
+
+ GfxSubpath *curSubPath;
+ public:
+ /**
+ * Creates the ReusablePathIterator. This should only be done from
+ * GfxState::getReusablePath().
+ *
+ * @param path the path as it is used so far. Changing this path,
+ * deleting it or starting a new path from scratch will most likely
+ * invalidate the iterator (and may cause serious problems). Make
+ * sure the path's memory structure is not changed during the
+ * lifetime of the ReusablePathIterator.
+ */
+ ReusablePathIterator( GfxPath* path );
+
+ /**
+ * Returns true if and only if the current iterator position is
+ * beyond the last valid point.
+ *
+ * A call to setCoord() will be undefined.
+ */
+ bool isEnd();
+
+ /**
+ * Advances the iterator.
+ */
+ void next();
+
+ /**
+ * Updates the coordinates associated to the current iterator
+ * position.
+ */
+ void setCoord( double x, double y );
+
+ /**
+ * Resets the iterator.
+ */
+ void reset();
+ };
// Construct a default GfxState, for a device with resolution <hDPI>
// x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
@@ -1274,6 +1396,7 @@ public:
// Misc
GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
+ ReusablePathIterator *getReusablePath() { return new ReusablePathIterator(path); }
private:
double hDPI, vDPI; // resolution
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 020a7c4..cf1958e 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -118,9 +118,7 @@
// Max delta allowed in any color component for a patch mesh shading
// fill.
-#define patchColorDelta ((3. / 256.0))
-
-#define GOURAUD_SAVE_ALLOC
+#define patchColorDelta (dblToCol((3. / 256.0)))
//------------------------------------------------------------------------
// Operator table
@@ -3268,6 +3266,18 @@ void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
} else {
start = 0;
}
+ /*
+ * Parameterized shadings take one parameter [t_0,t_e]
+ * and map it into the color space.
+ *
+ * Consequently, all color values are stored as doubles.
+ *
+ * These color values are interpreted as parameters for parameterized
+ * shadings and as colorspace entities otherwise.
+ *
+ * The only difference is that color space entities are stored into
+ * DOUBLE arrays, not into arrays of type GfxColorComp.
+ */
int colorComps = shading->getColorSpace()->getNComps();
double refineColorThreshold;
if( shading->isParameterized() ) {
@@ -3288,6 +3298,7 @@ void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
}
}
+
void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, GfxPatchMeshShading *shading ) {
GfxPatch patch00, patch01, patch10, patch11;
double xx[4][8], yy[4][8];
@@ -3295,6 +3306,10 @@ void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double
int i;
for (i = 0; i < patchColorComps; ++i) {
+ // these comparisons are done in double arithmetics.
+ //
+ // For non-parameterized shadings, they are done in color space
+ // components.
if (fabs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
> refineColorThreshold ||
fabs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
@@ -3312,8 +3327,10 @@ void Gfx::fillPatch(GfxPatch *patch, int colorComps, int patchColorComps, double
shading->getParameterizedColor( patch->color[0][0].c[0], &flatColor );
} else {
- for( i = 0; i<colorComps; ++i )
- flatColor.c[i] = dblToCol(patch->color[0][0].c[i]);
+ for( i = 0; i<colorComps; ++i ) {
+ // simply cast to the desired type; that's all what is needed.
+ flatColor.c[i] = GfxColorComp(patch->color[0][0].c[i]);
+ }
}
state->setFillColor(&flatColor);
out->updateFillColor(state);
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index ef515c9..d6431d0 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -3499,7 +3499,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
Guint flag;
double x[16], y[16];
Guint xi, yi;
- GfxColorComp c[4][gfxColorMaxComps];
+ double c[4][gfxColorMaxComps];
Guint ci[4];
GfxShadingBitBuf *bitBuf;
Object obj1, obj2;
@@ -3621,7 +3621,14 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
if (!bitBuf->getBits(compBits, &ci[j])) {
break;
}
+ // store it into a double array.
+ // That's necessary for parameterized shadings....
c[i][j] = cMin[j] + cMul[j] * (double)ci[j];
+ if( nFuncsA == 0 ) {
+ // ... and colorspace values can also be stored into doubles.
+ // They will be casted later.
+ c[i][j] = dblToCol(c[i][j]);
+ }
}
if (j < nComps) {
break;
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler