diff --git a/Pdf4QtLib/sources/pdfcms.cpp b/Pdf4QtLib/sources/pdfcms.cpp index ce2a55f..2620cd1 100644 --- a/Pdf4QtLib/sources/pdfcms.cpp +++ b/Pdf4QtLib/sources/pdfcms.cpp @@ -26,6 +26,7 @@ #pragma warning(disable:5033) #define CMS_NO_REGISTER_KEYWORD #include +#include #pragma warning(pop) #include @@ -56,6 +57,14 @@ public: private: void init(); + static int installCmsPlugins(); + + static cmsBool optimizePipeline(cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + enum Profile { Output, @@ -415,6 +424,9 @@ PDFLittleCMS::PDFLittleCMS(const PDFCMSManager* manager, const PDFCMSSettings& s m_paperColor(Qt::white), m_profiles() { + static const int installed = installCmsPlugins(); + Q_UNUSED(installed); + init(); } @@ -700,6 +712,145 @@ void PDFLittleCMS::init() m_transformationCache.reserve(64); } +int PDFLittleCMS::installCmsPlugins() +{ + static cmsPluginOptimization optimizationPlugin = { }; + optimizationPlugin.base.Magic = cmsPluginMagicNumber; + optimizationPlugin.base.Type = cmsPluginOptimizationSig; + optimizationPlugin.base.Next = nullptr; + optimizationPlugin.base.ExpectedVersion = LCMS_VERSION; + optimizationPlugin.OptimizePtr = &PDFLittleCMS::optimizePipeline; + + cmsPlugin(&optimizationPlugin); + + return 0; +} + +cmsBool PDFLittleCMS::optimizePipeline(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + if (*dwFlags & cmsFLAGS_HIGHRESPRECALC) + { + // Do not optimize + return FALSE; + } + + Q_UNUSED(Intent); + + // We will find, if we can optimize... + bool shouldOptimize = false; + for (auto stage = cmsPipelineGetPtrToFirstStage(*Lut); stage; stage = cmsStageNext(stage)) + { + if (cmsStageType(stage) == cmsSigCurveSetElemType) + { + _cmsStageToneCurvesData* data = reinterpret_cast<_cmsStageToneCurvesData*>(cmsStageData(stage)); + for (cmsUInt32Number i = 0; i < data->nCurves; ++i) + { + const cmsToneCurve* curve = data->TheCurves[i]; + const cmsInt32Number type = cmsGetToneCurveParametricType(curve); + + if (type != 0 && !cmsIsToneCurveMultisegment(curve)) + { + shouldOptimize = true; + } + } + } + } + + if (shouldOptimize) + { + cmsContext contextId = cmsGetPipelineContextID(*Lut); + cmsPipeline* pipeline = cmsPipelineAlloc(contextId, T_CHANNELS(*InputFormat), T_CHANNELS(*OutputFormat)); + if (!pipeline) + { + return FALSE; + } + + for (auto stage = cmsPipelineGetPtrToFirstStage(*Lut); stage; stage = cmsStageNext(stage)) + { + if (cmsStageType(stage) == cmsSigCurveSetElemType) + { + _cmsStageToneCurvesData* data = reinterpret_cast<_cmsStageToneCurvesData*>(cmsStageData(stage)); + std::vector curves(data->nCurves, nullptr); + + for (cmsUInt32Number i = 0; i < data->nCurves; ++i) + { + const cmsToneCurve* curve = data->TheCurves[i]; + const cmsInt32Number type = cmsGetToneCurveParametricType(curve); + + if (type != 0 && !cmsIsToneCurveMultisegment(curve)) + { + std::array segments = { }; + + const cmsFloat64Number* params = cmsGetToneCurveParams(curve); + + cmsCurveSegment& s1 = segments[0]; + cmsCurveSegment& s2 = segments[1]; + cmsCurveSegment& s3 = segments[2]; + + const cmsFloat32Number eps = cmsFloat32Number(1e-5); + const cmsFloat32Number low = 0.0f - eps; + const cmsFloat32Number high = 1.0f + eps; + + s1.Type = type; + s1.nGridPoints = 0; + s1.SampledPoints = nullptr; + std::copy(params, params + (sizeof(s1.Params) / sizeof(*s1.Params)), s1.Params); + s1.x0 = std::numeric_limits::min(); + s1.x1 = low; + + const cmsUInt32Number gridPoints = 1024; + const cmsFloat32Number factor = 1.0f / cmsFloat32Number(gridPoints - 1); + + s2.Type = 0; + s2.nGridPoints = gridPoints; + s2.SampledPoints = static_cast(_cmsCalloc(contextId, gridPoints, sizeof(*s2.SampledPoints))); + std::copy(params, params + (sizeof(s2.Params) / sizeof(*s2.Params)), s2.Params); + s2.x0 = low; + s2.x1 = high; + + for (cmsUInt32Number i = 0; i < gridPoints; ++i) + { + const cmsFloat32Number x = i * factor; + s2.SampledPoints[i] = cmsEvalToneCurveFloat(curve, x); + } + + s3.Type = type; + s3.nGridPoints = 0; + s3.SampledPoints = nullptr; + std::copy(params, params + (sizeof(s3.Params) / sizeof(*s3.Params)), s3.Params); + s3.x0 = high; + s3.x1 = std::numeric_limits::max(); + + curves[i] = cmsBuildSegmentedToneCurve(contextId, cmsUInt32Number(segments.size()), segments.data()); + + _cmsFree(contextId, s2.SampledPoints); + } + else + { + curves[i] = cmsDupToneCurve(curve); + } + } + + cmsStageAllocToneCurves(contextId, cmsFloat32Number(curves.size()), curves.data()); + + for (cmsToneCurve* curve : curves) + { + cmsFreeToneCurve(curve); + } + } + else + { + cmsPipelineInsertStage(pipeline, cmsAT_END, cmsStageDup(stage)); + } + } + + cmsPipelineFree(*Lut); + *Lut = pipeline; + } + + return FALSE; +} + bool PDFLittleCMS::isSoftProofing() const { return (m_settings.isSoftProofing || m_settings.isGamutChecking) && m_profiles[SoftProofing]; diff --git a/Pdf4QtViewer/pdfrendertoimagesdialog.cpp b/Pdf4QtViewer/pdfrendertoimagesdialog.cpp index 32ae401..91de2ba 100644 --- a/Pdf4QtViewer/pdfrendertoimagesdialog.cpp +++ b/Pdf4QtViewer/pdfrendertoimagesdialog.cpp @@ -49,6 +49,7 @@ PDFRenderToImagesDialog::PDFRenderToImagesDialog(const pdf::PDFDocument* documen ui->setupUi(this); qRegisterMetaType("PDFRenderError"); + qRegisterMetaType("PDFInteger"); // Load image formats for (const QByteArray& format : m_imageWriterSettings.getFormats()) @@ -245,7 +246,17 @@ void PDFRenderToImagesDialog::onProgressiveScanWriteChanged(bool value) void PDFRenderToImagesDialog::onRenderError(pdf::PDFInteger pageIndex, pdf::PDFRenderError error) { - ui->progressMessagesEdit->setPlainText(QString("%1\n%2").arg(ui->progressMessagesEdit->toPlainText()).arg(tr("Page %1: %2").arg(pageIndex + 1).arg(error.message))); + QString text; + + if (pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX) + { + text = tr("%1\nPage %2: %3").arg(ui->progressMessagesEdit->toPlainText(), QString::number(pageIndex + 1), error.message); + } + else + { + text = QString("%1\n%2").arg(ui->progressMessagesEdit->toPlainText(), error.message); + } + ui->progressMessagesEdit->setPlainText(text); } void PDFRenderToImagesDialog::onRenderingFinished()