diff --git a/Pdf4QtDocDiff/mainwindow.cpp b/Pdf4QtDocDiff/mainwindow.cpp index ee9d336..7c3ba9b 100644 --- a/Pdf4QtDocDiff/mainwindow.cpp +++ b/Pdf4QtDocDiff/mainwindow.cpp @@ -82,7 +82,7 @@ MainWindow::MainWindow(QWidget* parent) : ui->documentFrame->setLayout(new QVBoxLayout); m_cmsManager = new pdf::PDFCMSManager(this); - m_pdfWidget = new pdf::PDFWidget(m_cmsManager, pdf::RendererEngine::Software, 1, ui->documentFrame); + m_pdfWidget = new pdf::PDFWidget(m_cmsManager, pdf::RendererEngine::QPainter, ui->documentFrame); m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress); ui->documentFrame->layout()->addWidget(m_pdfWidget); m_pdfWidget->getDrawWidgetProxy()->registerDrawInterface(&m_drawInterface); diff --git a/Pdf4QtLibCore/CMakeLists.txt b/Pdf4QtLibCore/CMakeLists.txt index cc1bd92..48274d8 100644 --- a/Pdf4QtLibCore/CMakeLists.txt +++ b/Pdf4QtLibCore/CMakeLists.txt @@ -146,6 +146,8 @@ add_library(Pdf4QtLibCore SHARED cmaps.qrc sources/pdfcertificatestore.h sources/pdfcertificatestore.cpp + sources/pdfblpainter.h + sources/pdfblpainter.cpp ) include(GenerateExportHeader) diff --git a/Pdf4QtLibCore/sources/pdfannotation.cpp b/Pdf4QtLibCore/sources/pdfannotation.cpp index d213c3e..8034779 100644 --- a/Pdf4QtLibCore/sources/pdfannotation.cpp +++ b/Pdf4QtLibCore/sources/pdfannotation.cpp @@ -1482,17 +1482,6 @@ void PDFAnnotationManager::drawPage(QPainter* painter, } } -void PDFAnnotationManager::drawPage(BLContext& context, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, QList& errors) const -{ - // TODO: Implement it - Q_UNUSED(context); - Q_UNUSED(pageIndex); - Q_UNUSED(compiledPage); - Q_UNUSED(layoutGetter); - Q_UNUSED(pagePointToDevicePointMatrix); - Q_UNUSED(errors); -} - void PDFAnnotationManager::drawAnnotation(const PageAnnotation& annotation, const QTransform& pagePointToDevicePointMatrix, const PDFPage* page, diff --git a/Pdf4QtLibCore/sources/pdfannotation.h b/Pdf4QtLibCore/sources/pdfannotation.h index 1955d97..8e58568 100644 --- a/Pdf4QtLibCore/sources/pdfannotation.h +++ b/Pdf4QtLibCore/sources/pdfannotation.h @@ -40,8 +40,6 @@ class QKeyEvent; class QMouseEvent; class QWheelEvent; -class BLContext; - namespace pdf { class PDFWidget; @@ -1468,13 +1466,6 @@ public: const QTransform& pagePointToDevicePointMatrix, QList& errors) const; - virtual void drawPage(BLContext& context, - PDFInteger pageIndex, - const PDFPrecompiledPage* compiledPage, - PDFTextLayoutGetter& layoutGetter, - const QTransform& pagePointToDevicePointMatrix, - QList& errors) const; - /// Set document /// \param document New document virtual void setDocument(const PDFModifiedDocument& document); diff --git a/Pdf4QtLibCore/sources/pdfblpainter.cpp b/Pdf4QtLibCore/sources/pdfblpainter.cpp new file mode 100644 index 0000000..dbf1a0c --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfblpainter.cpp @@ -0,0 +1,755 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#include "pdfblpainter.h" +#include "pdffont.h" + +#include +#include +#include + +#include + +namespace pdf +{ + +class PDFBLPaintEngine : public QPaintEngine +{ +public: + explicit PDFBLPaintEngine(QImage& qtOffscreenBuffer, bool isMultithreaded); + + virtual bool begin(QPaintDevice*) override; + virtual bool end() override; + virtual void updateState(const QPaintEngineState& updatedState) override; + virtual void drawRects(const QRect* rects, int rectCount) override; + virtual void drawRects(const QRectF* rects, int rectCount) override; + virtual void drawLines(const QLine* lines, int lineCount) override; + virtual void drawLines(const QLineF* lines, int lineCount) override; + virtual void drawEllipse(const QRectF& r) override; + virtual void drawEllipse(const QRect& r) override; + virtual void drawPath(const QPainterPath& path) override; + virtual void drawPoints(const QPointF* points, int pointCount) override; + virtual void drawPoints(const QPoint* points, int pointCount) override; + virtual void drawPolygon(const QPointF* points, int pointCount, PolygonDrawMode mode) override; + virtual void drawPolygon(const QPoint* points, int pointCount, PolygonDrawMode mode) override; + virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) override; + virtual void drawTextItem(const QPointF& p, const QTextItem& textItem) override; + virtual void drawTiledPixmap(const QRectF& r, const QPixmap& pixmap, const QPointF& s) override; + virtual void drawImage(const QRectF& r, const QImage& pm, const QRectF& sr, Qt::ImageConversionFlags flags) override; + virtual Type type() const override; + + static PaintEngineFeatures getStaticFeatures(); + +private: + + /// Get BL matrix from transformation + static BLMatrix2D getBLMatrix(QTransform transform); + + static BLPoint getBLPoint(const QPoint& point); + static BLPoint getBLPoint(const QPointF& point); + + /// Get BL rect from regular rect + static BLRectI getBLRect(QRect rect); + + /// Get BL rect from regular rect + static BLRect getBLRect(QRectF rect); + + /// Get BL path from path + static BLPath getBLPath(const QPainterPath& path); + + /// Set pen to the context + static void setBLPen(BLContext& context, const QPen& pen); + + /// Set brush to the context + static void setBLBrush(BLContext& context, const QBrush& brush); + + /// Load font + static bool loadBLFont(BLFont& font, QString fontName, PDFReal fontSize); + + /// Returns composition operator + static BLCompOp getBLCompOp(QPainter::CompositionMode mode); + + bool isStrokeActive() const { return m_currentPen.style() != Qt::NoPen; } + bool isFillActive() const { return m_currentBrush.style() != Qt::NoBrush; } + + QImage& m_qtOffscreenBuffer; + std::optional m_blContext; + std::optional m_blOffscreenBuffer; + bool m_isMultithreaded; + + QPen m_currentPen; + QBrush m_currentBrush; + QFont m_currentFont; +}; + +PDFBLPaintDevice::PDFBLPaintDevice(QImage& offscreenBuffer, bool isMultithreaded) : + m_offscreenBuffer(offscreenBuffer), + m_paintEngine(new PDFBLPaintEngine(offscreenBuffer, isMultithreaded)) +{ + +} + +PDFBLPaintDevice::~PDFBLPaintDevice() +{ + delete m_paintEngine; + m_paintEngine = nullptr; +} + +int PDFBLPaintDevice::devType() const +{ + return QInternal::CustomRaster; +} + +QPaintEngine* PDFBLPaintDevice::paintEngine() const +{ + return m_paintEngine; +} + +int PDFBLPaintDevice::metric(PaintDeviceMetric metric) const +{ + switch (metric) + { + case QPaintDevice::PdmWidth: + return m_offscreenBuffer.width(); + case QPaintDevice::PdmHeight: + return m_offscreenBuffer.height(); + case QPaintDevice::PdmWidthMM: + return m_offscreenBuffer.widthMM(); + case QPaintDevice::PdmHeightMM: + return m_offscreenBuffer.heightMM(); + case QPaintDevice::PdmNumColors: + return m_offscreenBuffer.colorCount(); + case QPaintDevice::PdmDepth: + return m_offscreenBuffer.depth(); + case QPaintDevice::PdmDpiX: + return m_offscreenBuffer.logicalDpiX(); + case QPaintDevice::PdmDpiY: + return m_offscreenBuffer.logicalDpiY(); + case QPaintDevice::PdmPhysicalDpiX: + return m_offscreenBuffer.physicalDpiX(); + case QPaintDevice::PdmPhysicalDpiY: + return m_offscreenBuffer.physicalDpiY(); + case QPaintDevice::PdmDevicePixelRatio: + return m_offscreenBuffer.devicePixelRatio(); + case QPaintDevice::PdmDevicePixelRatioScaled: + return m_offscreenBuffer.devicePixelRatioFScale(); + default: + Q_ASSERT(false); + break; + } + + return 0; +} + +PDFBLPaintEngine::PDFBLPaintEngine(QImage& qtOffscreenBuffer, bool isMultithreaded) : + QPaintEngine(getStaticFeatures()), + m_qtOffscreenBuffer(qtOffscreenBuffer), + m_isMultithreaded(isMultithreaded) +{ + +} + +bool PDFBLPaintEngine::begin(QPaintDevice*) +{ + if (isActive()) + { + return false; + } + + m_blContext.emplace(); + m_blOffscreenBuffer.emplace(); + + BLContextCreateInfo info{}; + + if (m_isMultithreaded) + { + info.flags = BL_CONTEXT_CREATE_FLAG_FALLBACK_TO_SYNC; + info.threadCount = QThread::idealThreadCount(); + } + + m_blContext->setHint(BL_CONTEXT_HINT_RENDERING_QUALITY, BL_RENDERING_QUALITY_MAX_VALUE); + + m_blOffscreenBuffer->createFromData(m_qtOffscreenBuffer.width(), m_qtOffscreenBuffer.height(), BL_FORMAT_PRGB32, m_qtOffscreenBuffer.bits(), m_qtOffscreenBuffer.bytesPerLine()); + if (m_blContext->begin(m_blOffscreenBuffer.value(), info) == BL_SUCCESS) + { + m_blContext->clearAll(); + return true; + } + else + { + m_blContext.reset(); + m_blOffscreenBuffer.reset(); + } + + return false; +} + +bool PDFBLPaintEngine::end() +{ + if (!isActive()) + { + return false; + } + + m_blContext->end(); + m_blContext.reset(); + m_blOffscreenBuffer.reset(); + return true; +} + +void PDFBLPaintEngine::updateState(const QPaintEngineState& updatedState) +{ + /* DirtyBrushOrigin = 0x0004, + DirtyFont = 0x0008, + DirtyBackground = 0x0010, + DirtyBackgroundMode = 0x0020, + DirtyClipRegion = 0x0080, + DirtyClipPath = 0x0100, + DirtyHints = 0x0200, + DirtyClipEnabled = 0x0800,*/ + + if (updatedState.state().testFlag(QPaintEngine::DirtyPen)) + { + m_currentPen = updatedState.pen(); + setBLPen(m_blContext.value(), updatedState.pen()); + } + + if (updatedState.state().testFlag(QPaintEngine::DirtyBrush)) + { + m_currentBrush = updatedState.brush(); + setBLBrush(m_blContext.value(), updatedState.brush()); + } + + if (updatedState.state().testFlag(QPaintEngine::DirtyCompositionMode)) + { + m_blContext->setCompOp(getBLCompOp(updatedState.compositionMode())); + } + + if (updatedState.state().testFlag(QPaintEngine::DirtyOpacity)) + { + m_blContext->setGlobalAlpha(updatedState.opacity()); + } + + if (updatedState.state().testFlag(QPaintEngine::DirtyTransform)) + { + m_blContext->setMatrix(getBLMatrix(updatedState.transform())); + } +} + +void PDFBLPaintEngine::drawRects(const QRect* rects, int rectCount) +{ + BLArray blRects; + blRects.reserve(rectCount); + + for (int i = 0; i < rectCount; ++i) + { + blRects.append(getBLRect(rects[i])); + } + + if (isFillActive()) + { + m_blContext->fillRectArray(blRects.view()); + } + + if (isStrokeActive()) + { + m_blContext->strokeRectArray(blRects.view()); + } +} + +void PDFBLPaintEngine::drawRects(const QRectF* rects, int rectCount) +{ + BLArray blRects; + blRects.reserve(rectCount); + + for (int i = 0; i < rectCount; ++i) + { + blRects.append(getBLRect(rects[i])); + } + + if (isFillActive()) + { + m_blContext->fillRectArray(blRects.view()); + } + + if (isStrokeActive()) + { + m_blContext->strokeRectArray(blRects.view()); + } +} + +void PDFBLPaintEngine::drawLines(const QLine* lines, int lineCount) +{ + if (!isStrokeActive()) + { + return; + } + + for (int i = 0; i < lineCount; ++i) + { + const QLine& line = lines[i]; + m_blContext->strokeLine(line.x1(), line.y1(), line.x2(), line.y2()); + } +} + +void PDFBLPaintEngine::drawLines(const QLineF* lines, int lineCount) +{ + if (!isStrokeActive()) + { + return; + } + + for (int i = 0; i < lineCount; ++i) + { + const QLineF& line = lines[i]; + m_blContext->strokeLine(line.x1(), line.y1(), line.x2(), line.y2()); + } +} + +void PDFBLPaintEngine::drawEllipse(const QRectF& r) +{ + QPointF c = r.center(); + BLEllipse blEllipse(c.x(), c.y(), r.width() * 0.5, r.height() * 0.5); + + if (isFillActive()) + { + m_blContext->fillEllipse(blEllipse); + } + + if (isStrokeActive()) + { + m_blContext->strokeEllipse(blEllipse); + } +} + +void PDFBLPaintEngine::drawEllipse(const QRect& r) +{ + QPointF c = r.center(); + BLEllipse blEllipse(c.x(), c.y(), r.width() * 0.5, r.height() * 0.5); + + if (isFillActive()) + { + m_blContext->fillEllipse(blEllipse); + } + + if (isStrokeActive()) + { + m_blContext->strokeEllipse(blEllipse); + } +} + +void PDFBLPaintEngine::drawPath(const QPainterPath& path) +{ + BLPath blPath = getBLPath(path); + + BLFillRule fillRule{}; + + switch (path.fillRule()) + { + case Qt::OddEvenFill: + fillRule = BL_FILL_RULE_EVEN_ODD; + break; + + case Qt::WindingFill: + fillRule = BL_FILL_RULE_NON_ZERO; + break; + + default: + Q_ASSERT(false); + break; + } + + m_blContext->setFillRule(fillRule); + + if (isFillActive()) + { + m_blContext->fillPath(blPath); + } + + if (isStrokeActive()) + { + m_blContext->strokePath(blPath); + } +} + +void PDFBLPaintEngine::drawPoints(const QPointF* points, int pointCount) +{ + +} + +void PDFBLPaintEngine::drawPoints(const QPoint* points, int pointCount) +{ +} + +void PDFBLPaintEngine::drawPolygon(const QPointF* points, int pointCount, PolygonDrawMode mode) +{ +} + +void PDFBLPaintEngine::drawPolygon(const QPoint* points, int pointCount, PolygonDrawMode mode) +{ +} + +void PDFBLPaintEngine::drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) +{ +} + +void PDFBLPaintEngine::drawTextItem(const QPointF& p, const QTextItem& textItem) +{ +} + +void PDFBLPaintEngine::drawTiledPixmap(const QRectF& r, const QPixmap& pixmap, const QPointF& s) +{ +} + +void PDFBLPaintEngine::drawImage(const QRectF& r, const QImage& pm, const QRectF& sr, Qt::ImageConversionFlags flags) +{ + QImage image = pm; + + if (image.format() != QImage::Format_ARGB32_Premultiplied) + { + image.convertTo(QImage::Format_ARGB32_Premultiplied); + } + + BLImage blImage; + blImage.createFromData(image.width(), image.height(), BL_FORMAT_PRGB32, image.bits(), image.bytesPerLine()); + + BLImage blDrawImage; + blDrawImage.assignDeep(blImage); + + m_blContext->blitImage(BLRect(r.x(), r.y(), r.width(), r.height()), + blDrawImage, + BLRectI(sr.x(), sr.y(), sr.width(), sr.height())); +} + +QPaintEngine::Type PDFBLPaintEngine::type() const +{ + return User; +} + +QPaintEngine::PaintEngineFeatures PDFBLPaintEngine::getStaticFeatures() +{ + return PrimitiveTransform | PixmapTransform | LinearGradientFill | + RadialGradientFill | ConicalGradientFill | AlphaBlend | + PorterDuff | PainterPaths | Antialiasing | ConstantOpacity | + BlendModes | PaintOutsidePaintEvent; +} + + +BLMatrix2D PDFBLPaintEngine::getBLMatrix(QTransform transform) +{ + BLMatrix2D matrix; + matrix.reset(transform.m11(), transform.m12(), transform.m21(), transform.m22(), transform.dx(), transform.dy()); + return matrix; +} + +BLPoint PDFBLPaintEngine::getBLPoint(const QPoint& point) +{ + return BLPoint(point.x(), point.y()); +} + +BLPoint PDFBLPaintEngine::getBLPoint(const QPointF& point) +{ + return BLPoint(point.x(), point.y()); +} + +BLRectI PDFBLPaintEngine::getBLRect(QRect rect) +{ + return BLRectI(rect.x(), rect.y(), rect.width(), rect.height()); +} + +BLRect PDFBLPaintEngine::getBLRect(QRectF rect) +{ + return BLRect(rect.x(), rect.y(), rect.width(), rect.height()); +} + +BLPath PDFBLPaintEngine::getBLPath(const QPainterPath& path) +{ + BLPath blPath; + + int elementCount = path.elementCount(); + for (int i = 0; i < elementCount; ++i) { + const QPainterPath::Element& element = path.elementAt(i); + + switch (element.type) + { + case QPainterPath::MoveToElement: + blPath.moveTo(element.x, element.y); + break; + + case QPainterPath::LineToElement: + blPath.lineTo(element.x, element.y); + break; + + case QPainterPath::CurveToElement: + if (i + 2 < elementCount) + { + const QPainterPath::Element& ctrlPoint1 = path.elementAt(i++); + const QPainterPath::Element& ctrlPoint2 = path.elementAt(i++); + const QPainterPath::Element& endPoint = path.elementAt(i); + blPath.cubicTo(ctrlPoint1.x, ctrlPoint1.y, ctrlPoint2.x, ctrlPoint2.y, endPoint.x, endPoint.y); + } + break; + + case QPainterPath::CurveToDataElement: + Q_ASSERT(false); + break; + } + } + + return blPath; +} + +void PDFBLPaintEngine::setBLPen(BLContext& context, const QPen& pen) +{ + const Qt::PenCapStyle capStyle = pen.capStyle(); + const Qt::PenJoinStyle joinStyle = pen.joinStyle(); + const QColor color = pen.color(); + const qreal width = pen.widthF(); + const qreal miterLimit = pen.miterLimit(); + const qreal dashOffset = pen.dashOffset(); + const QList customDashPattern = pen.dashPattern(); + const Qt::PenStyle penStyle = pen.style(); + + context.setStrokeAlpha(pen.color().alphaF()); + context.setStrokeWidth(width); + context.setStrokeMiterLimit(miterLimit); + + switch (capStyle) + { + case Qt::FlatCap: + context.setStrokeCaps(BL_STROKE_CAP_BUTT); + break; + case Qt::SquareCap: + context.setStrokeCaps(BL_STROKE_CAP_SQUARE); + break; + case Qt::RoundCap: + context.setStrokeCaps(BL_STROKE_CAP_ROUND); + break; + } + + BLArray dashArray; + + for (double value : customDashPattern) + { + dashArray.append(value); + } + + context.setStrokeDashOffset(dashOffset); + context.setStrokeDashArray(dashArray); + + switch (joinStyle) + { + case Qt::MiterJoin: + context.setStrokeJoin(BL_STROKE_JOIN_MITER_CLIP); + break; + case Qt::BevelJoin: + context.setStrokeJoin(BL_STROKE_JOIN_BEVEL); + break; + case Qt::RoundJoin: + context.setStrokeJoin(BL_STROKE_JOIN_ROUND); + break; + case Qt::SvgMiterJoin: + context.setStrokeJoin(BL_STROKE_JOIN_MITER_CLIP); + break; + } + + context.setStrokeStyle(BLRgba32(color.rgba())); + + BLStrokeOptions strokeOptions = context.strokeOptions(); + + switch (penStyle) + { + case Qt::SolidLine: + strokeOptions.dashArray.clear(); + strokeOptions.dashOffset = 0.0; + break; + + case Qt::DashLine: + { + constexpr double dashPattern[] = {4, 4}; + strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); + break; + } + + case Qt::DotLine: + { + constexpr double dashPattern[] = {1, 3}; + strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); + break; + } + + case Qt::DashDotLine: + { + constexpr double dashPattern[] = {4, 2, 1, 2}; + strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); + break; + } + + case Qt::DashDotDotLine: + { + constexpr double dashPattern[] = {4, 2, 1, 2, 1, 2}; + strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); + break; + } + + default: + break; + } + + context.setStrokeOptions(strokeOptions); +} + +void PDFBLPaintEngine::setBLBrush(BLContext& context, const QBrush& brush) +{ + auto setGradientStops = [](BLGradient& blGradient, const auto& qGradient) + { + QVector stops; + for (const auto& stop : qGradient.stops()) + { + stops.append(BLGradientStop(stop.first, BLRgba32(stop.second.red(), stop.second.green(), stop.second.blue(), stop.second.alpha()))); + } + blGradient.assignStops(stops.constData(), stops.size()); + }; + + switch (brush.style()) + { + default: + case Qt::SolidPattern: + { + QColor color = brush.color(); + BLRgba32 blColor = BLRgba32(color.red(), color.green(), color.blue(), color.alpha()); + context.setFillStyle(blColor); + break; + } + case Qt::LinearGradientPattern: + { + const QGradient* gradient = brush.gradient(); + if (gradient && gradient->type() == QGradient::LinearGradient) + { + const QLinearGradient* linearGradient = static_cast(gradient); + BLLinearGradientValues blLinearGradient; + blLinearGradient.x0 = linearGradient->start().x(); + blLinearGradient.y0 = linearGradient->start().y(); + blLinearGradient.x1 = linearGradient->finalStop().x(); + blLinearGradient.y1 = linearGradient->finalStop().y(); + BLGradient blGradient(blLinearGradient); + setGradientStops(blGradient, *gradient); + context.setFillStyle(blGradient); + } + break; + } + case Qt::RadialGradientPattern: + { + const QGradient* gradient = brush.gradient(); + if (gradient && gradient->type() == QGradient::RadialGradient) + { + const QRadialGradient* radialGradient = static_cast(gradient); + BLRadialGradientValues blRadialGradientValues; + blRadialGradientValues.x0 = radialGradient->center().x(); + blRadialGradientValues.y0 = radialGradient->center().y(); + blRadialGradientValues.x1 = radialGradient->focalPoint().x(); + blRadialGradientValues.y1 = radialGradient->focalPoint().y(); + blRadialGradientValues.r0 = radialGradient->radius(); + BLGradient blGradient(blRadialGradientValues); + setGradientStops(blGradient, *gradient); + context.setFillStyle(blGradient); + } + break; + } + } +} + +bool PDFBLPaintEngine::loadBLFont(BLFont& font, QString fontName, PDFReal fontSize) +{ + QByteArray data = PDFSystemFont::getFontData(fontName.toLatin1()); + + BLFontData blFontData; + if (blFontData.createFromData(data.data(), data.size()) == BL_SUCCESS) + { + BLFontFace fontFace; + if (fontFace.createFromData(blFontData, 0) == BL_SUCCESS) + { + if (font.createFromFace(fontFace, fontSize) == BL_SUCCESS) + { + return true; + } + } + } + + return false; +} + +BLCompOp PDFBLPaintEngine::getBLCompOp(QPainter::CompositionMode mode) +{ + switch (mode) + { + case QPainter::CompositionMode_SourceOver: + return BL_COMP_OP_SRC_OVER; + case QPainter::CompositionMode_DestinationOver: + return BL_COMP_OP_DST_OVER; + case QPainter::CompositionMode_Clear: + return BL_COMP_OP_CLEAR; + case QPainter::CompositionMode_Source: + return BL_COMP_OP_SRC_COPY; + case QPainter::CompositionMode_Destination: + return BL_COMP_OP_DST_COPY; + case QPainter::CompositionMode_SourceIn: + return BL_COMP_OP_SRC_IN; + case QPainter::CompositionMode_DestinationIn: + return BL_COMP_OP_DST_IN; + case QPainter::CompositionMode_SourceOut: + return BL_COMP_OP_SRC_OUT; + case QPainter::CompositionMode_DestinationOut: + return BL_COMP_OP_DST_OUT; + case QPainter::CompositionMode_SourceAtop: + return BL_COMP_OP_SRC_ATOP; + case QPainter::CompositionMode_DestinationAtop: + return BL_COMP_OP_DST_ATOP; + case QPainter::CompositionMode_Xor: + return BL_COMP_OP_XOR; + case QPainter::CompositionMode_Plus: + return BL_COMP_OP_PLUS; + case QPainter::CompositionMode_Multiply: + return BL_COMP_OP_MULTIPLY; + case QPainter::CompositionMode_Screen: + return BL_COMP_OP_SCREEN; + case QPainter::CompositionMode_Overlay: + return BL_COMP_OP_OVERLAY; + case QPainter::CompositionMode_Darken: + return BL_COMP_OP_DARKEN; + case QPainter::CompositionMode_Lighten: + return BL_COMP_OP_LIGHTEN; + case QPainter::CompositionMode_ColorDodge: + return BL_COMP_OP_COLOR_DODGE; + case QPainter::CompositionMode_ColorBurn: + return BL_COMP_OP_COLOR_BURN; + case QPainter::CompositionMode_HardLight: + return BL_COMP_OP_HARD_LIGHT; + case QPainter::CompositionMode_SoftLight: + return BL_COMP_OP_SOFT_LIGHT; + case QPainter::CompositionMode_Difference: + return BL_COMP_OP_DIFFERENCE; + case QPainter::CompositionMode_Exclusion: + return BL_COMP_OP_EXCLUSION; + default: + break; + } + + return BL_COMP_OP_SRC_OVER; +} + +} // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfblpainter.h b/Pdf4QtLibCore/sources/pdfblpainter.h new file mode 100644 index 0000000..5db20fd --- /dev/null +++ b/Pdf4QtLibCore/sources/pdfblpainter.h @@ -0,0 +1,49 @@ +// Copyright (C) 2024 Jakub Melka +// +// This file is part of PDF4QT. +// +// PDF4QT is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// with the written consent of the copyright owner, any later version. +// +// PDF4QT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDF4QT. If not, see . + +#ifndef PDFBLPAINTER_H +#define PDFBLPAINTER_H + +#include "pdfglobal.h" + +#include +#include + +namespace pdf +{ +class PDFBLPaintEngine; + +class PDF4QTLIBCORESHARED_EXPORT PDFBLPaintDevice : public QPaintDevice +{ +public: + PDFBLPaintDevice(QImage& offscreenBuffer, bool isMultithreaded); + virtual ~PDFBLPaintDevice() override; + + virtual int devType() const override; + virtual QPaintEngine* paintEngine() const override; + +protected: + virtual int metric(PaintDeviceMetric metric) const override; + +private: + QImage& m_offscreenBuffer; + PDFBLPaintEngine* m_paintEngine; +}; + +} // namespace pdf + +#endif // PDFBLPAINTER_H diff --git a/Pdf4QtLibCore/sources/pdffont.cpp b/Pdf4QtLibCore/sources/pdffont.cpp index ecd1df5..79c452e 100644 --- a/Pdf4QtLibCore/sources/pdffont.cpp +++ b/Pdf4QtLibCore/sources/pdffont.cpp @@ -2685,4 +2685,16 @@ CharacterInfos PDFRealizedType3FontImpl::getCharacterInfos() const return result; } +QByteArray PDFSystemFont::getFontData(const QByteArray& fontName) +{ + const PDFSystemFontInfoStorage* storage = PDFSystemFontInfoStorage::getInstance(); + + CIDSystemInfo systemInfo; + PDFRenderErrorReporterDummy reporter; + FontDescriptor descriptor; + descriptor.fontName = fontName; + + return storage->loadFont(&systemInfo, &descriptor, StandardFontType::Invalid, &reporter); +} + } // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdffont.h b/Pdf4QtLibCore/sources/pdffont.h index 73ba36b..9f5863e 100644 --- a/Pdf4QtLibCore/sources/pdffont.h +++ b/Pdf4QtLibCore/sources/pdffont.h @@ -698,6 +698,12 @@ private: std::map m_cmaps; }; +class PDF4QTLIBCORESHARED_EXPORT PDFSystemFont +{ +public: + static QByteArray getFontData(const QByteArray& fontName); +}; + } // namespace pdf #endif // PDFFONT_H diff --git a/Pdf4QtLibCore/sources/pdfpainter.cpp b/Pdf4QtLibCore/sources/pdfpainter.cpp index 314722f..00d23d1 100644 --- a/Pdf4QtLibCore/sources/pdfpainter.cpp +++ b/Pdf4QtLibCore/sources/pdfpainter.cpp @@ -24,8 +24,6 @@ #include #include -#include - #include "pdfdbgheap.h" namespace pdf @@ -623,135 +621,6 @@ void PDFPrecompiledPage::draw(QPainter* painter, painter->restore(); } -void PDFPrecompiledPage::draw(BLContext& painter, - const QRectF& cropBox, - const QTransform& pagePointToDevicePointMatrix, - PDFRenderer::Features features, - PDFReal opacity) const -{ - Q_ASSERT(painter); - Q_ASSERT(pagePointToDevicePointMatrix.isInvertible()); - - painter.save(); - painter.setMatrix(BLMatrix2D()); - painter.setGlobalAlpha(opacity); - - if (features.testFlag(PDFRenderer::ClipToCropBox) && cropBox.isValid()) - { - QRectF mappedCropBox = pagePointToDevicePointMatrix.mapRect(cropBox); - BLRect clipRect = PDFPainterHelper::getBLRect(mappedCropBox); - painter.clipToRect(clipRect); - } - - // Process all instructions - for (const Instruction& instruction : m_instructions) - { - switch (instruction.type) - { - case InstructionType::DrawPath: - { - const PathPaintData& data = m_paths[instruction.dataIndex]; - BLPath path = PDFPainterHelper::getBLPath(data.path); - - PDFPainterHelper::setBLPen(painter, data.pen); - PDFPainterHelper::setBLBrush(painter, data.brush); - - if (data.brush.style() != Qt::NoBrush) - { - painter.fillPath(path); - } - - if (data.pen.style() != Qt::NoPen) - { - painter.strokePath(path); - } - break; - } - - case InstructionType::DrawImage: - { - const ImageData& data = m_images[instruction.dataIndex]; - QImage image = data.image; - - if (image.format() != QImage::Format_ARGB32_Premultiplied) - { - image.convertTo(QImage::Format_ARGB32_Premultiplied); - } - - painter.save(); - - BLImage blImage; - blImage.createFromData(image.width(), image.height(), BL_FORMAT_PRGB32, image.bits(), image.bytesPerLine()); - - BLMatrix2D imageTransform(1.0 / image.width(), 0, 0, 1.0 / image.height(), 0, 0); - BLMatrix2D worldTransform = imageTransform; - worldTransform.postTransform(painter.userMatrix()); - - // Jakub Melka: Because Qt uses opposite axis direction than PDF, then we must transform the y-axis - // to the opposite (so the image is then unchanged) - worldTransform.translate(0, image.height()); - worldTransform.scale(1, -1); - - painter.setMatrix(worldTransform); - painter.blitImage(BLPointI(0, 0), blImage); - painter.restore(); - break; - } - - case InstructionType::DrawMesh: - { - const MeshPaintData& data = m_meshes[instruction.dataIndex]; - - painter.save(); - painter.setMatrix(PDFPainterHelper::getBLMatrix(pagePointToDevicePointMatrix)); - data.mesh.paint(painter, data.alpha); - painter.restore(); - break; - } - - case InstructionType::Clip: - { - // Blend2D does not have path clipping - break; - } - - case InstructionType::SaveGraphicState: - { - painter.save(); - break; - } - - case InstructionType::RestoreGraphicState: - { - painter.restore(); - break; - } - - case InstructionType::SetWorldMatrix: - { - QTransform transform(m_matrices[instruction.dataIndex] * pagePointToDevicePointMatrix); - BLMatrix2D matrix = PDFPainterHelper::getBLMatrix(transform); - painter.setMatrix(matrix); - break; - } - - case InstructionType::SetCompositionMode: - { - painter.setCompOp(PDFPainterHelper::getBLCompOp(m_compositionModes[instruction.dataIndex])); - break; - } - - default: - { - Q_ASSERT(false); - break; - } - } - } - - painter.restore(); -} - void PDFPrecompiledPage::redact(QPainterPath redactPath, const QTransform& matrix, QColor color) { if (redactPath.isEmpty()) diff --git a/Pdf4QtLibCore/sources/pdfpainter.h b/Pdf4QtLibCore/sources/pdfpainter.h index c4d0fea..0ca55ef 100644 --- a/Pdf4QtLibCore/sources/pdfpainter.h +++ b/Pdf4QtLibCore/sources/pdfpainter.h @@ -30,8 +30,6 @@ #include #include -class BLContext; - namespace pdf { @@ -198,18 +196,6 @@ public: PDFRenderer::Features features, PDFReal opacity) const; - /// Paints page onto the blend2d painter using matrix - /// \param painter Painter, onto which is page drawn - /// \param cropBox Page's crop box - /// \param pagePointToDevicePointMatrix Page point to device point transformation matrix - /// \param features Renderer features - /// \param opacity Opacity of page graphics - void draw(BLContext& painter, - const QRectF& cropBox, - const QTransform& pagePointToDevicePointMatrix, - PDFRenderer::Features features, - PDFReal opacity) const; - /// Redact path - remove all content intersecting given path, /// and fill redact path with given color. /// \param redactPath Redaction path in page coordinates diff --git a/Pdf4QtLibCore/sources/pdfpainterutils.cpp b/Pdf4QtLibCore/sources/pdfpainterutils.cpp index 3a36ae9..5ff65bf 100644 --- a/Pdf4QtLibCore/sources/pdfpainterutils.cpp +++ b/Pdf4QtLibCore/sources/pdfpainterutils.cpp @@ -64,277 +64,5 @@ QRect PDFPainterHelper::drawBubble(QPainter* painter, QPoint point, QColor color return rectangle; } -BLMatrix2D PDFPainterHelper::getBLMatrix(QTransform transform) -{ - BLMatrix2D matrix; - matrix.reset(transform.m11(), transform.m12(), transform.m21(), transform.m22(), transform.dx(), transform.dy()); - return matrix; -} - -BLRect PDFPainterHelper::getBLRect(QRect rect) -{ - return BLRect(rect.x(), rect.y(), rect.width(), rect.height()); -} - -BLRect PDFPainterHelper::getBLRect(QRectF rect) -{ - return BLRect(rect.x(), rect.y(), rect.width(), rect.height()); -} - -BLPath PDFPainterHelper::getBLPath(const QPainterPath& path) -{ - BLPath blPath; - - int elementCount = path.elementCount(); - for (int i = 0; i < elementCount; ++i) { - const QPainterPath::Element& element = path.elementAt(i); - - switch (element.type) - { - case QPainterPath::MoveToElement: - blPath.moveTo(element.x, element.y); - break; - - case QPainterPath::LineToElement: - blPath.lineTo(element.x, element.y); - break; - - case QPainterPath::CurveToElement: - if (i + 2 < elementCount) - { - const QPainterPath::Element& ctrlPoint1 = path.elementAt(i++); - const QPainterPath::Element& ctrlPoint2 = path.elementAt(i++); - const QPainterPath::Element& endPoint = path.elementAt(i); - blPath.cubicTo(ctrlPoint1.x, ctrlPoint1.y, ctrlPoint2.x, ctrlPoint2.y, endPoint.x, endPoint.y); - } - break; - - case QPainterPath::CurveToDataElement: - Q_ASSERT(false); - break; - } - } - - return blPath; -} - -void PDFPainterHelper::setBLPen(BLContext& context, const QPen& pen) -{ - const Qt::PenCapStyle capStyle = pen.capStyle(); - const Qt::PenJoinStyle joinStyle = pen.joinStyle(); - const QColor color = pen.color(); - const qreal width = pen.widthF(); - const qreal miterLimit = pen.miterLimit(); - const qreal dashOffset = pen.dashOffset(); - const QList customDashPattern = pen.dashPattern(); - const Qt::PenStyle penStyle = pen.style(); - - context.setStrokeAlpha(pen.color().alphaF()); - context.setStrokeWidth(width); - context.setStrokeMiterLimit(miterLimit); - - switch (capStyle) - { - case Qt::FlatCap: - context.setStrokeCaps(BL_STROKE_CAP_BUTT); - break; - case Qt::SquareCap: - context.setStrokeCaps(BL_STROKE_CAP_SQUARE); - break; - case Qt::RoundCap: - context.setStrokeCaps(BL_STROKE_CAP_ROUND); - break; - } - - BLArray dashArray; - - for (double value : customDashPattern) - { - dashArray.append(value); - } - - context.setStrokeDashOffset(dashOffset); - context.setStrokeDashArray(dashArray); - - switch (joinStyle) - { - case Qt::MiterJoin: - context.setStrokeJoin(BL_STROKE_JOIN_MITER_CLIP); - break; - case Qt::BevelJoin: - context.setStrokeJoin(BL_STROKE_JOIN_BEVEL); - break; - case Qt::RoundJoin: - context.setStrokeJoin(BL_STROKE_JOIN_ROUND); - break; - case Qt::SvgMiterJoin: - context.setStrokeJoin(BL_STROKE_JOIN_MITER_CLIP); - break; - } - - context.setStrokeStyle(BLRgba32(color.rgba())); - - BLStrokeOptions strokeOptions = context.strokeOptions(); - - switch (penStyle) - { - case Qt::SolidLine: - strokeOptions.dashArray.clear(); - strokeOptions.dashOffset = 0.0; - break; - - case Qt::DashLine: - { - constexpr double dashPattern[] = {4, 4}; - strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); - break; - } - - case Qt::DotLine: - { - constexpr double dashPattern[] = {1, 3}; - strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); - break; - } - - case Qt::DashDotLine: - { - constexpr double dashPattern[] = {4, 2, 1, 2}; - strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); - break; - } - - case Qt::DashDotDotLine: - { - constexpr double dashPattern[] = {4, 2, 1, 2, 1, 2}; - strokeOptions.dashArray.assignData(dashPattern, std::size(dashPattern)); - break; - } - - default: - break; - } - - context.setStrokeOptions(strokeOptions); -} - -void PDFPainterHelper::setBLBrush(BLContext& context, const QBrush& brush) -{ - auto setGradientStops = [](BLGradient& blGradient, const auto& qGradient) - { - QVector stops; - for (const auto& stop : qGradient.stops()) - { - stops.append(BLGradientStop(stop.first, BLRgba32(stop.second.red(), stop.second.green(), stop.second.blue(), stop.second.alpha()))); - } - blGradient.assignStops(stops.constData(), stops.size()); - }; - - switch (brush.style()) - { - default: - case Qt::SolidPattern: - { - QColor color = brush.color(); - BLRgba32 blColor = BLRgba32(color.red(), color.green(), color.blue(), color.alpha()); - context.setFillStyle(blColor); - break; - } - case Qt::LinearGradientPattern: - { - const QGradient* gradient = brush.gradient(); - if (gradient && gradient->type() == QGradient::LinearGradient) - { - const QLinearGradient* linearGradient = static_cast(gradient); - BLLinearGradientValues blLinearGradient; - blLinearGradient.x0 = linearGradient->start().x(); - blLinearGradient.y0 = linearGradient->start().y(); - blLinearGradient.x1 = linearGradient->finalStop().x(); - blLinearGradient.y1 = linearGradient->finalStop().y(); - BLGradient blGradient(blLinearGradient); - setGradientStops(blGradient, *gradient); - context.setFillStyle(blGradient); - } - break; - } - case Qt::RadialGradientPattern: - { - const QGradient* gradient = brush.gradient(); - if (gradient && gradient->type() == QGradient::RadialGradient) - { - const QRadialGradient* radialGradient = static_cast(gradient); - BLRadialGradientValues blRadialGradientValues; - blRadialGradientValues.x0 = radialGradient->center().x(); - blRadialGradientValues.y0 = radialGradient->center().y(); - blRadialGradientValues.x1 = radialGradient->focalPoint().x(); - blRadialGradientValues.y1 = radialGradient->focalPoint().y(); - blRadialGradientValues.r0 = radialGradient->radius(); - BLGradient blGradient(blRadialGradientValues); - setGradientStops(blGradient, *gradient); - context.setFillStyle(blGradient); - } - break; - } - } -} - -BLCompOp PDFPainterHelper::getBLCompOp(QPainter::CompositionMode mode) -{ - switch (mode) - { - case QPainter::CompositionMode_SourceOver: - return BL_COMP_OP_SRC_OVER; - case QPainter::CompositionMode_DestinationOver: - return BL_COMP_OP_DST_OVER; - case QPainter::CompositionMode_Clear: - return BL_COMP_OP_CLEAR; - case QPainter::CompositionMode_Source: - return BL_COMP_OP_SRC_COPY; - case QPainter::CompositionMode_Destination: - return BL_COMP_OP_DST_COPY; - case QPainter::CompositionMode_SourceIn: - return BL_COMP_OP_SRC_IN; - case QPainter::CompositionMode_DestinationIn: - return BL_COMP_OP_DST_IN; - case QPainter::CompositionMode_SourceOut: - return BL_COMP_OP_SRC_OUT; - case QPainter::CompositionMode_DestinationOut: - return BL_COMP_OP_DST_OUT; - case QPainter::CompositionMode_SourceAtop: - return BL_COMP_OP_SRC_ATOP; - case QPainter::CompositionMode_DestinationAtop: - return BL_COMP_OP_DST_ATOP; - case QPainter::CompositionMode_Xor: - return BL_COMP_OP_XOR; - case QPainter::CompositionMode_Plus: - return BL_COMP_OP_PLUS; - case QPainter::CompositionMode_Multiply: - return BL_COMP_OP_MULTIPLY; - case QPainter::CompositionMode_Screen: - return BL_COMP_OP_SCREEN; - case QPainter::CompositionMode_Overlay: - return BL_COMP_OP_OVERLAY; - case QPainter::CompositionMode_Darken: - return BL_COMP_OP_DARKEN; - case QPainter::CompositionMode_Lighten: - return BL_COMP_OP_LIGHTEN; - case QPainter::CompositionMode_ColorDodge: - return BL_COMP_OP_COLOR_DODGE; - case QPainter::CompositionMode_ColorBurn: - return BL_COMP_OP_COLOR_BURN; - case QPainter::CompositionMode_HardLight: - return BL_COMP_OP_HARD_LIGHT; - case QPainter::CompositionMode_SoftLight: - return BL_COMP_OP_SOFT_LIGHT; - case QPainter::CompositionMode_Difference: - return BL_COMP_OP_DIFFERENCE; - case QPainter::CompositionMode_Exclusion: - return BL_COMP_OP_EXCLUSION; - default: - break; - } - - return BL_COMP_OP_SRC_OVER; -} } // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfpainterutils.h b/Pdf4QtLibCore/sources/pdfpainterutils.h index ba98566..ecef6e1 100644 --- a/Pdf4QtLibCore/sources/pdfpainterutils.h +++ b/Pdf4QtLibCore/sources/pdfpainterutils.h @@ -22,8 +22,6 @@ #include -#include - namespace pdf { @@ -57,27 +55,6 @@ public: /// \param text Text inside the bubble /// \param alignment Bubble alignment relative to the bubble position point static QRect drawBubble(QPainter* painter, QPoint point, QColor color, QString text, Qt::Alignment alignment); - - /// Get BL matrix from transformation - static BLMatrix2D getBLMatrix(QTransform transform); - - /// Get BL rect from regular rect - static BLRect getBLRect(QRect rect); - - /// Get BL rect from regular rect - static BLRect getBLRect(QRectF rect); - - /// Get BL path from path - static BLPath getBLPath(const QPainterPath& path); - - /// Set pen to the context - static void setBLPen(BLContext& context, const QPen& pen); - - /// Set brush to the context - static void setBLBrush(BLContext& context, const QBrush& brush); - - /// Returns composition operator - static BLCompOp getBLCompOp(QPainter::CompositionMode mode); }; } // namespace pdf diff --git a/Pdf4QtLibCore/sources/pdfpattern.cpp b/Pdf4QtLibCore/sources/pdfpattern.cpp index 96f655c..ee7d1ac 100644 --- a/Pdf4QtLibCore/sources/pdfpattern.cpp +++ b/Pdf4QtLibCore/sources/pdfpattern.cpp @@ -27,8 +27,6 @@ #include #include -#include - #include "pdfdbgheap.h" #include @@ -1362,45 +1360,6 @@ void PDFMesh::paint(QPainter* painter, PDFReal alpha) const painter->restore(); } -void PDFMesh::paint(BLContext& context, PDFReal alpha) const -{ - if (m_triangles.empty()) - { - return; - } - - context.save(); - PDFPainterHelper::setBLPen(context, Qt::NoPen); - - if (!m_backgroundPath.isEmpty() && m_backgroundColor.isValid()) - { - QColor backgroundColor = m_backgroundColor; - backgroundColor.setAlphaF(alpha); - PDFPainterHelper::setBLBrush(context, QBrush(backgroundColor, Qt::SolidPattern)); - context.fillPath(PDFPainterHelper::getBLPath(m_backgroundPath)); - } - - QColor color; - - // Draw all triangles - for (const Triangle& triangle : m_triangles) - { - if (color != triangle.color) - { - QColor newColor(triangle.color); - newColor.setAlphaF(alpha); - PDFPainterHelper::setBLBrush(context, QBrush(newColor, Qt::SolidPattern)); - color = newColor; - } - - context.fillTriangle(m_vertices[triangle.v1].x(), m_vertices[triangle.v1].y(), - m_vertices[triangle.v2].x(), m_vertices[triangle.v2].y(), - m_vertices[triangle.v3].x(), m_vertices[triangle.v3].y()); - } - - context.restore(); -} - void PDFMesh::transform(const QTransform& matrix) { for (QPointF& vertex : m_vertices) diff --git a/Pdf4QtLibCore/sources/pdfpattern.h b/Pdf4QtLibCore/sources/pdfpattern.h index 217e079..a50d4ab 100644 --- a/Pdf4QtLibCore/sources/pdfpattern.h +++ b/Pdf4QtLibCore/sources/pdfpattern.h @@ -29,8 +29,6 @@ #include -class BLContext; - namespace pdf { class PDFPattern; @@ -96,11 +94,6 @@ public: /// \param alpha Opacity factor void paint(QPainter* painter, PDFReal alpha) const; - /// Paints the mesh on the context - /// \param context Painter, onto which is mesh drawn - /// \param alpha Opacity factor - void paint(BLContext& context, PDFReal alpha) const; - /// Transforms the mesh according to the matrix transform /// \param matrix Matrix transform to be performed void transform(const QTransform& matrix); diff --git a/Pdf4QtLibCore/sources/pdfrenderer.cpp b/Pdf4QtLibCore/sources/pdfrenderer.cpp index 585dea0..85f391f 100644 --- a/Pdf4QtLibCore/sources/pdfrenderer.cpp +++ b/Pdf4QtLibCore/sources/pdfrenderer.cpp @@ -21,13 +21,12 @@ #include "pdfexecutionpolicy.h" #include "pdfprogress.h" #include "pdfannotation.h" +#include "pdfblpainter.h" #include #include #include -#include - #include "pdfdbgheap.h" namespace pdf @@ -250,26 +249,16 @@ QImage PDFRasterizer::render(PDFInteger pageIndex, if (m_rendererEngine == RendererEngine::Blend2D) { - BLContext blContext; - BLImage blImage; + PDFBLPaintDevice blPaintDevice(image, false); - blContext.setHint(BL_CONTEXT_HINT_RENDERING_QUALITY, BL_RENDERING_QUALITY_MAX_VALUE); + QPainter painter(&blPaintDevice); + compiledPage->draw(&painter, page->getCropBox(), matrix, features, 1.0); - blImage.createFromData(image.width(), image.height(), BL_FORMAT_PRGB32, image.bits(), image.bytesPerLine()); - if (blContext.begin(blImage) == BL_SUCCESS) + if (annotationManager) { - blContext.clearAll(); - - compiledPage->draw(blContext, page->getCropBox(), matrix, features, 1.0); - - if (annotationManager) - { - QList errors; - PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); - annotationManager->drawPage(blContext, pageIndex, compiledPage, textLayoutGetter, matrix, errors); - } - - blContext.end(); + QList errors; + PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); + annotationManager->drawPage(&painter, pageIndex, compiledPage, textLayoutGetter, matrix, errors); } } else diff --git a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp index 8806827..6dade91 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp +++ b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.cpp @@ -32,8 +32,6 @@ #include #include -#include - #include "pdfdbgheap.h" namespace pdf @@ -768,18 +766,6 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect) } } -void PDFDrawWidgetProxy::draw(BLContext& context, QRect rect) -{ - drawPages(context, rect, m_features); - - for (IDocumentDrawInterface* drawInterface : m_drawInterfaces) - { - context.save(); - drawInterface->drawPostRendering(context, rect); - context.restore(); - } -} - QColor PDFDrawWidgetProxy::getPaperColor() { QColor paperColor = getCMSManager()->getCurrentCMS()->getPaperColor(); @@ -935,153 +921,6 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F } } -void PDFDrawWidgetProxy::drawPages(BLContext& context, QRect rect, PDFRenderer::Features features) -{ - PDFPainterHelper::setBLBrush(context, QBrush(Qt::lightGray)); - context.fillRect(rect); - BLMatrix2D baseMatrix = context.userMatrix(); - - // Use current paper color (it can be a bit different from white) - QColor paperColor = getPaperColor(); - - // Iterate trough pages and display them on the painter device - for (const LayoutItem& item : m_layout.items) - { - // The offsets m_horizontalOffset and m_verticalOffset are offsets to the - // topleft point of the block. But block maybe doesn't start at (0, 0), - // so we must also use translation from the block beginning. - QRect placedRect = item.pageRect.translated(m_horizontalOffset - m_layout.blockRect.left(), m_verticalOffset - m_layout.blockRect.top()); - if (placedRect.intersects(rect)) - { - GroupInfo groupInfo = getGroupInfo(item.groupIndex); - - // Clear the page space by paper color - if (groupInfo.drawPaper) - { - PDFPainterHelper::setBLBrush(context, paperColor); - context.fillRect(placedRect); - } - - const PDFPrecompiledPage* compiledPage = m_compiler->getCompiledPage(item.pageIndex, true); - if (compiledPage && compiledPage->isValid()) - { - QElapsedTimer timer; - timer.start(); - - const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex); - QTransform matrix = QTransform(createPagePointToDevicePointMatrix(page, placedRect)) * baseMatrix; - compiledPage->draw(context, page->getCropBox(), matrix, features, groupInfo.transparency); - PDFTextLayoutGetter layoutGetter = m_textLayoutCompiler->getTextLayoutLazy(item.pageIndex); - - // Draw text blocks/text lines, if it is enabled - if (features.testFlag(PDFRenderer::DebugTextBlocks)) - { - m_textLayoutCompiler->makeTextLayout(); - const PDFTextLayout& layout = layoutGetter; - const PDFTextBlocks& textBlocks = layout.getTextBlocks(); - - context.save(); - painter->setFont(m_widget->font()); - painter->setPen(Qt::red); - painter->setBrush(QColor(255, 0, 0, 128)); - - QFontMetricsF fontMetrics(painter->font(), painter->device()); - int blockIndex = 1; - for (const PDFTextBlock& block : textBlocks) - { - QString blockNumber = QString::number(blockIndex++); - - painter->drawPath(matrix.map(block.getBoundingBox())); - painter->drawText(matrix.map(block.getTopLeft()) - QPointF(fontMetrics.horizontalAdvance(blockNumber), 0), blockNumber, Qt::TextSingleLine, 0); - } - - context.restore(); - } - if (features.testFlag(PDFRenderer::DebugTextLines)) - { - m_textLayoutCompiler->makeTextLayout(); - const PDFTextLayout& layout = layoutGetter; - const PDFTextBlocks& textBlocks = layout.getTextBlocks(); - - context.save(); - painter->setFont(m_widget->font()); - painter->setPen(Qt::green); - painter->setBrush(QColor(0, 255, 0, 128)); - - QFontMetricsF fontMetrics(painter->font(), painter->device()); - int lineIndex = 1; - for (const PDFTextBlock& block : textBlocks) - { - for (const PDFTextLine& line : block.getLines()) - { - QString lineNumber = QString::number(lineIndex++); - - painter->drawPath(matrix.map(line.getBoundingBox())); - painter->drawText(matrix.map(line.getTopLeft()) - QPointF(fontMetrics.horizontalAdvance(lineNumber), 0), lineNumber, Qt::TextSingleLine, 0); - } - } - - context.restore(); - } - - QList drawInterfaceErrors; - if (!features.testFlag(PDFRenderer::DenyExtraGraphics)) - { - for (IDocumentDrawInterface* drawInterface : m_drawInterfaces) - { - context.save(); - drawInterface->drawPage(context, item.pageIndex, compiledPage, layoutGetter, matrix, drawInterfaceErrors); - context.restore(); - } - } - - const qint64 drawTimeNS = timer.nsecsElapsed(); - - // Draw rendering times - if (features.testFlag(PDFRenderer::DisplayTimes)) - { - QFont font = m_widget->font(); - font.setPointSize(12); - - auto formatDrawTime = [](qint64 nanoseconds) - { - PDFReal miliseconds = nanoseconds / 1000000.0; - return QString::number(miliseconds, 'f', 3); - }; - - QFontMetrics fontMetrics(font); - const int lineSpacing = fontMetrics.lineSpacing(); - - context.save(); - painter->setPen(Qt::red); - painter->setFont(font); - context.translate(placedRect.topLeft()); - context.translate(placedRect.width() / 20.0, placedRect.height() / 20.0); // Offset - - painter->setBackground(QBrush(Qt::white)); - painter->setBackgroundMode(Qt::OpaqueMode); - painter->drawText(0, 0, PDFTranslationContext::tr("Compile time: %1 [ms]").arg(formatDrawTime(compiledPage->getCompilingTimeNS()))); - painter->translate(0, lineSpacing); - painter->drawText(0, 0, PDFTranslationContext::tr("Draw time: %1 [ms]").arg(formatDrawTime(drawTimeNS))); - - context.restore(); - } - - const QList& pageErrors = compiledPage->getErrors(); - if (!pageErrors.empty() || !drawInterfaceErrors.empty()) - { - QList errors = pageErrors; - if (!drawInterfaceErrors.isEmpty()) - { - errors.append(drawInterfaceErrors); - } - Q_EMIT renderingError(item.pageIndex, qMove(errors)); - } - } - } - } -} - QImage PDFDrawWidgetProxy::drawThumbnailImage(PDFInteger pageIndex, int pixelSize) const { QImage image; diff --git a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.h b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.h index 4c429ee..ab3f9eb 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.h +++ b/Pdf4QtLibWidgets/sources/pdfdrawspacecontroller.h @@ -34,8 +34,6 @@ class QPainter; class QScrollBar; class QTimer; -class BLContext; - namespace pdf { class PDFProgress; @@ -214,15 +212,6 @@ public: /// \param rect Rectangle in which the content is painted void draw(QPainter* painter, QRect rect); - /// Draws the actually visible pages on the context using the rectangle. - /// Rectangle is space in the widget, which is used for painting the PDF. - /// This function is using drawPages function to draw all pages. After that, - /// custom drawing is performed. - /// \sa drawPages - /// \param context Context to paint the PDF pages - /// \param rect Rectangle in which the content is painted - void draw(BLContext& context, QRect rect); - /// Draws the actually visible pages on the painter using the rectangle. /// Rectangle is space in the widget, which is used for painting the PDF. /// \param painter Painter to paint the PDF pages @@ -230,13 +219,6 @@ public: /// \param features Rendering features void drawPages(QPainter* painter, QRect rect, PDFRenderer::Features features); - /// Draws the actually visible pages on the painter using the rectangle. - /// Rectangle is space in the widget, which is used for painting the PDF. - /// \param painter Painter to paint the PDF pages - /// \param rect Rectangle in which the content is painted - /// \param features Rendering features - void drawPages(BLContext& context, QRect rect, PDFRenderer::Features features); - /// Draws thumbnail image of the given size (so larger of the page size /// width or height equals to pixel size and the latter size is rescaled /// using the aspect ratio) diff --git a/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp b/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp index b034a3a..73ee74a 100644 --- a/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp +++ b/Pdf4QtLibWidgets/sources/pdfdrawwidget.cpp @@ -22,6 +22,7 @@ #include "pdfannotation.h" #include "pdfwidgetannotation.h" #include "pdfwidgetformmanager.h" +#include "pdfblpainter.h" #include #include @@ -30,8 +31,6 @@ #include #include -#include - #include "pdfdbgheap.h" namespace pdf @@ -583,33 +582,23 @@ void PDFDrawWidget::paintEvent(QPaintEvent* event) { case RendererEngine::Blend2D: { - BLContext blContext; - BLImage blImage; - QRect rect = this->rect(); if (m_blend2DframeBuffer.size() != rect.size()) { m_blend2DframeBuffer = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied); } - BLContextCreateInfo info{}; - info.reset(); - info.flags = BL_CONTEXT_CREATE_FLAG_FALLBACK_TO_SYNC; - info.threadCount = QThread::idealThreadCount(); + PDFBLPaintDevice blPaintDevice(m_blend2DframeBuffer, true); + QPainter blPainter; - blContext.setHint(BL_CONTEXT_HINT_RENDERING_QUALITY, BL_RENDERING_QUALITY_MAX_VALUE); - - blImage.createFromData(m_blend2DframeBuffer.width(), m_blend2DframeBuffer.height(), BL_FORMAT_PRGB32, m_blend2DframeBuffer.bits(), m_blend2DframeBuffer.bytesPerLine()); - if (blContext.begin(blImage, info) == BL_SUCCESS) + if (blPainter.begin(&blPaintDevice)) { - blContext.clearAll(); - getPDFWidget()->getDrawWidgetProxy()->draw(blContext, rect); - blContext.end(); - - QPainter painter(this); - painter.drawImage(QPoint(0, 0), m_blend2DframeBuffer); + getPDFWidget()->getDrawWidgetProxy()->draw(&blPainter, rect); + blPainter.end(); } + QPainter painter(this); + painter.drawImage(QPoint(0, 0), m_blend2DframeBuffer); break; } diff --git a/Pdf4QtLibWidgets/sources/pdfwidgetannotation.cpp b/Pdf4QtLibWidgets/sources/pdfwidgetannotation.cpp index fda8c2e..4d797ef 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgetannotation.cpp +++ b/Pdf4QtLibWidgets/sources/pdfwidgetannotation.cpp @@ -539,9 +539,4 @@ void PDFWidgetAnnotationManager::drawPage(QPainter* painter, BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); } -void PDFWidgetAnnotationManager::drawPage(BLContext& context, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QTransform& pagePointToDevicePointMatrix, QList& errors) const -{ - BaseClass::drawPage(context, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors); -} - } // namespace pdf diff --git a/Pdf4QtLibWidgets/sources/pdfwidgetannotation.h b/Pdf4QtLibWidgets/sources/pdfwidgetannotation.h index a57c704..4ad1865 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgetannotation.h +++ b/Pdf4QtLibWidgets/sources/pdfwidgetannotation.h @@ -57,13 +57,6 @@ public: const QTransform& pagePointToDevicePointMatrix, QList& errors) const override; - virtual void drawPage(BLContext& context, - PDFInteger pageIndex, - const PDFPrecompiledPage* compiledPage, - PDFTextLayoutGetter& layoutGetter, - const QTransform& pagePointToDevicePointMatrix, - QList& errors) const override; - /// Returns tooltip generated from annotation virtual QString getTooltip() const override { return m_tooltip; } diff --git a/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp b/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp index 8c40f5c..e1de00d 100644 --- a/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp +++ b/Pdf4QtLibWidgets/sources/pdfwidgettool.cpp @@ -1197,7 +1197,7 @@ void PDFMagnifierTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event) if (m_mousePos != mousePos) { m_mousePos = mousePos; - getProxy()->repaintNeeded(); + Q_EMIT getProxy()->repaintNeeded(); } } diff --git a/Pdf4QtViewer/pdfprogramcontroller.cpp b/Pdf4QtViewer/pdfprogramcontroller.cpp index 2b8b56a..146ea89 100644 --- a/Pdf4QtViewer/pdfprogramcontroller.cpp +++ b/Pdf4QtViewer/pdfprogramcontroller.cpp @@ -624,7 +624,7 @@ void PDFProgramController::initialize(Features features, readSettings(Settings(GeneralSettings | PluginsSettings | RecentFileSettings | CertificateSettings)); - m_pdfWidget = new pdf::PDFWidget(m_CMSManager, m_settings->getRendererEngine(), m_settings->isMultisampleAntialiasingEnabled() ? m_settings->getRendererSamples() : -1, m_mainWindow); + m_pdfWidget = new pdf::PDFWidget(m_CMSManager, m_settings->getRendererEngine(), m_mainWindow); m_pdfWidget->setObjectName("pdfWidget"); m_pdfWidget->updateCacheLimits(m_settings->getCompiledPageCacheLimit() * 1024, m_settings->getThumbnailsCacheLimit(), m_settings->getFontCacheLimit(), m_settings->getInstancedFontCacheLimit()); m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress); @@ -1697,7 +1697,7 @@ void PDFProgramController::updateActionsAvailability() void PDFProgramController::onViewerSettingsChanged() { - m_pdfWidget->updateRenderer(m_settings->getRendererEngine(), m_settings->isMultisampleAntialiasingEnabled() ? m_settings->getRendererSamples() : -1); + m_pdfWidget->updateRenderer(m_settings->getRendererEngine()); m_pdfWidget->updateCacheLimits(m_settings->getCompiledPageCacheLimit() * 1024, m_settings->getThumbnailsCacheLimit(), m_settings->getFontCacheLimit(), m_settings->getInstancedFontCacheLimit()); m_pdfWidget->getDrawWidgetProxy()->setFeatures(m_settings->getFeatures()); m_pdfWidget->getDrawWidgetProxy()->setPreferredMeshResolutionRatio(m_settings->getPreferredMeshResolutionRatio()); diff --git a/Pdf4QtViewer/pdfviewersettings.cpp b/Pdf4QtViewer/pdfviewersettings.cpp index b54cbd2..4b12bf6 100644 --- a/Pdf4QtViewer/pdfviewersettings.cpp +++ b/Pdf4QtViewer/pdfviewersettings.cpp @@ -44,8 +44,6 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti m_settings.m_directory = settings.value("defaultDirectory", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); m_settings.m_features = static_cast(settings.value("rendererFeaturesv2", static_cast(pdf::PDFRenderer::getDefaultFeatures())).toInt()); m_settings.m_rendererEngine = static_cast(settings.value("renderingEngine", static_cast(pdf::RendererEngine::Blend2D)).toInt()); - m_settings.m_multisampleAntialiasing = settings.value("msaa", defaultSettings.m_multisampleAntialiasing).toBool(); - m_settings.m_rendererSamples = settings.value("rendererSamples", defaultSettings.m_rendererSamples).toInt(); m_settings.m_prefetchPages = settings.value("prefetchPages", defaultSettings.m_prefetchPages).toBool(); m_settings.m_preferredMeshResolutionRatio = settings.value("preferredMeshResolutionRatio", defaultSettings.m_preferredMeshResolutionRatio).toDouble(); m_settings.m_minimalMeshResolutionRatio = settings.value("minimalMeshResolutionRatio", defaultSettings.m_minimalMeshResolutionRatio).toDouble(); @@ -119,8 +117,6 @@ void PDFViewerSettings::writeSettings(QSettings& settings) settings.setValue("defaultDirectory", m_settings.m_directory); settings.setValue("rendererFeaturesv2", static_cast(m_settings.m_features)); settings.setValue("renderingEngine", static_cast(m_settings.m_rendererEngine)); - settings.setValue("msaa", m_settings.m_multisampleAntialiasing); - settings.setValue("rendererSamples", m_settings.m_rendererSamples); settings.setValue("prefetchPages", m_settings.m_prefetchPages); settings.setValue("preferredMeshResolutionRatio", m_settings.m_preferredMeshResolutionRatio); settings.setValue("minimalMeshResolutionRatio", m_settings.m_minimalMeshResolutionRatio); @@ -228,20 +224,6 @@ void PDFViewerSettings::setRendererEngine(pdf::RendererEngine rendererEngine) } } -int PDFViewerSettings::getRendererSamples() const -{ - return m_settings.m_rendererSamples; -} - -void PDFViewerSettings::setRendererSamples(int rendererSamples) -{ - if (m_settings.m_rendererSamples != rendererSamples) - { - m_settings.m_rendererSamples = rendererSamples; - Q_EMIT settingsChanged(); - } -} - void PDFViewerSettings::setPreferredMeshResolutionRatio(pdf::PDFReal preferredMeshResolutionRatio) { if (m_settings.m_preferredMeshResolutionRatio != preferredMeshResolutionRatio) @@ -272,8 +254,6 @@ void PDFViewerSettings::setColorTolerance(pdf::PDFReal colorTolerance) PDFViewerSettings::Settings::Settings() : m_features(pdf::PDFRenderer::getDefaultFeatures()), m_rendererEngine(pdf::RendererEngine::Blend2D), - m_multisampleAntialiasing(true), - m_rendererSamples(16), m_prefetchPages(true), m_preferredMeshResolutionRatio(0.02), m_minimalMeshResolutionRatio(0.005), diff --git a/Pdf4QtViewer/pdfviewersettings.h b/Pdf4QtViewer/pdfviewersettings.h index 1e73f9b..443b03e 100644 --- a/Pdf4QtViewer/pdfviewersettings.h +++ b/Pdf4QtViewer/pdfviewersettings.h @@ -111,11 +111,7 @@ public: pdf::RendererEngine getRendererEngine() const; void setRendererEngine(pdf::RendererEngine rendererEngine); - int getRendererSamples() const; - void setRendererSamples(int rendererSamples); - bool isPagePrefetchingEnabled() const { return m_settings.m_prefetchPages; } - bool isMultisampleAntialiasingEnabled() const { return m_settings.m_multisampleAntialiasing; } pdf::PDFReal getPreferredMeshResolutionRatio() const { return m_settings.m_preferredMeshResolutionRatio; } void setPreferredMeshResolutionRatio(pdf::PDFReal preferredMeshResolutionRatio); diff --git a/Pdf4QtViewer/pdfviewersettingsdialog.cpp b/Pdf4QtViewer/pdfviewersettingsdialog.cpp index 735f48e..b8ef740 100644 --- a/Pdf4QtViewer/pdfviewersettingsdialog.cpp +++ b/Pdf4QtViewer/pdfviewersettingsdialog.cpp @@ -105,11 +105,6 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin ui->renderingEngineComboBox->addItem(tr("Software | QPainter"), static_cast(pdf::RendererEngine::QPainter)); ui->renderingEngineComboBox->addItem(tr("Software | Blend2D | Multithreaded"), static_cast(pdf::RendererEngine::Blend2D)); - for (int i : { 1, 2, 4, 8, 16 }) - { - ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i); - } - ui->multithreadingComboBox->addItem(tr("Single thread"), static_cast(pdf::PDFExecutionPolicy::Strategy::SingleThreaded)); ui->multithreadingComboBox->addItem(tr("Multithreading (load balanced)"), static_cast(pdf::PDFExecutionPolicy::Strategy::PageMultithreaded)); ui->multithreadingComboBox->addItem(tr("Multithreading (maximum threads)"), static_cast(pdf::PDFExecutionPolicy::Strategy::AlwaysMultithreaded)); @@ -409,14 +404,6 @@ void PDFViewerSettingsDialog::saveData() { m_settings.m_rendererEngine = static_cast(ui->renderingEngineComboBox->currentData().toInt()); } - else if (sender == ui->multisampleAntialiasingCheckBox) - { - m_settings.m_multisampleAntialiasing = ui->multisampleAntialiasingCheckBox->isChecked(); - } - else if (sender == ui->multisampleAntialiasingSamplesCountComboBox) - { - m_settings.m_rendererSamples = ui->multisampleAntialiasingSamplesCountComboBox->currentData().toInt(); - } else if (sender == ui->prefetchPagesCheckBox) { m_settings.m_prefetchPages = ui->prefetchPagesCheckBox->isChecked();