From cfedca6f4ffa42fed7cd289259b7015d06e82d55 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Tue, 13 Apr 2021 20:40:33 +0200 Subject: [PATCH] Ink coverage calculator --- Pdf4QtLib/sources/pdftransparencyrenderer.cpp | 142 ++++++++++++++++++ Pdf4QtLib/sources/pdftransparencyrenderer.h | 46 ++++++ 2 files changed, 188 insertions(+) diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp index 7aa0a66..97aaffb 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp @@ -3848,4 +3848,146 @@ void PDFTransparencyRenderer::PDFTransparencySoftMask::makeOpaque() } } +PDFInkCoverageCalculator::PDFInkCoverageCalculator(const PDFDocument* document, + const PDFFontCache* fontCache, + const PDFCMSManager* cmsManager, + const PDFOptionalContentActivity* optionalContentActivity, + const PDFInkMapper* inkMapper, + PDFTransparencyRendererSettings settings) : + m_document(document), + m_fontCache(fontCache), + m_cmsManager(cmsManager), + m_optionalContentActivity(optionalContentActivity), + m_inkMapper(inkMapper), + m_settings(settings) +{ + Q_ASSERT(m_document); + Q_ASSERT(m_fontCache); + Q_ASSERT(m_cmsManager); + Q_ASSERT(m_optionalContentActivity); + Q_ASSERT(m_inkMapper); +} + +void PDFInkCoverageCalculator::perform(QSize size, const std::vector& pages) +{ + if (pages.empty()) + { + // Nothing to do + return; + } + + auto calculatePageCoverage = [this, size](PDFInteger pageIndex) + { + if (pageIndex >= PDFInteger(m_document->getCatalog()->getPageCount())) + { + return; + } + + const PDFPage* page = m_document->getCatalog()->getPage(pageIndex); + if (!page) + { + return; + } + + QRectF pageRect = page->getRotatedMediaBox(); + QSizeF pageSize = pageRect.size(); + pageSize.scale(size.width(), size.height(), Qt::KeepAspectRatio); + QSize imageSize = pageSize.toSize(); + + if (!imageSize.isValid()) + { + return; + } + + pdf::PDFTransparencyRendererSettings settings; + settings.flags.setFlag(PDFTransparencyRendererSettings::SaveOriginalProcessImage, true); + + // Jakub Melka: debug is very slow, use multithreading +#ifdef QT_DEBUG + settings.flags.setFlag(PDFTransparencyRendererSettings::MultithreadedPathSampler, true); +#endif + + settings.flags.setFlag(PDFTransparencyRendererSettings::ActiveColorMask, false); + settings.flags.setFlag(PDFTransparencyRendererSettings::SeparationSimulation, true); + settings.activeColorMask = PDFPixelFormat::getAllColorsMask(); + + QMatrix pagePointToDevicePoint = pdf::PDFRenderer::createPagePointToDevicePointMatrix(page, QRect(QPoint(0, 0), imageSize)); + pdf::PDFCMSPointer cms = m_cmsManager->getCurrentCMS(); + pdf::PDFTransparencyRenderer renderer(page, m_document, m_fontCache, cms.data(), m_optionalContentActivity, + m_inkMapper, settings, pagePointToDevicePoint); + + renderer.beginPaint(imageSize); + renderer.processContents(); + renderer.endPaint(); + + PDFFloatBitmapWithColorSpace originalProcessImage = renderer.getOriginalProcessBitmap(); + QSizeF pageSizeMM = page->getRotatedMediaBoxMM().size(); + + pdf::PDFPixelFormat pixelFormat = originalProcessImage.getPixelFormat(); + pdf::PDFColorComponent totalArea = pageSizeMM.width() * pageSizeMM.height(); + pdf::PDFColorComponent pixelArea = totalArea / pdf::PDFColorComponent(originalProcessImage.getWidth() * originalProcessImage.getHeight()); + + std::vector pageCoverage; + const uint8_t colorChannelCount = pixelFormat.getColorChannelCount(); + pageCoverage.resize(colorChannelCount, 0.0f); + + for (size_t y = 0; y < originalProcessImage.getHeight(); ++y) + { + for (size_t x = 0; x < originalProcessImage.getWidth(); ++x) + { + const pdf::PDFColorBuffer buffer = originalProcessImage.getPixel(x, y); + const pdf::PDFColorComponent alpha = pixelFormat.hasOpacityChannel() ? buffer[pixelFormat.getOpacityChannelIndex()] : 1.0f; + + for (uint8_t i = 0; i < colorChannelCount; ++i) + { + pageCoverage[i] += buffer[i] * alpha; + } + } + } + + std::vector pageRatioCoverage = pageCoverage; + for (uint8_t i = 0; i < colorChannelCount; ++i) + { + pageCoverage[i] *= pixelArea; + pageRatioCoverage[i] *= pixelArea / totalArea; + } + + std::vector separations = m_inkMapper->getSeparations(pixelFormat.getProcessColorChannelCount()); + Q_ASSERT(pixelFormat.getColorChannelCount() == separations.size()); + + std::vector results; + results.reserve(separations.size()); + + for (size_t i = 0; i < separations.size(); ++i) + { + const PDFInkMapper::ColorInfo& colorInfo = separations[i]; + + InkCoverageChannelInfo info; + info.color = colorInfo.color; + info.name = colorInfo.name; + info.textName = colorInfo.textName; + info.isSpot = colorInfo.isSpot; + info.coveredArea = pageCoverage[i]; + results.emplace_back(qMove(info)); + } + + QMutexLocker lock(&m_mutex); + m_inkCoverageResults[pageIndex] = qMove(results); + }; + + PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pages.begin(), pages.end(), calculatePageCoverage); +} + +const std::vector* PDFInkCoverageCalculator::getInkCoverage(PDFInteger pageIndex) const +{ + auto it = m_inkCoverageResults.find(pageIndex); + if (it != m_inkCoverageResults.end()) + { + return &it->second; + } + + static const std::vector dummy; + return &dummy; +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.h b/Pdf4QtLib/sources/pdftransparencyrenderer.h index 113f831..904ec00 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.h +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.h @@ -924,6 +924,52 @@ private: PDFFloatBitmapWithColorSpace m_originalProcessBitmap; }; +/// Ink coverage calculator. Calculates ink coverage for a given +/// page range. Calculates ink coverage of both cmyk colors and spot colors. +class Pdf4QtLIBSHARED_EXPORT PDFInkCoverageCalculator +{ +public: + PDFInkCoverageCalculator(const PDFDocument* document, + const PDFFontCache* fontCache, + const PDFCMSManager* cmsManager, + const PDFOptionalContentActivity* optionalContentActivity, + const PDFInkMapper* inkMapper, + PDFTransparencyRendererSettings settings); + + struct InkCoverageChannelInfo + { + QByteArray name; + QString textName; + bool isSpot = true; + QColor color; + PDFColorComponent coveredArea = 0.0f; + PDFColorComponent ratio = 0.0f; + }; + + /// Perform ink coverage calculations on given pages. Results are stored + /// in this object. Page images are rendered using \p size resolution, + /// and in this resolution, ink coverage is calculated. + /// \param size Resolution size (for ink coverage calculation) + /// \param pages Page indices + void perform(QSize size, const std::vector& pages); + + /// Clears calculated ink coverage for all pages. If ink coverage is not + /// calculated for given page index, empty vector is returned. + /// \param pageIndex Page index + const std::vector* getInkCoverage(PDFInteger pageIndex) const; + +private: + const PDFDocument* m_document; + const PDFFontCache* m_fontCache; + const PDFCMSManager* m_cmsManager; + const PDFOptionalContentActivity* m_optionalContentActivity; + const PDFInkMapper* m_inkMapper; + PDFTransparencyRendererSettings m_settings; + + QMutex m_mutex; + std::map> m_inkCoverageResults; +}; + } // namespace pdf #endif // PDFTRANSPARENCYRENDERER_H