[poppler] qt5/src
Albert Astals Cid
aacid at kemper.freedesktop.org
Tue Mar 27 22:01:10 UTC 2018
qt5/src/ArthurOutputDev.cc | 128 ++++++++++++++++++++++++++++++++++++++++++++-
qt5/src/ArthurOutputDev.h | 9 ++-
2 files changed, 135 insertions(+), 2 deletions(-)
New commits:
commit 7a708ffd374cd18e9e4bfe8a8e95c02184a074ba
Author: Oliver Sander <oliver.sander at tu-dresden.de>
Date: Wed Mar 28 00:00:35 2018 +0200
Implement ArthurOutputDev::axialShadedFill
diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc
index 34fde569..9af32ae5 100644
--- a/qt5/src/ArthurOutputDev.cc
+++ b/qt5/src/ArthurOutputDev.cc
@@ -23,7 +23,7 @@
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2013 Dominik Haumann <dhaumann at kde.org>
// Copyright (C) 2013 Mihai Niculescu <q.quark at gmail.com>
-// Copyright (C) 2017 Oliver Sander <oliver.sander at tu-dresden.de>
+// Copyright (C) 2017, 2018 Oliver Sander <oliver.sander at tu-dresden.de>
// Copyright (C) 2017 Adrian Johnson <ajohnson at redneon.com>
//
// To see a description of the changes please see the Changelog file that
@@ -687,6 +687,132 @@ void ArthurOutputDev::eoFill(GfxState *state)
m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentBrush );
}
+GBool ArthurOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax)
+{
+ double x0, y0, x1, y1;
+ shading->getCoords(&x0, &y0, &x1, &y1);
+
+ // get the clip region bbox
+ double xMin, yMin, xMax, yMax;
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ // get the function domain
+ double t0 = shading->getDomain0();
+ double t1 = shading->getDomain1();
+
+ // Max number of splits along the t axis
+ constexpr int maxSplits = 256;
+
+ // Max delta allowed in any color component
+ const double colorDelta = (dblToCol(1 / 256.0));
+
+ // Number of color space components
+ auto nComps = shading->getColorSpace()->getNComps();
+
+ // Helper function to test two color objects for 'almost-equality'
+ auto isSameGfxColor = [&nComps,&colorDelta](const GfxColor &colorA, const GfxColor &colorB)
+ {
+ for (int k = 0; k < nComps; ++k) {
+ if (abs(colorA.c[k] - colorB.c[k]) > colorDelta) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // Helper function: project a number into an interval
+ // With C++17 this is part of the standard library
+ auto clamp = [](double v, double lo, double hi)
+ { return std::min(std::max(v,lo), hi); };
+
+ // ta stores all parameter values where we evaluate the input shading function.
+ // In between, QLinearGradient will interpolate linearly.
+ // We set up the array with three values.
+ std::array<double, maxSplits+1> ta;
+ ta[0] = tMin;
+ std::array<int, maxSplits+1> next;
+ next[0] = maxSplits / 2;
+ ta[maxSplits / 2] = 0.5 * (tMin + tMax);
+ next[maxSplits / 2] = maxSplits;
+ ta[maxSplits] = tMax;
+
+ // compute the color at t = tMin
+ double tt = clamp(t0 + (t1 - t0) * tMin, t0, t1);
+
+ GfxColor color0, color1;
+ shading->getColor(tt, &color0);
+
+ // Construct a gradient object and set its color at one parameter end
+ QLinearGradient gradient(QPointF(x0 + tMin * (x1 - x0), y0 + tMin * (y1 - y0)),
+ QPointF(x0 + tMax * (x1 - x0), y0 + tMax * (y1 - y0)));
+
+ GfxRGB rgb;
+ shading->getColorSpace()->getRGB(&color0, &rgb);
+ QColor qColor(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b));
+ gradient.setColorAt(0,qColor);
+
+ // Look for more relevant parameter values by bisection
+ int i = 0;
+ while (i < maxSplits) {
+
+ int j = next[i];
+ while (j > i + 1) {
+
+ // Next parameter value to try
+ tt = clamp(t0 + (t1 - t0) * ta[j], t0, t1);
+ shading->getColor(tt, &color1);
+
+ // j is a good next color stop if the input shading can be approximated well
+ // on the interval (ta[i], ta[j]) by a linear interpolation.
+ // We test this by comparing the real color in the middle between ta[i] and ta[j]
+ // with the linear interpolant there.
+ auto midPoint = 0.5 * (ta[i] + ta[j]);
+ GfxColor colorAtMidPoint;
+ shading->getColor(midPoint, &colorAtMidPoint);
+
+ GfxColor linearlyInterpolatedColor;
+ for (int ii=0; ii<nComps; ii++)
+ linearlyInterpolatedColor.c[ii] = 0.5 * (color0.c[ii] + color1.c[ii]);
+
+ // If the two colors are equal, ta[j] is a good place for the next color stop; take it!
+ if (isSameGfxColor(colorAtMidPoint, linearlyInterpolatedColor))
+ break;
+
+ // Otherwise: bisect further
+ int k = (i + j) / 2;
+ ta[k] = midPoint;
+ next[i] = k;
+ next[k] = j;
+ j = k;
+ }
+
+ // set the color
+ GfxRGB rgb;
+ shading->getColorSpace()->getRGB(&color1, &rgb);
+ qColor.setRgb(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b));
+ gradient.setColorAt((ta[j] - tMin)/(tMax - tMin), qColor);
+
+ // Move to the next parameter region
+ color0 = color1;
+ i = next[i];
+ }
+
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMin, yMax);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMax, yMin);
+ state->closePath();
+
+ // Actually paint the shaded region
+ QBrush newBrush(gradient);
+ m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), newBrush );
+
+ state->clearPath();
+
+ // True means: The shaded region has been painted
+ return gTrue;
+}
+
void ArthurOutputDev::clip(GfxState *state)
{
m_painter.top()->setClipPath(convertPath( state, state->getPath(), Qt::WindingFill ), Qt::IntersectClip );
diff --git a/qt5/src/ArthurOutputDev.h b/qt5/src/ArthurOutputDev.h
index b4ab3d7e..701a883a 100644
--- a/qt5/src/ArthurOutputDev.h
+++ b/qt5/src/ArthurOutputDev.h
@@ -20,7 +20,7 @@
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2013 Mihai Niculescu <q.quark at gmail.com>
-// Copyright (C) 2017 Oliver Sander <oliver.sander at tu-dresden.de>
+// Copyright (C) 2017, 2018 Oliver Sander <oliver.sander at tu-dresden.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
@@ -83,6 +83,12 @@ public:
// Does this device use drawChar() or drawString()?
GBool useDrawChar() override { return gTrue; }
+ // Does this device implement shaded fills (aka gradients) natively?
+ // If this returns false, these shaded fills
+ // will be reduced to a series of other drawing operations.
+ // type==2 is 'axial shading'
+ GBool useShadedFills(int type) override { return type == 2; }
+
// Does this device use beginType3Char/endType3Char? Otherwise,
// text in Type 3 fonts will be drawn with drawChar/drawString.
GBool interpretType3Chars() override { return gTrue; }
@@ -125,6 +131,7 @@ public:
void stroke(GfxState *state) override;
void fill(GfxState *state) override;
void eoFill(GfxState *state) override;
+ GBool axialShadedFill(GfxState * state, GfxAxialShading *shading, double tMin, double tMax) override;
//----- path clipping
void clip(GfxState *state) override;
More information about the poppler
mailing list