Issue #123: Blend2D painting implementation

This commit is contained in:
Jakub Melka
2024-02-10 18:04:58 +01:00
parent d314683d38
commit f3e1a94e1c
25 changed files with 844 additions and 778 deletions

View File

@ -32,8 +32,6 @@
#include <QScreen>
#include <QGuiApplication>
#include <Blend2d.h>
#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<PDFRenderError> 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<PDFRenderError>& pageErrors = compiledPage->getErrors();
if (!pageErrors.empty() || !drawInterfaceErrors.empty())
{
QList<PDFRenderError> 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;

View File

@ -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)

View File

@ -22,6 +22,7 @@
#include "pdfannotation.h"
#include "pdfwidgetannotation.h"
#include "pdfwidgetformmanager.h"
#include "pdfblpainter.h"
#include <QPainter>
#include <QGridLayout>
@ -30,8 +31,6 @@
#include <QPixmapCache>
#include <QColorSpace>
#include <Blend2d.h>
#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;
}

View File

@ -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<PDFRenderError>& errors) const
{
BaseClass::drawPage(context, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
} // namespace pdf

View File

@ -57,13 +57,6 @@ public:
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void drawPage(BLContext& context,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
/// Returns tooltip generated from annotation
virtual QString getTooltip() const override { return m_tooltip; }

View File

@ -1197,7 +1197,7 @@ void PDFMagnifierTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
if (m_mousePos != mousePos)
{
m_mousePos = mousePos;
getProxy()->repaintNeeded();
Q_EMIT getProxy()->repaintNeeded();
}
}