From 8ac5e2bcdafc71ad8b19ffd310f4275c815249dd Mon Sep 17 00:00:00 2001 From: Oliver Sander Date: Fri, 27 Oct 2017 20:58:31 +0200 Subject: [PATCH 3/4] Replace the QPainter by a stack of QPainters This patch lays some groundwork for the support of transparency groups. Transparency groups temporarily create new painters. These get painted upon, and then the resulting new painting will be drawn at once onto the original PaintDevice. To implement this, we need a stack of painters rather than a single one. The first painter on the stack is the original one. Opening a transparency group pushes a new painter onto the stack, and all drawing operations always go to the painter on the top of the stack. --- qt5/src/ArthurOutputDev.cc | 88 +++++++++++++++++++++++----------------------- qt5/src/ArthurOutputDev.h | 7 +++- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc index cd827e0f..5a4202f0 100644 --- a/qt5/src/ArthurOutputDev.cc +++ b/qt5/src/ArthurOutputDev.cc @@ -88,9 +88,9 @@ private: //------------------------------------------------------------------------ ArthurOutputDev::ArthurOutputDev(QPainter *painter): - m_painter(painter), m_fontHinting(NoHinting) { + m_painter.push(painter); m_currentBrush = QBrush(Qt::SolidPattern); m_fontEngine = 0; } @@ -114,7 +114,7 @@ void ArthurOutputDev::startDoc(XRef *xrefA) { globalParams->getEnableFreeType(), isHintingEnabled, isSlightHinting, - m_painter->testRenderHint(QPainter::TextAntialiasing)); + m_painter.top()->testRenderHint(QPainter::TextAntialiasing)); #endif } @@ -125,11 +125,11 @@ void ArthurOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) int h = static_cast(state->getPageHeight()); QColor fillColour(Qt::white); QBrush fill(fillColour); - m_painter->save(); - m_painter->setPen(fillColour); - m_painter->setBrush(fill); - m_painter->drawRect(0, 0, w, h); - m_painter->restore(); + m_painter.top()->save(); + m_painter.top()->setPen(fillColour); + m_painter.top()->setBrush(fill); + m_painter.top()->drawRect(0, 0, w, h); + m_painter.top()->restore(); } void ArthurOutputDev::endPage() { @@ -142,12 +142,12 @@ void ArthurOutputDev::saveState(GfxState *state) m_rawFontStack.push(m_rawFont); m_codeToGIDStack.push(m_codeToGID); - m_painter->save(); + m_painter.top()->save(); } void ArthurOutputDev::restoreState(GfxState *state) { - m_painter->restore(); + m_painter.top()->restore(); m_codeToGID = m_codeToGIDStack.top(); m_codeToGIDStack.pop(); @@ -168,7 +168,7 @@ void ArthurOutputDev::updateAll(GfxState *state) // Set CTM (Current Transformation Matrix) to a fixed matrix void ArthurOutputDev::setDefaultCTM(double *ctm) { - m_painter->setTransform(QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5])); + m_painter.top()->setTransform(QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5])); } // Update the CTM (Current Transformation Matrix), i.e., compose the old @@ -185,7 +185,7 @@ void ArthurOutputDev::updateCTM(GfxState *state, double m11, double m12, QTransform update(m11, m12, m21, m22, m31, m32); // We could also set (rather than update) the painter transformation to state->getCMT(); - m_painter->setTransform(update, true); + m_painter.top()->setTransform(update, true); } void ArthurOutputDev::updateLineDash(GfxState *state) @@ -202,7 +202,7 @@ void ArthurOutputDev::updateLineDash(GfxState *state) if (dashLength==0) { m_currentPen.setStyle(Qt::SolidLine); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); return; } @@ -214,7 +214,7 @@ void ArthurOutputDev::updateLineDash(GfxState *state) } m_currentPen.setDashPattern(pattern); m_currentPen.setDashOffset(dashStart); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateFlatness(GfxState *state) @@ -238,7 +238,7 @@ void ArthurOutputDev::updateLineJoin(GfxState *state) m_currentPen.setJoinStyle(Qt::BevelJoin); break; } - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateLineCap(GfxState *state) @@ -254,19 +254,19 @@ void ArthurOutputDev::updateLineCap(GfxState *state) m_currentPen.setCapStyle(Qt::SquareCap); break; } - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateMiterLimit(GfxState *state) { m_currentPen.setMiterLimit(state->getMiterLimit()); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateLineWidth(GfxState *state) { m_currentPen.setWidthF(state->getLineWidth()); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); // The updateLineDash method needs to know the line width, but it is sometimes // called before the updateLineWidth method. To make sure that the last call // to updateLineDash before a drawing operation is always with the correct line @@ -290,7 +290,7 @@ void ArthurOutputDev::updateStrokeColor(GfxState *state) state->getStrokeRGB(&rgb); penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF()); m_currentPen.setColor(penColour); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateBlendMode(GfxState * state) @@ -305,42 +305,42 @@ void ArthurOutputDev::updateBlendMode(GfxState * state) switch(blendMode){ case gfxBlendMultiply: - m_painter->setCompositionMode(QPainter::CompositionMode_Multiply); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Multiply); break; case gfxBlendScreen: - m_painter->setCompositionMode(QPainter::CompositionMode_Screen); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Screen); break; case gfxBlendDarken: - m_painter->setCompositionMode(QPainter::CompositionMode_Darken); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Darken); break; case gfxBlendLighten: - m_painter->setCompositionMode(QPainter::CompositionMode_Lighten); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Lighten); break; case gfxBlendColorDodge: - m_painter->setCompositionMode(QPainter::CompositionMode_ColorDodge); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorDodge); break; case gfxBlendColorBurn: - m_painter->setCompositionMode(QPainter::CompositionMode_ColorBurn); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorBurn); break; case gfxBlendHardLight: - m_painter->setCompositionMode(QPainter::CompositionMode_HardLight); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_HardLight); break; case gfxBlendSoftLight: - m_painter->setCompositionMode(QPainter::CompositionMode_SoftLight); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_SoftLight); break; case gfxBlendDifference: - m_painter->setCompositionMode(QPainter::CompositionMode_Difference); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Difference); break; case gfxBlendExclusion: - m_painter->setCompositionMode(QPainter::CompositionMode_Exclusion); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Exclusion); break; case gfxBlendColor: - m_painter->setCompositionMode(QPainter::CompositionMode_Plus); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_Plus); break; default: qDebug() << "Unsupported blend mode, falling back to CompositionMode_SourceOver"; case gfxBlendNormal: - m_painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + m_painter.top()->setCompositionMode(QPainter::CompositionMode_SourceOver); break; } } @@ -357,7 +357,7 @@ void ArthurOutputDev::updateStrokeOpacity(GfxState *state) QColor penColour= m_currentPen.color(); penColour.setAlphaF(state->getStrokeOpacity()); m_currentPen.setColor(penColour); - m_painter->setPen(m_currentPen); + m_painter.top()->setPen(m_currentPen); } void ArthurOutputDev::updateFont(GfxState *state) @@ -668,27 +668,27 @@ static QPainterPath convertPath(GfxState *state, GfxPath *path, Qt::FillRule fil void ArthurOutputDev::stroke(GfxState *state) { - m_painter->strokePath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentPen ); + m_painter.top()->strokePath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentPen ); } void ArthurOutputDev::fill(GfxState *state) { - m_painter->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), m_currentBrush ); + m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), m_currentBrush ); } void ArthurOutputDev::eoFill(GfxState *state) { - m_painter->fillPath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentBrush ); + m_painter.top()->fillPath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentBrush ); } void ArthurOutputDev::clip(GfxState *state) { - m_painter->setClipPath(convertPath( state, state->getPath(), Qt::WindingFill ) ); + m_painter.top()->setClipPath(convertPath( state, state->getPath(), Qt::WindingFill ) ); } void ArthurOutputDev::eoClip(GfxState *state) { - m_painter->setClipPath(convertPath( state, state->getPath(), Qt::OddEvenFill ) ); + m_painter.top()->setClipPath(convertPath( state, state->getPath(), Qt::OddEvenFill ) ); } void ArthurOutputDev::drawChar(GfxState *state, double x, double y, @@ -719,7 +719,7 @@ void ArthurOutputDev::drawChar(GfxState *state, double x, double y, glyphRun.setRawFont(*m_rawFont); // Store the QPainter state; we need to modify it temporarily - m_painter->save(); + m_painter.top()->save(); // Apply the text matrix to the glyph. The glyph is not scaled by the font size, // because the font in m_rawFont already has the correct size. @@ -730,7 +730,7 @@ void ArthurOutputDev::drawChar(GfxState *state, double x, double y, // Make the glyph position the coordinate origin -- that's our center of scaling const double *textMat = state->getTextMat(); - m_painter->translate(QPointF(glyphPosition.x(),glyphPosition.y())); + m_painter.top()->translate(QPointF(glyphPosition.x(),glyphPosition.y())); QTransform textTransform(textMat[0] * state->getHorizScaling(), textMat[1] * state->getHorizScaling(), @@ -739,7 +739,7 @@ void ArthurOutputDev::drawChar(GfxState *state, double x, double y, 0, 0); - m_painter->setTransform(textTransform,true); + m_painter.top()->setTransform(textTransform,true); // We are painting a filled glyph here. But QPainter uses the pen to draw even filled text, // not the brush. (see, e.g., http://doc.qt.io/qt-5/qpainter.html#setPen ) @@ -752,13 +752,13 @@ void ArthurOutputDev::drawChar(GfxState *state, double x, double y, state->getFillRGB(&rgb); QColor fontColor; fontColor.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity()); - m_painter->setPen(fontColor); + m_painter.top()->setPen(fontColor); // Actually draw the glyph - m_painter->drawGlyphRun(QPointF(-glyphPosition.x(),-glyphPosition.y()), glyphRun); + m_painter.top()->drawGlyphRun(QPointF(-glyphPosition.x(),-glyphPosition.y()), glyphRun); // Restore transformation and pen color - m_painter->restore(); + m_painter.top()->restore(); } } @@ -913,7 +913,7 @@ void ArthurOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. - m_painter->drawImage( QRect(0,0,1,1), image ); + m_painter.top()->drawImage( QRect(0,0,1,1), image ); delete imgStr; } @@ -983,6 +983,6 @@ void ArthurOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream * // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. - m_painter->drawImage( QRect(0,0,1,1), image ); + m_painter.top()->drawImage( QRect(0,0,1,1), image ); } diff --git a/qt5/src/ArthurOutputDev.h b/qt5/src/ArthurOutputDev.h index bf8cac81..4fae0a5c 100644 --- a/qt5/src/ArthurOutputDev.h +++ b/qt5/src/ArthurOutputDev.h @@ -172,7 +172,12 @@ public: GBool isReverseVideo() { return gFalse; } private: - QPainter *m_painter; + + // The stack of QPainters is used to implement transparency groups. When such a group + // is opened, annew Painter that paints onto a QPicture is pushed onto the stack. + // It is popped again when the transparency group ends. + std::stack m_painter; + FontHinting m_fontHinting; QPen m_currentPen; -- 2.14.2