diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp index 609d4a6..1efafde 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp @@ -90,6 +90,23 @@ PDFColorComponent PDFFloatBitmap::getPixelInkCoverage(size_t x, size_t y) const return inkCoverage; } +PDFFloatBitmap PDFFloatBitmap::getInkCoverageBitmap() const +{ + PDFFloatBitmap result(getWidth(), getHeight(), PDFPixelFormat::createFormat(1, 0, false, true, false)); + + for (size_t y = 0; y < getHeight(); ++y) + { + for (size_t x = 0; x < getWidth(); ++x) + { + PDFColorComponent coverage = getPixelInkCoverage(x, y); + PDFColorBuffer targetProcessColorBuffer = result.getPixel(x, y); + targetProcessColorBuffer[0] = coverage; + } + } + + return result; +} + const PDFColorComponent* PDFFloatBitmap::begin() const { return m_data.data(); diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.h b/Pdf4QtLib/sources/pdftransparencyrenderer.h index aa8d4e9..113f831 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.h +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.h @@ -160,6 +160,10 @@ public: /// Returns ink coverage PDFColorComponent getPixelInkCoverage(size_t x, size_t y) const; + /// Returns ink coverage bitmap. Bitmap consists of one color channel, + /// which consists of ink coverage. + PDFFloatBitmap getInkCoverageBitmap() const; + const PDFColorComponent* begin() const; const PDFColorComponent* end() const; diff --git a/Pdf4QtLib/sources/pdfutils.cpp b/Pdf4QtLib/sources/pdfutils.cpp index 73f30ad..89f8795 100644 --- a/Pdf4QtLib/sources/pdfutils.cpp +++ b/Pdf4QtLib/sources/pdfutils.cpp @@ -518,4 +518,43 @@ QString PDFSysUtils::getUserName() return userName; } +PDFColorScale::PDFColorScale(PDFReal min, PDFReal max) : + m_min(min), + m_max(max) +{ + m_colorScales = { + Qt::blue, + Qt::cyan, + Qt::green, + Qt::yellow, + Qt::red + }; +} + +QColor PDFColorScale::map(PDFReal value) const +{ + PDFReal correctedValue = qBound(m_min, value, m_max); + PDFReal intervalValue = interpolate(correctedValue, m_min, m_max, 0.0, PDFReal(m_colorScales.size() - 1)); + PDFReal indexValue = qFloor(intervalValue); + int index = indexValue; + PDFReal fractionValue = intervalValue - index; + + if (index == int(m_colorScales.size()) - 1) + { + --index; + fractionValue = 1.0; + } + + Q_ASSERT(index + 1 < m_colorScales.size()); + + const QColor& leftValue = m_colorScales[index]; + const QColor& rightValue = m_colorScales[index + 1]; + + qreal r = (1.0 - fractionValue) * leftValue.redF() + fractionValue * rightValue.redF(); + qreal g = (1.0 - fractionValue) * leftValue.greenF() + fractionValue * rightValue.greenF(); + qreal b = (1.0 - fractionValue) * leftValue.blueF() + fractionValue * rightValue.blueF(); + + return QColor::fromRgbF(r, g, b); +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfutils.h b/Pdf4QtLib/sources/pdfutils.h index a55347a..299c6c4 100644 --- a/Pdf4QtLib/sources/pdfutils.h +++ b/Pdf4QtLib/sources/pdfutils.h @@ -806,6 +806,21 @@ QDataStream& operator<<(QDataStream& stream, const std::set& set) return stream; } +/// Color scale represents hot-to-cold color scale. It maps value +/// to the color from blue trough green to red. +class Pdf4QtLIBSHARED_EXPORT PDFColorScale +{ +public: + explicit PDFColorScale(PDFReal min, PDFReal max); + + QColor map(PDFReal value) const; + +private: + std::vector m_colorScales; + PDFReal m_min; + PDFReal m_max; +}; + } // namespace pdf #endif // PDFUTILS_H diff --git a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.cpp b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.cpp index 4953d7b..97ecd04 100644 --- a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.cpp +++ b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.cpp @@ -57,6 +57,7 @@ void OutputPreviewWidget::clear() m_inkCoverageMM.dirty(); m_alarmCoverageImage.dirty(); m_alarmRichBlackImage.dirty(); + m_inkCoverageImage.dirty(); update(); } @@ -78,6 +79,7 @@ void OutputPreviewWidget::setPageImage(QImage image, pdf::PDFFloatBitmapWithColo m_inkCoverageMM.dirty(); m_alarmCoverageImage.dirty(); m_alarmRichBlackImage.dirty(); + m_inkCoverageImage.dirty(); buildInfoBoxItems(); update(); @@ -135,7 +137,15 @@ void OutputPreviewWidget::paintEvent(QPaintEvent* event) } case InkCoverage: + { + const InkCoverageInfo& image = getInkCoverageInfo(); + if (!image.image.isNull()) + { + painter.translate(0, (pageImageRect.height() - image.image.height()) / 2); + painter.drawImage(pageImageRect.topLeft(), image.image); + } break; + } default: Q_ASSERT(false); @@ -490,6 +500,11 @@ const OutputPreviewWidget::AlarmImageInfo& OutputPreviewWidget::getAlarmRichBlac return m_alarmRichBlackImage.get(this, &OutputPreviewWidget::getAlarmRichBlackImageImpl); } +const OutputPreviewWidget::InkCoverageInfo& OutputPreviewWidget::getInkCoverageInfo() const +{ + return m_inkCoverageImage.get(this, &OutputPreviewWidget::getInkCoverageInfoImpl); +} + std::vector OutputPreviewWidget::getInkCoverageImpl() const { std::vector result; @@ -611,6 +626,47 @@ OutputPreviewWidget::AlarmImageInfo OutputPreviewWidget::getAlarmRichBlackImageI return alarmImage; } +OutputPreviewWidget::InkCoverageInfo OutputPreviewWidget::getInkCoverageInfoImpl() const +{ + InkCoverageInfo coverageInfo; + coverageInfo.minValue = 0.0f; + coverageInfo.maxValue = 1.0f; + + pdf::PDFFloatBitmap inkCoverageBitmap = m_originalProcessBitmap.getInkCoverageBitmap(); + + int width = int(inkCoverageBitmap.getWidth()); + int height = int(inkCoverageBitmap.getHeight()); + + if (width > 0 && height > 0) + { + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + pdf::PDFColorBuffer buffer = inkCoverageBitmap.getPixel(x, y); + const pdf::PDFColorComponent coverage = buffer[0]; + coverageInfo.maxValue = qMax(coverage, coverageInfo.maxValue); + } + } + + pdf::PDFColorScale colorScale(coverageInfo.minValue, coverageInfo.maxValue); + coverageInfo.image = QImage(width, height, QImage::Format_RGBX8888); + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + pdf::PDFColorBuffer buffer = inkCoverageBitmap.getPixel(x, y); + const pdf::PDFColorComponent coverage = buffer[0]; + + coverageInfo.image.setPixelColor(x, y, colorScale.map(coverage)); + } + } + } + + return coverageInfo; +} + pdf::PDFColorComponent OutputPreviewWidget::getRichBlackLimit() const { return m_richBlackLimit; diff --git a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.h b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.h index de29236..72279e2 100644 --- a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.h +++ b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewwidget.h @@ -95,13 +95,22 @@ private: pdf::PDFColorComponent areaInvalid = 0.0f; }; + struct InkCoverageInfo + { + QImage image; + pdf::PDFColorComponent minValue = 0.0f; + pdf::PDFColorComponent maxValue = 0.0f; + }; + const std::vector& getInkCoverage() const; const AlarmImageInfo& getAlarmCoverageImage() const; const AlarmImageInfo& getAlarmRichBlackImage() const; + const InkCoverageInfo& getInkCoverageInfo() const; std::vector getInkCoverageImpl() const; AlarmImageInfo getAlarmCoverageImageImpl() const; AlarmImageInfo getAlarmRichBlackImageImpl() const; + InkCoverageInfo getInkCoverageInfoImpl() const; enum InfoBoxStyle { @@ -139,6 +148,7 @@ private: mutable pdf::PDFCachedItem> m_inkCoverageMM; mutable pdf::PDFCachedItem m_alarmCoverageImage; mutable pdf::PDFCachedItem m_alarmRichBlackImage; + mutable pdf::PDFCachedItem m_inkCoverageImage; QImage m_pageImage; pdf::PDFFloatBitmapWithColorSpace m_originalProcessBitmap;