From 5bacb6a96d038857e0377e9f34082d2793fdb76d Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Thu, 8 Feb 2018 07:05:26 +0300 Subject: [PATCH 01/28] ~ => Replaced fix for showing the main window maximized for Windows. --- main.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index 35718d4c8..09d096fd0 100644 --- a/main.cpp +++ b/main.cpp @@ -75,12 +75,8 @@ int main(int argc, char** argv) { if (settings.value("mainWindow/maximized") == false) { main_wnd->show(); } else { -#ifdef _WIN32 - main_wnd->show(); - main_wnd->showMaximized(); - main_wnd->showNormal(); -#endif - main_wnd->showMaximized(); + // main_wnd->showMaximized(); // Doesn't work for Windows. + QTimer::singleShot(0, main_wnd, &QMainWindow::showMaximized); } if (!cli.projectFile().isEmpty()) { From d64f423975fc2a80f191c7d0ba06eca374068d23 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 9 Feb 2018 09:42:11 +0300 Subject: [PATCH 02/28] Added color adjustments settings for color segmenter. --- DefaultParamsDialog.cpp | 16 +- filters/output/BlackWhiteOptions.cpp | 106 +++++++-- filters/output/BlackWhiteOptions.h | 52 ++++- filters/output/OptionsWidget.cpp | 79 ++++++- filters/output/OptionsWidget.h | 6 + filters/output/OutputGenerator.cpp | 38 ++-- filters/output/OutputGenerator.h | 2 + filters/output/RenderParams.cpp | 2 +- filters/output/ui/OutputOptionsWidget.ui | 260 +++++++++++++++++----- imageproc/ColorSegmenter.cpp | 39 ++-- imageproc/ColorSegmenter.h | 17 +- ui/DefaultParamsDialog.ui | 271 ++++++++++++++++++----- 12 files changed, 708 insertions(+), 180 deletions(-) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 89be91a15..8886f09a2 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -277,8 +277,11 @@ void DefaultParamsDialog::updateOutputDisplay(const DefaultParams::OutputParams& fillingColorBox->findData(colorCommonOptions.getFillingColor()) ); - colorSegmentationCB->setChecked(blackWhiteOptions.isColorSegmentationEnabled()); - reduceNoiseSB->setValue(blackWhiteOptions.getSegmentationNoiseReduction()); + colorSegmentationCB->setChecked(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); + reduceNoiseSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getNoiseReduction()); + redAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getRedThresholdAdjustment()); + greenAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getGreenThresholdAdjustment()); + blueAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getBlueThresholdAdjustment()); posterizeCB->setChecked(colorCommonOptions.isPosterizeEnabled()); posterizeLevelSB->setValue(colorCommonOptions.getPosterizationLevel()); posterizeForceBwCB->setChecked(colorCommonOptions.isForceBlackAndWhite()); @@ -724,8 +727,13 @@ std::unique_ptr DefaultParamsDialog::buildParams() const { blackWhiteOptions.setWolfCoef(wolfCoef->value()); blackWhiteOptions.setWolfLowerBound(upperBound->value()); blackWhiteOptions.setWolfLowerBound(lowerBound->value()); - blackWhiteOptions.setColorSegmentationEnabled(colorSegmentationCB->isChecked()); - blackWhiteOptions.setSegmentationNoiseReduction(reduceNoiseSB->value()); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setEnabled(colorSegmentationCB->isChecked()); + segmenterOptions.setNoiseReduction(reduceNoiseSB->value()); + segmenterOptions.setRedThresholdAdjustment(redAdjustmentSB->value()); + segmenterOptions.setGreenThresholdAdjustment(greenAdjustmentSB->value()); + segmenterOptions.setBlueThresholdAdjustment(blueAdjustmentSB->value()); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); colorParams.setBlackWhiteOptions(blackWhiteOptions); SplittingOptions splittingOptions; diff --git a/filters/output/BlackWhiteOptions.cpp b/filters/output/BlackWhiteOptions.cpp index 42a8d8788..f0bb53aa9 100644 --- a/filters/output/BlackWhiteOptions.cpp +++ b/filters/output/BlackWhiteOptions.cpp @@ -18,6 +18,7 @@ #include "BlackWhiteOptions.h" #include +#include namespace output { BlackWhiteOptions::BlackWhiteOptions() @@ -30,9 +31,7 @@ namespace output { wolfLowerBound(1), wolfUpperBound(254), wolfCoef(0.3), - binarizationMethod(OTSU), - colorSegmentationEnabled(false), - segmentationNoiseReduction(12) { + binarizationMethod(OTSU) { } BlackWhiteOptions::BlackWhiteOptions(const QDomElement& el) @@ -46,8 +45,7 @@ namespace output { wolfUpperBound(el.attribute("wolfUpperBound").toInt()), wolfCoef(el.attribute("wolfCoef").toDouble()), binarizationMethod(parseBinarizationMethod(el.attribute("binarizationMethod"))), - colorSegmentationEnabled(el.attribute("colorSegmentationEnabled") == "1"), - segmentationNoiseReduction(el.attribute("segmentationNoiseReduction").toInt()) { + colorSegmenterOptions(el.namedItem("color-segmenter-options").toElement()) { } QDomElement BlackWhiteOptions::toXml(QDomDocument& doc, const QString& name) const { @@ -62,8 +60,7 @@ namespace output { el.setAttribute("wolfUpperBound", wolfUpperBound); el.setAttribute("wolfCoef", wolfCoef); el.setAttribute("binarizationMethod", formatBinarizationMethod(binarizationMethod)); - el.setAttribute("colorSegmentationEnabled", colorSegmentationEnabled ? "1" : "0"); - el.setAttribute("segmentationNoiseReduction", segmentationNoiseReduction); + el.appendChild(colorSegmenterOptions.toXml(doc, "color-segmenter-options")); return el; } @@ -79,8 +76,7 @@ namespace output { && (wolfUpperBound == other.wolfUpperBound) && (wolfCoef == other.wolfCoef) && (binarizationMethod == other.binarizationMethod) - && (colorSegmentationEnabled == other.colorSegmentationEnabled) - && (segmentationNoiseReduction == other.segmentationNoiseReduction); + && (colorSegmenterOptions == other.colorSegmenterOptions); } bool BlackWhiteOptions::operator!=(const BlackWhiteOptions& other) const { @@ -194,20 +190,96 @@ namespace output { m_normalizeIllumination = val; } - bool BlackWhiteOptions::isColorSegmentationEnabled() const { - return colorSegmentationEnabled; + const BlackWhiteOptions::ColorSegmenterOptions& BlackWhiteOptions::getColorSegmenterOptions() const { + return colorSegmenterOptions; } - void BlackWhiteOptions::setColorSegmentationEnabled(bool colorSegmentationEnabled) { - BlackWhiteOptions::colorSegmentationEnabled = colorSegmentationEnabled; + void + BlackWhiteOptions::setColorSegmenterOptions(const BlackWhiteOptions::ColorSegmenterOptions& colorSegmenterOptions) { + BlackWhiteOptions::colorSegmenterOptions = colorSegmenterOptions; } - int BlackWhiteOptions::getSegmentationNoiseReduction() const { - return segmentationNoiseReduction; +/*=============================== BlackWhiteOptions::ColorSegmenterOptions ==================================*/ + + BlackWhiteOptions::ColorSegmenterOptions::ColorSegmenterOptions() + : enabled(false), + noiseReduction(12), + redThresholdAdjustment(0), + greenThresholdAdjustment(0), + blueThresholdAdjustment(0) { + } + + BlackWhiteOptions::ColorSegmenterOptions::ColorSegmenterOptions(const QDomElement& el) + : enabled(el.attribute("enabled") == "1"), + noiseReduction(el.attribute("noiseReduction").toInt()), + redThresholdAdjustment(el.attribute("redThresholdAdjustment").toInt()), + greenThresholdAdjustment(el.attribute("greenThresholdAdjustment").toInt()), + blueThresholdAdjustment(el.attribute("blueThresholdAdjustment").toInt()) { + } + + QDomElement BlackWhiteOptions::ColorSegmenterOptions::toXml(QDomDocument& doc, const QString& name) const { + QDomElement el(doc.createElement(name)); + el.setAttribute("colorSegmentationEnabled", enabled ? "1" : "0"); + el.setAttribute("noiseReduction", noiseReduction); + el.setAttribute("redThresholdAdjustment", redThresholdAdjustment); + el.setAttribute("greenThresholdAdjustment", greenThresholdAdjustment); + el.setAttribute("blueThresholdAdjustment", blueThresholdAdjustment); + + return el; + } + + bool + BlackWhiteOptions::ColorSegmenterOptions::operator==(const BlackWhiteOptions::ColorSegmenterOptions& other) const { + return (enabled == other.enabled) + && (noiseReduction == other.noiseReduction) + && (redThresholdAdjustment == other.redThresholdAdjustment) + && (greenThresholdAdjustment == other.greenThresholdAdjustment) + && (blueThresholdAdjustment == other.blueThresholdAdjustment); + } + + bool + BlackWhiteOptions::ColorSegmenterOptions::operator!=(const BlackWhiteOptions::ColorSegmenterOptions& other) const { + return !(*this == other); + } + + bool BlackWhiteOptions::ColorSegmenterOptions::isEnabled() const { + return enabled; + } + + void BlackWhiteOptions::ColorSegmenterOptions::setEnabled(bool enabled) { + ColorSegmenterOptions::enabled = enabled; + } + + int BlackWhiteOptions::ColorSegmenterOptions::getNoiseReduction() const { + return noiseReduction; + } + + void BlackWhiteOptions::ColorSegmenterOptions::setNoiseReduction(int noiseReduction) { + ColorSegmenterOptions::noiseReduction = noiseReduction; + } + + int BlackWhiteOptions::ColorSegmenterOptions::getRedThresholdAdjustment() const { + return redThresholdAdjustment; + } + + void BlackWhiteOptions::ColorSegmenterOptions::setRedThresholdAdjustment(int redThresholdAdjustment) { + ColorSegmenterOptions::redThresholdAdjustment = redThresholdAdjustment; + } + + int BlackWhiteOptions::ColorSegmenterOptions::getGreenThresholdAdjustment() const { + return greenThresholdAdjustment; + } + + void BlackWhiteOptions::ColorSegmenterOptions::setGreenThresholdAdjustment(int greenThresholdAdjustment) { + ColorSegmenterOptions::greenThresholdAdjustment = greenThresholdAdjustment; + } + + int BlackWhiteOptions::ColorSegmenterOptions::getBlueThresholdAdjustment() const { + return blueThresholdAdjustment; } - void BlackWhiteOptions::setSegmentationNoiseReduction(int segmentationNoiseReduction) { - BlackWhiteOptions::segmentationNoiseReduction = segmentationNoiseReduction; + void BlackWhiteOptions::ColorSegmenterOptions::setBlueThresholdAdjustment(int blueThresholdAdjustment) { + ColorSegmenterOptions::blueThresholdAdjustment = blueThresholdAdjustment; } } // namespace output \ No newline at end of file diff --git a/filters/output/BlackWhiteOptions.h b/filters/output/BlackWhiteOptions.h index 792d1cc74..f1ad85624 100644 --- a/filters/output/BlackWhiteOptions.h +++ b/filters/output/BlackWhiteOptions.h @@ -31,6 +31,47 @@ namespace output { }; class BlackWhiteOptions { + public: + class ColorSegmenterOptions { + public: + ColorSegmenterOptions(); + + explicit ColorSegmenterOptions(const QDomElement& el); + + QDomElement toXml(QDomDocument& doc, const QString& name) const; + + bool operator==(const ColorSegmenterOptions& other) const; + + bool operator!=(const ColorSegmenterOptions& other) const; + + bool isEnabled() const; + + void setEnabled(bool enabled); + + int getNoiseReduction() const; + + void setNoiseReduction(int noiseReduction); + + int getRedThresholdAdjustment() const; + + void setRedThresholdAdjustment(int redThresholdAdjustment); + + int getGreenThresholdAdjustment() const; + + void setGreenThresholdAdjustment(int greenThresholdAdjustment); + + int getBlueThresholdAdjustment() const; + + void setBlueThresholdAdjustment(int blueThresholdAdjustment); + + private: + bool enabled; + int noiseReduction; + int redThresholdAdjustment; + int greenThresholdAdjustment; + int blueThresholdAdjustment; + }; + public: BlackWhiteOptions(); @@ -82,13 +123,9 @@ namespace output { void setBinarizationMethod(BinarizationMethod binarizationMethod); - bool isColorSegmentationEnabled() const; - - void setColorSegmentationEnabled(bool colorSegmentationEnabled); - - int getSegmentationNoiseReduction() const; + const ColorSegmenterOptions& getColorSegmenterOptions() const; - void setSegmentationNoiseReduction(int segmentationNoiseReduction); + void setColorSegmenterOptions(const ColorSegmenterOptions& colorSegmenterOptions); private: static BinarizationMethod parseBinarizationMethod(const QString& str); @@ -106,8 +143,7 @@ namespace output { int wolfUpperBound; double wolfCoef; BinarizationMethod binarizationMethod; - bool colorSegmentationEnabled; - int segmentationNoiseReduction; + ColorSegmenterOptions colorSegmenterOptions; }; } #endif // ifndef OUTPUT_BLACK_WHITE_OPTIONS_H_ diff --git a/filters/output/OptionsWidget.cpp b/filters/output/OptionsWidget.cpp index e7ed50593..681cdcbb4 100644 --- a/filters/output/OptionsWidget.cpp +++ b/filters/output/OptionsWidget.cpp @@ -620,17 +620,20 @@ namespace output { colorSegmentationCB->setVisible(threshold_options_visible); segmenterOptionsWidget->setVisible(threshold_options_visible); - segmenterOptionsWidget->setEnabled(blackWhiteOptions.isColorSegmentationEnabled()); + segmenterOptionsWidget->setEnabled(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); if (threshold_options_visible) { - posterizeCB->setEnabled(blackWhiteOptions.isColorSegmentationEnabled()); - posterizeOptionsWidget->setEnabled(blackWhiteOptions.isColorSegmentationEnabled() + posterizeCB->setEnabled(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); + posterizeOptionsWidget->setEnabled(blackWhiteOptions.getColorSegmenterOptions().isEnabled() && colorCommonOptions.isPosterizeEnabled()); } else { posterizeCB->setEnabled(true); posterizeOptionsWidget->setEnabled(colorCommonOptions.isPosterizeEnabled()); } - colorSegmentationCB->setChecked(blackWhiteOptions.isColorSegmentationEnabled()); - reduceNoiseSB->setValue(blackWhiteOptions.getSegmentationNoiseReduction()); + colorSegmentationCB->setChecked(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); + reduceNoiseSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getNoiseReduction()); + redAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getRedThresholdAdjustment()); + greenAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getGreenThresholdAdjustment()); + blueAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getBlueThresholdAdjustment()); posterizeCB->setChecked(colorCommonOptions.isPosterizeEnabled()); posterizeLevelSB->setValue(colorCommonOptions.getPosterizationLevel()); posterizeForceBwCB->setChecked(colorCommonOptions.isForceBlackAndWhite()); @@ -757,7 +760,9 @@ namespace output { void OptionsWidget::colorSegmentationToggled(bool checked) { BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); - blackWhiteOptions.setColorSegmentationEnabled(checked); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setEnabled(checked); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); m_colorParams.setBlackWhiteOptions(blackWhiteOptions); m_ptrSettings->setColorParams(m_pageId, m_colorParams); @@ -772,13 +777,48 @@ namespace output { void OptionsWidget::reduceNoiseChanged(int value) { BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); - blackWhiteOptions.setSegmentationNoiseReduction(value); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setNoiseReduction(value); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); m_colorParams.setBlackWhiteOptions(blackWhiteOptions); m_ptrSettings->setColorParams(m_pageId, m_colorParams); delayedReloadRequest.start(750); } + void OptionsWidget::redAdjustmentChanged(int value) { + BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setRedThresholdAdjustment(value); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); + m_colorParams.setBlackWhiteOptions(blackWhiteOptions); + m_ptrSettings->setColorParams(m_pageId, m_colorParams); + + delayedReloadRequest.start(750); + } + + void OptionsWidget::greenAdjustmentChanged(int value) { + BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setGreenThresholdAdjustment(value); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); + m_colorParams.setBlackWhiteOptions(blackWhiteOptions); + m_ptrSettings->setColorParams(m_pageId, m_colorParams); + + delayedReloadRequest.start(750); + } + + void OptionsWidget::blueAdjustmentChanged(int value) { + BlackWhiteOptions blackWhiteOptions = m_colorParams.blackWhiteOptions(); + BlackWhiteOptions::ColorSegmenterOptions segmenterOptions = blackWhiteOptions.getColorSegmenterOptions(); + segmenterOptions.setBlueThresholdAdjustment(value); + blackWhiteOptions.setColorSegmenterOptions(segmenterOptions); + m_colorParams.setBlackWhiteOptions(blackWhiteOptions); + m_ptrSettings->setColorParams(m_pageId, m_colorParams); + + delayedReloadRequest.start(750); + } + void OptionsWidget::posterizeToggled(bool checked) { ColorCommonOptions colorCommonOptions = m_colorParams.colorCommonOptions(); colorCommonOptions.setPosterizeEnabled(checked); @@ -874,6 +914,18 @@ namespace output { reduceNoiseSB, SIGNAL(valueChanged(int)), this, SLOT(reduceNoiseChanged(int)) ); + connect( + redAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(redAdjustmentChanged(int)) + ); + connect( + greenAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(greenAdjustmentChanged(int)) + ); + connect( + blueAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(blueAdjustmentChanged(int)) + ); connect( posterizeCB, SIGNAL(clicked(bool)), this, SLOT(posterizeToggled(bool)) @@ -1007,6 +1059,18 @@ namespace output { reduceNoiseSB, SIGNAL(valueChanged(int)), this, SLOT(reduceNoiseChanged(int)) ); + disconnect( + redAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(redAdjustmentChanged(int)) + ); + disconnect( + greenAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(greenAdjustmentChanged(int)) + ); + disconnect( + blueAdjustmentSB, SIGNAL(valueChanged(int)), + this, SLOT(blueAdjustmentChanged(int)) + ); disconnect( posterizeCB, SIGNAL(clicked(bool)), this, SLOT(posterizeToggled(bool)) @@ -1114,5 +1178,4 @@ namespace output { return m_depthPerception; } - } // namespace output \ No newline at end of file diff --git a/filters/output/OptionsWidget.h b/filters/output/OptionsWidget.h index f70635d48..ab97fef82 100644 --- a/filters/output/OptionsWidget.h +++ b/filters/output/OptionsWidget.h @@ -102,6 +102,12 @@ namespace output { void reduceNoiseChanged(int value); + void redAdjustmentChanged(int value); + + void greenAdjustmentChanged(int value); + + void blueAdjustmentChanged(int value); + void posterizeToggled(bool checked); void posterizeLevelChanged(int value); diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index ad5685da1..14862ba98 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -571,7 +571,7 @@ namespace output { }(); QColor outsideBackgroundColor = BackgroundColorCalculator::calcDominantBackgroundColor( - inputOrigImage.allGray() ? inputGrayImage : inputOrigImage, m_xform.resultingPreCropArea() + inputOrigImage.allGray() ? inputGrayImage : inputOrigImage, m_xform.resultingPreCropArea() ); const bool needNormalizeIllumination @@ -697,8 +697,7 @@ namespace output { drawOver(color_image, dst_rect, maybe_normalized, src_rect); maybe_normalized = QImage(); - const int noiseReduction = m_colorParams.blackWhiteOptions().getSegmentationNoiseReduction(); - segmented_image = ColorSegmenter(dst, color_image, m_dpi, noiseReduction).getImage(); + segmented_image = segmentImage(dst, color_image); dst.release(); } @@ -919,9 +918,7 @@ namespace output { { QImage maybe_normalized_content(maybe_normalized); applyMask(maybe_normalized_content, bw_mask); - const int noiseReduction = m_colorParams.blackWhiteOptions().getSegmentationNoiseReduction(); - segmented_image = ColorSegmenter( - bw_content, maybe_normalized_content, m_dpi, noiseReduction).getImage(); + segmented_image = segmentImage(bw_content, maybe_normalized_content); maybe_normalized_content = QImage(); if (dbg) { @@ -992,7 +989,8 @@ namespace output { } if (render_params.mixedOutput() && render_params.needBinarization()) { - applyFillZonesToMixedInPlace(dst, fill_zones, bw_content_mask_output, !render_params.needColorSegmentation()); + applyFillZonesToMixedInPlace(dst, fill_zones, bw_content_mask_output, + !render_params.needColorSegmentation()); } else { applyFillZonesInPlace(dst, fill_zones); } @@ -1632,10 +1630,7 @@ namespace output { return dewarped_bw_content.toQImage(); } else { - const int noiseReduction = m_colorParams.blackWhiteOptions().getSegmentationNoiseReduction(); - QImage segmented_image = ColorSegmenter( - dewarped_bw_content, dewarped, m_dpi, noiseReduction).getImage(); - + QImage segmented_image = segmentImage(dewarped_bw_content, dewarped); dewarped = QImage(); dewarped_bw_content.release(); @@ -1828,9 +1823,7 @@ namespace output { { QImage dewarped_content(dewarped); applyMask(dewarped_content, dewarped_bw_mask); - const int noiseReduction = m_colorParams.blackWhiteOptions().getSegmentationNoiseReduction(); - segmented_image = ColorSegmenter( - dewarped_bw_content, dewarped_content, m_dpi, noiseReduction).getImage(); + segmented_image = segmentImage(dewarped_bw_content, dewarped_content); dewarped_content = QImage(); if (dbg) { @@ -2840,4 +2833,21 @@ namespace output { combineImageColor(img, content, picture_mask); } } + + QImage OutputGenerator::segmentImage(const BinaryImage& image, const QImage& color_image) const { + const BlackWhiteOptions::ColorSegmenterOptions& segmenterOptions + = m_colorParams.blackWhiteOptions().getColorSegmenterOptions(); + if (!color_image.allGray()) { + return ColorSegmenter( + image, color_image, m_dpi, segmenterOptions.getNoiseReduction(), + segmenterOptions.getRedThresholdAdjustment(), + segmenterOptions.getGreenThresholdAdjustment(), + segmenterOptions.getBlueThresholdAdjustment() + ).getImage(); + } else { + return ColorSegmenter( + image, GrayImage(color_image), m_dpi, segmenterOptions.getNoiseReduction() + ).getImage(); + } + } } // namespace output \ No newline at end of file diff --git a/filters/output/OutputGenerator.h b/filters/output/OutputGenerator.h index b709e5d5f..cb0a45f1e 100644 --- a/filters/output/OutputGenerator.h +++ b/filters/output/OutputGenerator.h @@ -317,6 +317,8 @@ namespace output { const imageproc::BinaryImage& picture_mask, bool binary_mode) const; + QImage segmentImage(const BinaryImage& image, const QImage& color_image) const; + Dpi m_dpi; ColorParams m_colorParams; SplittingOptions m_splittingOptions; diff --git a/filters/output/RenderParams.cpp b/filters/output/RenderParams.cpp index a8d01b2e5..7bdceef49 100644 --- a/filters/output/RenderParams.cpp +++ b/filters/output/RenderParams.cpp @@ -50,7 +50,7 @@ namespace output { if (blackWhiteOptions.normalizeIllumination()) { m_mask |= NORMALIZE_ILLUMINATION; } - if (blackWhiteOptions.isColorSegmentationEnabled()) { + if (blackWhiteOptions.getColorSegmenterOptions().isEnabled()) { m_mask |= COLOR_SEGMENTATION; if (colorCommonOptions.isPosterizeEnabled()) { m_mask |= POSTERIZE; diff --git a/filters/output/ui/OutputOptionsWidget.ui b/filters/output/ui/OutputOptionsWidget.ui index 86c51d2e4..03526dd1a 100644 --- a/filters/output/ui/OutputOptionsWidget.ui +++ b/filters/output/ui/OutputOptionsWidget.ui @@ -9,7 +9,7 @@ 0 0 - 241 + 238 628 @@ -87,8 +87,8 @@ 0 0 - 226 - 1137 + 228 + 1165 @@ -543,7 +543,7 @@ - + 0 @@ -557,59 +557,207 @@ 0 - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 15 - 1 - - - - - - - - Reduce noise: - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + 0 - - 999 - - - 0 - - + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + R + + + 1 + + + 0 + + + + + + + Red component adjustment. Negative value means the segmenter will be more sensitive to red and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + G + + + 1 + + + 3 + + + + + + + Green component adjustment. Negative value means the segmenter will be more sensitive to green and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + B + + + 1 + + + 3 + + + + + + + Blue component adjustment. Negative value means the segmenter will be more sensitive to blue and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + - - - Qt::Horizontal - - - - 1 - 1 - - - + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + Reduce noise: + + + + + + + + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 0 + + + 999 + + + 0 + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + @@ -675,6 +823,9 @@ Lower value means lower count of colors in the output image, values between 2 and 6 inclusive guarantee an indexed image. + + true + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -847,6 +998,9 @@ + + true + 0 diff --git a/imageproc/ColorSegmenter.cpp b/imageproc/ColorSegmenter.cpp index c2053fb76..29b92d606 100644 --- a/imageproc/ColorSegmenter.cpp +++ b/imageproc/ColorSegmenter.cpp @@ -88,7 +88,10 @@ namespace imageproc { ColorSegmenter::ColorSegmenter(const BinaryImage& image, const QImage& originalImage, const Dpi& dpi, - const int noiseThreshold) + const int noiseThreshold, + const int redThresholdAdjustment, + const int greenThresholdAdjustment, + const int blueThresholdAdjustment) : settings(dpi, noiseThreshold) { if (image.size() != originalImage.size()) { throw std::invalid_argument( @@ -108,7 +111,7 @@ namespace imageproc { throw std::invalid_argument("Error: wrong image format."); } } - fromRgb(image, originalImage); + fromRgb(image, originalImage, redThresholdAdjustment, greenThresholdAdjustment, blueThresholdAdjustment); } ColorSegmenter::ColorSegmenter(const BinaryImage& image, @@ -167,34 +170,40 @@ namespace imageproc { return dst; } - void ColorSegmenter::fromRgb(const BinaryImage& image, const QImage& originalImage) { + void ColorSegmenter::fromRgb(const BinaryImage& image, + const QImage& originalImage, + const int redThresholdAdjustment, + const int greenThresholdAdjustment, + const int blueThresholdAdjustment) { this->originalImage = originalImage; - BinaryThreshold threshold = BinaryThreshold::otsuThreshold(originalImage); BinaryImage redComponent; { GrayImage redChannel = getRgbChannel(originalImage, RED_CHANNEL); - redComponent = BinaryImage(redChannel, threshold); + BinaryThreshold redThreshold = BinaryThreshold::otsuThreshold(redChannel); + redComponent = BinaryImage(redChannel, adjustThreshold(redThreshold, redThresholdAdjustment)); rasterOp>(redComponent, image); } BinaryImage greenComponent; { GrayImage greenChannel = getRgbChannel(originalImage, GREEN_CHANNEL); - greenComponent = BinaryImage(greenChannel, threshold); + BinaryThreshold greenThreshold = BinaryThreshold::otsuThreshold(greenChannel); + greenComponent = BinaryImage(greenChannel, adjustThreshold(greenThreshold, greenThresholdAdjustment)); rasterOp>(greenComponent, image); } BinaryImage blueComponent; { GrayImage blueChannel = getRgbChannel(originalImage, BLUE_CHANNEL); - blueComponent = BinaryImage(blueChannel, threshold); + BinaryThreshold blueThreshold = BinaryThreshold::otsuThreshold(blueChannel); + blueComponent = BinaryImage(blueChannel, adjustThreshold(blueThreshold, blueThresholdAdjustment)); rasterOp>(blueComponent, image); } BinaryImage yellowComponent(redComponent); rasterOp>(yellowComponent, greenComponent); BinaryImage magentaComponent(redComponent); rasterOp>(magentaComponent, blueComponent); - BinaryImage azureComponent(greenComponent); - rasterOp>(azureComponent, blueComponent); + BinaryImage cyanComponent(greenComponent); + rasterOp>(cyanComponent, blueComponent); BinaryImage blackComponent(blueComponent); rasterOp>(blackComponent, yellowComponent); @@ -204,19 +213,19 @@ namespace imageproc { rasterOp>(blueComponent, blackComponent); rasterOp>(yellowComponent, blackComponent); rasterOp>(magentaComponent, blackComponent); - rasterOp>(azureComponent, blackComponent); + rasterOp>(cyanComponent, blackComponent); rasterOp>(redComponent, yellowComponent); rasterOp>(redComponent, magentaComponent); rasterOp>(greenComponent, yellowComponent); - rasterOp>(greenComponent, azureComponent); + rasterOp>(greenComponent, cyanComponent); rasterOp>(blueComponent, magentaComponent); - rasterOp>(blueComponent, azureComponent); + rasterOp>(blueComponent, cyanComponent); segmentsMap = ConnectivityMap(blackComponent, CONN8); segmentsMap.addComponents(yellowComponent, CONN8); segmentsMap.addComponents(magentaComponent, CONN8); - segmentsMap.addComponents(azureComponent, CONN8); + segmentsMap.addComponents(cyanComponent, CONN8); segmentsMap.addComponents(redComponent, CONN8); segmentsMap.addComponents(greenComponent, CONN8); segmentsMap.addComponents(blueComponent, CONN8); @@ -406,4 +415,8 @@ namespace imageproc { return dst; } + inline BinaryThreshold ColorSegmenter::adjustThreshold(const BinaryThreshold threshold, const int adjustment) { + return qBound(1, int(threshold) + adjustment, 255); + } + } // namespace imageproc \ No newline at end of file diff --git a/imageproc/ColorSegmenter.h b/imageproc/ColorSegmenter.h index e64bf6382..b7e215e6e 100644 --- a/imageproc/ColorSegmenter.h +++ b/imageproc/ColorSegmenter.h @@ -3,6 +3,7 @@ #define SCANTAILOR_COLORSEGMENTER_H #include "ConnectivityMap.h" +#include "BinaryThreshold.h" #include class Dpi; @@ -44,7 +45,13 @@ namespace imageproc { }; public: - ColorSegmenter(const BinaryImage& image, const QImage& originalImage, const Dpi& dpi, int noiseThreshold); + ColorSegmenter(const BinaryImage& image, + const QImage& originalImage, + const Dpi& dpi, + int noiseThreshold, + int redThresholdAdjustment, + int greenThresholdAdjustment, + int blueThresholdAdjustment); ColorSegmenter(const BinaryImage& image, const GrayImage& originalImage, const Dpi& dpi, int noiseThreshold); @@ -56,7 +63,11 @@ namespace imageproc { void reduceNoise(); - void fromRgb(const BinaryImage& image, const QImage& originalImage); + void fromRgb(const BinaryImage& image, + const QImage& originalImage, + int redThresholdAdjustment, + int greenThresholdAdjustment, + int blueThresholdAdjustment); void fromGrayscale(const BinaryImage& image, const GrayImage& originalImage); @@ -64,6 +75,8 @@ namespace imageproc { QImage buildGrayImage() const; + BinaryThreshold adjustThreshold(BinaryThreshold threshold, int adjustment); + Settings settings; ConnectivityMap segmentsMap; QImage originalImage; diff --git a/ui/DefaultParamsDialog.ui b/ui/DefaultParamsDialog.ui index 3fdab2208..9f5e7d43b 100644 --- a/ui/DefaultParamsDialog.ui +++ b/ui/DefaultParamsDialog.ui @@ -1769,8 +1769,8 @@ 0 0 - 637 - 442 + 646 + 452 @@ -2024,7 +2024,7 @@ - + 0 @@ -2038,59 +2038,207 @@ 0 - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 15 - 1 - - - - - - - - Reduce noise: - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - 0 - - - 999 - - + + 0 - + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + R + + + 1 + + + 0 + + + + + + + Red component adjustment. Negative value means the segmenter will be more sensitive to red and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + G + + + 1 + + + 3 + + + + + + + Green component adjustment. Negative value means the segmenter will be more sensitive to green and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + B + + + 1 + + + 3 + + + + + + + Blue component adjustment. Negative value means the segmenter will be more sensitive to blue and vice versa for positive values. + + + false + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -99 + + + 99 + + + 0 + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + - - - Qt::Horizontal - - - - 1 - 1 - - - + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + Reduce noise: + + + + + + + + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 0 + + + 999 + + + 0 + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + @@ -2110,7 +2258,7 @@ - + 0 @@ -2124,12 +2272,12 @@ 0 - + 0 - + Qt::Horizontal @@ -2156,6 +2304,9 @@ Lower value means lower count of colors in the output image, values between 2 and 6 inclusive guarantee an indexed image. + + true + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -2168,7 +2319,7 @@ - + Qt::Horizontal @@ -2183,12 +2334,12 @@ - + 0 - + Qt::Horizontal @@ -2217,7 +2368,7 @@ - + Qt::Horizontal @@ -2237,7 +2388,7 @@ - + Qt::Horizontal From 69090a8a276377c3d16585375f571dca369da1ed Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 9 Feb 2018 04:45:58 +0300 Subject: [PATCH 03/28] Fixed: rectangular picture auto zones are incorrectly shifted. --- filters/output/OutputGenerator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index 14862ba98..a51b63b37 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -757,7 +757,8 @@ namespace output { bw_mask.rectangularizeAreas(areas, WHITE, m_pictureShapeOptions.getSensitivity()); QTransform xform1(m_xform.transform()); - xform1.translate(-normalize_illumination_rect.left(), -normalize_illumination_rect.top()); + xform1 *= QTransform().translate(-normalize_illumination_rect.x(), + -normalize_illumination_rect.y()); QTransform inv_xform(xform1.inverted()); for (auto i : areas) { @@ -1243,7 +1244,8 @@ namespace output { warped_bw_mask.rectangularizeAreas(areas, WHITE, m_pictureShapeOptions.getSensitivity()); QTransform xform1(m_xform.transform()); - xform1 *= QTransform().translate(-normalize_illumination_rect.x(), -normalize_illumination_rect.y()); + xform1 *= QTransform().translate(-normalize_illumination_rect.x(), + -normalize_illumination_rect.y()); QTransform inv_xform(xform1.inverted()); for (auto i : areas) { From fe823a06d5804499d5eebe3e766794e62a213a46 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 9 Feb 2018 09:51:03 +0300 Subject: [PATCH 04/28] ~ get rid of the `the returned value not used` warning. --- MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 286fbe3ec..59c1734be 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1319,7 +1319,7 @@ void MainWindow::filterResult(const BackgroundTaskPtr& task, const FilterResultP if (cmd.isEmpty()) { QApplication::beep(); } else { - std::system(cmd.toStdString().c_str()); + (void) std::system(cmd.toStdString().c_str()); } } From 75d01c62c96eed1a86677ff48a2ead9f4c47418a Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 9 Feb 2018 10:20:47 +0300 Subject: [PATCH 05/28] Support for any type of input image. --- filters/output/OutputGenerator.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index a51b63b37..ef244368e 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -566,6 +566,11 @@ namespace output { if (!input.isBlackOnWhite()) { result.invertPixels(); } + if (!result.allGray() + && (result.format() != QImage::Format_ARGB32) + && (result.format() != QImage::Format_RGB32)) { + result = result.convertToFormat(QImage::Format_RGB32); + } return result; }(); @@ -1119,6 +1124,11 @@ namespace output { if (!input.isBlackOnWhite()) { result.invertPixels(); } + if (!result.allGray() + && (result.format() != QImage::Format_ARGB32) + && (result.format() != QImage::Format_RGB32)) { + result = result.convertToFormat(QImage::Format_RGB32); + } return result; }(); From 0aaedc13657e3c89ba0305df869f3b0eb5118769 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sat, 10 Feb 2018 04:13:52 +0300 Subject: [PATCH 06/28] ~ fixed config and translation dirs paths. --- Application.cpp | 4 ++-- main.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Application.cpp b/Application.cpp index 2ee36ffc0..1e5da5652 100644 --- a/Application.cpp +++ b/Application.cpp @@ -90,9 +90,9 @@ void Application::initTranslations() { const QStringList language_file_filter("scantailor_*.qm"); for (const QString& path : translation_dirs) { - QDir dir(path); + QDir dir(QDir::cleanPath(applicationDirPath() + '/' + path)); if (dir.exists()) { - QStringList translationFileNames = QDir(path).entryList(language_file_filter); + QStringList translationFileNames = QDir(dir.path()).entryList(language_file_filter); for (const QString& fileName : translationFileNames) { QString locale(fileName); locale.truncate(locale.lastIndexOf('.')); diff --git a/main.cpp b/main.cpp index 09d096fd0..cc37c4f38 100644 --- a/main.cpp +++ b/main.cpp @@ -52,7 +52,7 @@ int main(int argc, char** argv) { JpegMetadataLoader::registerMyself(); QSettings::setDefaultFormat(QSettings::IniFormat); - QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, QDir::currentPath() + "/config"); + QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, app.applicationDirPath() + "/config"); QSettings settings; From b50d31e842cac00b6d155bb9a8e370f11dec4635 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sat, 10 Feb 2018 06:52:27 +0300 Subject: [PATCH 07/28] Default parameters don't more require processing to convert units. It caused a bug on margins stage. --- AbstractFilter.h | 5 ++--- MainWindow.cpp | 6 +++--- filters/deskew/Filter.cpp | 6 +++--- filters/deskew/Filter.h | 3 +-- filters/fix_orientation/Filter.cpp | 6 +++--- filters/fix_orientation/Filter.h | 3 +-- filters/output/Filter.cpp | 6 +++--- filters/output/Filter.h | 3 +-- filters/page_layout/Filter.cpp | 20 +++++++++++++++----- filters/page_layout/Filter.h | 3 +-- filters/page_layout/Task.cpp | 29 +---------------------------- filters/page_split/Filter.cpp | 6 +++--- filters/page_split/Filter.h | 3 +-- filters/select_content/Filter.cpp | 16 ++++++++++++---- filters/select_content/Filter.h | 3 +-- filters/select_content/Task.cpp | 23 ----------------------- 16 files changed, 51 insertions(+), 90 deletions(-) diff --git a/AbstractFilter.h b/AbstractFilter.h index ef47d7381..17b87a658 100644 --- a/AbstractFilter.h +++ b/AbstractFilter.h @@ -25,7 +25,7 @@ #include class FilterUiInterface; -class PageId; +class PageInfo; class ProjectReader; class ProjectWriter; class AbstractRelinker; @@ -69,8 +69,7 @@ class AbstractFilter : public ref_countable { virtual void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) = 0; - virtual void loadDefaultSettings(const PageId& page_id) { - }; + virtual void loadDefaultSettings(const PageInfo& page_info) = 0; }; diff --git a/MainWindow.cpp b/MainWindow.cpp index 59c1734be..94645fc1c 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1150,7 +1150,7 @@ void MainWindow::filterSelectionChanged(const QItemSelection& selected) { // load default settings for all the pages for (const PageInfo& pageInfo : m_ptrThumbSequence->toPageSequence()) { for (int i = 0; i < m_ptrStages->count(); i++) { - m_ptrStages->filterAt(i)->loadDefaultSettings(pageInfo.id()); + m_ptrStages->filterAt(i)->loadDefaultSettings(pageInfo); } } @@ -1212,7 +1212,7 @@ void MainWindow::startBatchProcessing() { PageInfo page(m_ptrThumbSequence->selectionLeader()); for (; !page.isNull(); page = m_ptrThumbSequence->nextPage(page.id())) { for (int i = 0; i < m_ptrStages->count(); i++) { - m_ptrStages->filterAt(i)->loadDefaultSettings(page.id()); + m_ptrStages->filterAt(i)->loadDefaultSettings(page); } m_ptrBatchQueue->addProcessingTask( page, createCompositeTask(page, m_curFilter, /*batch=*/ true, m_debug) @@ -1723,7 +1723,7 @@ void MainWindow::loadPageInteractive(const PageInfo& page) { } for (int i = 0; i < m_ptrStages->count(); i++) { - m_ptrStages->filterAt(i)->loadDefaultSettings(page.id()); + m_ptrStages->filterAt(i)->loadDefaultSettings(page); } if (!isBatchProcessingInProgress()) { diff --git a/filters/deskew/Filter.cpp b/filters/deskew/Filter.cpp index 47a6b4d3a..d24401df9 100644 --- a/filters/deskew/Filter.cpp +++ b/filters/deskew/Filter.cpp @@ -155,15 +155,15 @@ namespace deskew { ); } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->isParamsNull(page_id)) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->isParamsNull(page_info.id())) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); const DefaultParams::DeskewParams& deskewParams = defaultParams.getDeskewParams(); m_ptrSettings->setPageParams( - page_id, + page_info.id(), Params(deskewParams.getDeskewAngleDeg(), Dependencies(), deskewParams.getMode()) ); } diff --git a/filters/deskew/Filter.h b/filters/deskew/Filter.h index 165da29fb..567e844a5 100644 --- a/filters/deskew/Filter.h +++ b/filters/deskew/Filter.h @@ -27,7 +27,6 @@ #include "SafeDeletingQObjectPtr.h" #include "Settings.h" -class PageId; class QString; class PageSelectionAccessor; @@ -64,7 +63,7 @@ namespace deskew { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; intrusive_ptr createTask(const PageId& page_id, intrusive_ptr next_task, diff --git a/filters/fix_orientation/Filter.cpp b/filters/fix_orientation/Filter.cpp index 727a25dba..3bf7f9ab8 100644 --- a/filters/fix_orientation/Filter.cpp +++ b/filters/fix_orientation/Filter.cpp @@ -154,14 +154,14 @@ namespace fix_orientation { filter_el.appendChild(image_el); } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->isRotationNull(page_id.imageId())) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->isRotationNull(page_info.id().imageId())) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); const DefaultParams::FixOrientationParams& fixOrientationParams = defaultParams.getFixOrientationParams(); - m_ptrSettings->applyRotation(page_id.imageId(), fixOrientationParams.getImageRotation()); + m_ptrSettings->applyRotation(page_info.id().imageId(), fixOrientationParams.getImageRotation()); } OptionsWidget* Filter::optionsWidget() { diff --git a/filters/fix_orientation/Filter.h b/filters/fix_orientation/Filter.h index fdabeb0d9..5b58583cd 100644 --- a/filters/fix_orientation/Filter.h +++ b/filters/fix_orientation/Filter.h @@ -26,7 +26,6 @@ #include "intrusive_ptr.h" #include "SafeDeletingQObjectPtr.h" -class PageId; class ImageId; class PageSelectionAccessor; class QString; @@ -68,7 +67,7 @@ namespace fix_orientation { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; intrusive_ptr createTask(const PageId& page_id, intrusive_ptr next_task, diff --git a/filters/output/Filter.cpp b/filters/output/Filter.cpp index eb7c1f5a6..eb6f86bca 100644 --- a/filters/output/Filter.cpp +++ b/filters/output/Filter.cpp @@ -182,15 +182,15 @@ namespace output { ); } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->isParamsNull(page_id)) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->isParamsNull(page_info.id())) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); const DefaultParams::OutputParams& outputParams = defaultParams.getOutputParams(); m_ptrSettings->setParams( - page_id, + page_info.id(), Params(outputParams.getDpi(), outputParams.getColorParams(), outputParams.getSplittingOptions(), diff --git a/filters/output/Filter.h b/filters/output/Filter.h index feb220512..78df2bea5 100644 --- a/filters/output/Filter.h +++ b/filters/output/Filter.h @@ -29,7 +29,6 @@ #include "FillZonePropFactory.h" #include -class PageId; class PageSelectionAccessor; class ThumbnailPixmapCache; class OutputFileNameGenerator; @@ -61,7 +60,7 @@ namespace output { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; intrusive_ptr createTask(const PageId& page_id, intrusive_ptr thumbnail_cache, diff --git a/filters/page_layout/Filter.cpp b/filters/page_layout/Filter.cpp index ff0a2c496..8e70edaf5 100644 --- a/filters/page_layout/Filter.cpp +++ b/filters/page_layout/Filter.cpp @@ -39,6 +39,7 @@ #include "CommandLine.h" #include #include +#include namespace page_layout { Filter::Filter(intrusive_ptr pages, const PageSelectionAccessor& page_selection_accessor) @@ -210,17 +211,26 @@ namespace page_layout { ); } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->isParamsNull(page_id)) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->isParamsNull(page_info.id())) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); const DefaultParams::PageLayoutParams& pageLayoutParams = defaultParams.getPageLayoutParams(); - // we need to recalculate the margins later basing on metric units and dpi + const UnitsConverter unitsConverter(page_info.metadata().dpi()); + + const Margins& margins = pageLayoutParams.getHardMargins(); + double leftMargin = margins.left(); + double topMargin = margins.top(); + double rightMargin = margins.right(); + double bottomMargin = margins.bottom(); + unitsConverter.convert(leftMargin, topMargin, defaultParams.getUnits(), MILLIMETRES); + unitsConverter.convert(rightMargin, bottomMargin, defaultParams.getUnits(), MILLIMETRES); + m_ptrSettings->setPageParams( - page_id, - Params(Margins(-0.01, -0.01, -0.01, -0.01), + page_info.id(), + Params(Margins(leftMargin, topMargin, rightMargin, bottomMargin), QRectF(), QRectF(), QSizeF(), diff --git a/filters/page_layout/Filter.h b/filters/page_layout/Filter.h index ba14ea668..47309fc59 100644 --- a/filters/page_layout/Filter.h +++ b/filters/page_layout/Filter.h @@ -29,7 +29,6 @@ #include #include -class PageId; class ProjectPages; class PageSelectionAccessor; class ImageTransformation; @@ -76,7 +75,7 @@ namespace page_layout { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; void setContentBox(const PageId& page_id, const ImageTransformation& xform, const QRectF& content_rect); diff --git a/filters/page_layout/Task.cpp b/filters/page_layout/Task.cpp index 23e191a79..be53910bb 100644 --- a/filters/page_layout/Task.cpp +++ b/filters/page_layout/Task.cpp @@ -16,11 +16,8 @@ along with this program. If not, see . */ -#include -#include -#include - #include +#include #include "Task.h" #include "Filter.h" #include "OptionsWidget.h" @@ -86,8 +83,6 @@ namespace page_layout { const QRectF& content_rect) { status.throwIfCancelled(); - loadDefaultSettings(Dpm(data.origImage())); - const QSizeF content_size_mm( Utils::calcRectSizeMM(data.xform(), content_rect) ); @@ -142,28 +137,6 @@ namespace page_layout { } } // Task::process - void Task::loadDefaultSettings(const Dpi& dpi) { - if (m_ptrSettings->isParamsNull(m_pageId) - || (m_ptrSettings->getHardMarginsMM(m_pageId).top() != -0.01)) { - return; - } - - const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); - const DefaultParams::PageLayoutParams& pageLayoutParams = defaultParams.getPageLayoutParams(); - - UnitsConverter unitsConverter(dpi); - - const Margins& margins = pageLayoutParams.getHardMargins(); - double leftMargin = margins.left(); - double topMargin = margins.top(); - double rightMargin = margins.right(); - double bottomMargin = margins.bottom(); - unitsConverter.convert(leftMargin, topMargin, defaultParams.getUnits(), MILLIMETRES); - unitsConverter.convert(rightMargin, bottomMargin, defaultParams.getUnits(), MILLIMETRES); - - m_ptrSettings->setHardMarginsMM(m_pageId, Margins(leftMargin, topMargin, rightMargin, bottomMargin)); - } - /*============================ Task::UiUpdater ==========================*/ Task::UiUpdater::UiUpdater(intrusive_ptr filter, diff --git a/filters/page_split/Filter.cpp b/filters/page_split/Filter.cpp index 1b6f6c7b0..799603280 100644 --- a/filters/page_split/Filter.cpp +++ b/filters/page_split/Filter.cpp @@ -212,8 +212,8 @@ namespace page_split { m_selectedPageOrder = option; } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->getPageRecord(page_id.imageId()).isNull()) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->getPageRecord(page_info.id().imageId()).isNull()) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); @@ -221,7 +221,7 @@ namespace page_split { Settings::UpdateAction update; update.setLayoutType(pageSplitParams.getLayoutType()); - m_ptrSettings->updatePage(page_id.imageId(), update); + m_ptrSettings->updatePage(page_info.id().imageId(), update); } OptionsWidget* Filter::optionsWidget() { diff --git a/filters/page_split/Filter.h b/filters/page_split/Filter.h index 25d209780..dca123fa1 100644 --- a/filters/page_split/Filter.h +++ b/filters/page_split/Filter.h @@ -29,7 +29,6 @@ #include "PageOrderOption.h" #include -class PageId; class ImageId; class PageInfo; class ProjectPages; @@ -70,7 +69,7 @@ namespace page_split { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; intrusive_ptr createTask(const PageInfo& page_info, intrusive_ptr next_task, diff --git a/filters/select_content/Filter.cpp b/filters/select_content/Filter.cpp index d39a48ce9..762930d1c 100644 --- a/filters/select_content/Filter.cpp +++ b/filters/select_content/Filter.cpp @@ -33,6 +33,7 @@ #include "CommandLine.h" #include #include +#include namespace select_content { Filter::Filter(const PageSelectionAccessor& page_selection_accessor) @@ -192,18 +193,25 @@ namespace select_content { ); } - void Filter::loadDefaultSettings(const PageId& page_id) { - if (!m_ptrSettings->isParamsNull(page_id)) { + void Filter::loadDefaultSettings(const PageInfo& page_info) { + if (!m_ptrSettings->isParamsNull(page_info.id())) { return; } const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); const DefaultParams::SelectContentParams& selectContentParams = defaultParams.getSelectContentParams(); + const UnitsConverter unitsConverter(page_info.metadata().dpi()); + + const QSizeF& pageRectSize = selectContentParams.getPageRectSize(); + double pageRectWidth = pageRectSize.width(); + double pageRectHeight = pageRectSize.height(); + unitsConverter.convert(pageRectWidth, pageRectHeight, defaultParams.getUnits(), PIXELS); + m_ptrSettings->setPageParams( - page_id, + page_info.id(), Params(QRectF(), QSizeF(), - QRectF(), + QRectF(QPointF(0, 0), QSizeF(pageRectWidth, pageRectHeight)), Dependencies(), MODE_AUTO, selectContentParams.getPageDetectMode(), diff --git a/filters/select_content/Filter.h b/filters/select_content/Filter.h index d1e8160a4..7d4f3a0ad 100644 --- a/filters/select_content/Filter.h +++ b/filters/select_content/Filter.h @@ -30,7 +30,6 @@ #include #include -class PageId; class PageSelectionAccessor; class QString; @@ -74,7 +73,7 @@ namespace select_content { void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; - void loadDefaultSettings(const PageId& page_id) override; + void loadDefaultSettings(const PageInfo& page_info) override; intrusive_ptr createTask(const PageId& page_id, intrusive_ptr next_task, diff --git a/filters/select_content/Task.cpp b/filters/select_content/Task.cpp index a0626d4b7..eeb24a268 100644 --- a/filters/select_content/Task.cpp +++ b/filters/select_content/Task.cpp @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include "Dpm.h" namespace select_content { @@ -85,8 +83,6 @@ namespace select_content { FilterResultPtr Task::process(const TaskStatus& status, const FilterData& data) { status.throwIfCancelled(); - loadDefaultSettings(Dpm(data.origImage())); - const Dependencies deps(data.xform().resultingPreCropArea()); OptionsWidget::UiData ui_data; @@ -199,25 +195,6 @@ namespace select_content { } } // Task::process - void Task::loadDefaultSettings(const Dpi& dpi) { - std::unique_ptr params = m_ptrSettings->getPageParams(m_pageId); - if ((params == nullptr) || !params->pageRect().isNull()) { - return; - } - const DefaultParams defaultParams = DefaultParamsProvider::getInstance()->getParams(); - const DefaultParams::SelectContentParams& selectContentParams = defaultParams.getSelectContentParams(); - - UnitsConverter unitsConverter(dpi); - - const QSizeF& pageRectSize = selectContentParams.getPageRectSize(); - double pageRectWidth = pageRectSize.width(); - double pageRectHeight = pageRectSize.height(); - unitsConverter.convert(pageRectWidth, pageRectHeight, defaultParams.getUnits(), PIXELS); - - params->setPageRect(QRectF(QPointF(0, 0), QSizeF(pageRectWidth, pageRectHeight))); - m_ptrSettings->setPageParams(m_pageId, *params); - } - /*============================ Task::UiUpdater ==========================*/ Task::UiUpdater::UiUpdater(intrusive_ptr filter, From 833197022ee36abd63aa32dc6e50f8d5456cb427 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 11 Feb 2018 22:34:47 +0300 Subject: [PATCH 08/28] ~ status bar panel & units system code refactorings. * Dpi is no more stored in global object UnitsProvider, as it belongs to image / page info. * ImageInfoProvider is no more global object and is now part of ImageViewBase. * Status bar panel shows info of shown image in the main window but not the latest created in the program as it was before. Before it couldn't show the valid information if the main window has several image views in tabs. * Added fix dpi function for scaling transformations into image transform function. --- AbstractFilter.h | 2 +- CMakeLists.txt | 2 +- ImageViewBase.cpp | 32 +++++--- ImageViewBase.h | 7 ++ ImageViewInfoObserver.cpp | 11 --- ImageViewInfoObserver.h | 10 ++- ImageViewInfoProvider.cpp | 55 +++++++------ ImageViewInfoProvider.h | 29 ++++--- MainWindow.cpp | 18 ++-- MainWindow.h | 3 +- StatusBarPanel.cpp | 100 +++++++++++++++-------- StatusBarPanel.h | 17 ++++ UnitsObserver.cpp | 3 - UnitsObserver.h | 2 - UnitsProvider.cpp | 36 ++------ UnitsProvider.h | 13 +-- Utils.h | 12 +++ filters/deskew/Filter.cpp | 8 +- filters/deskew/Filter.h | 2 +- filters/deskew/Task.cpp | 2 - filters/fix_orientation/Filter.cpp | 6 +- filters/fix_orientation/Filter.h | 2 +- filters/fix_orientation/Task.cpp | 2 - filters/output/Filter.cpp | 4 +- filters/output/Filter.h | 2 +- filters/output/TabbedImageView.cpp | 25 +----- filters/output/TabbedImageView.h | 2 - filters/output/Task.cpp | 8 +- filters/page_layout/Filter.cpp | 8 +- filters/page_layout/Filter.h | 2 +- filters/page_layout/ImageView.cpp | 2 +- filters/page_layout/OptionsWidget.cpp | 19 +++-- filters/page_layout/OptionsWidget.h | 3 +- filters/page_layout/Task.cpp | 2 - filters/page_split/Filter.cpp | 8 +- filters/page_split/Filter.h | 2 +- filters/page_split/Task.cpp | 2 - filters/select_content/Filter.cpp | 4 +- filters/select_content/Filter.h | 2 +- filters/select_content/OptionsWidget.cpp | 10 +-- filters/select_content/OptionsWidget.h | 3 +- filters/select_content/Task.cpp | 2 - imageproc/GrayImage.cpp | 16 ++++ imageproc/GrayImage.h | 8 ++ imageproc/Transform.cpp | 26 ++++++ 45 files changed, 300 insertions(+), 234 deletions(-) delete mode 100644 ImageViewInfoObserver.cpp diff --git a/AbstractFilter.h b/AbstractFilter.h index 17b87a658..a08bf9f2e 100644 --- a/AbstractFilter.h +++ b/AbstractFilter.h @@ -60,7 +60,7 @@ class AbstractFilter : public ref_countable { virtual void performRelinking(const AbstractRelinker& relinker) = 0; - virtual void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) = 0; + virtual void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) = 0; virtual void updateStatistics() { } diff --git a/CMakeLists.txt b/CMakeLists.txt index 624e45a7d..ee65cf899 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -548,7 +548,7 @@ SET( filter_dc/ContentBoxCollector.h filter_dc/PageOrientationCollector.h ImageViewInfoProvider.cpp ImageViewInfoProvider.h - ImageViewInfoObserver.cpp ImageViewInfoObserver.h + ImageViewInfoObserver.h UnitsProvider.cpp UnitsProvider.h UnitsObserver.h UnitsObserver.cpp UnitsConverter.cpp UnitsConverter.h diff --git a/ImageViewBase.cpp b/ImageViewBase.cpp index 7dd332052..7d0a3581b 100644 --- a/ImageViewBase.cpp +++ b/ImageViewBase.cpp @@ -27,8 +27,8 @@ #include "imageproc/Transform.h" #include "OpenGLSupport.h" #include "ColorSchemeManager.h" -#include "ImageViewInfoProvider.h" #include "UnitsProvider.h" +#include "StatusBarPanel.h" #include #include #include @@ -36,6 +36,8 @@ #include #include #include +#include +#include using namespace imageproc; @@ -145,7 +147,8 @@ ImageViewBase::ImageViewBase(const QImage& image, m_transformChangeWatchersActive(0), m_ignoreScrollEvents(0), m_ignoreResizeEvents(0), - m_hqTransformEnabled(true) { + m_hqTransformEnabled(true), + m_infoProvider(Dpm(m_image)) { /* For some reason, the default viewport fills background with * a color different from QPalette::Window. Here we make it not * fill it at all, assuming QMainWindow will do that anyway @@ -197,13 +200,6 @@ ImageViewBase::ImageViewBase(const QImage& image, this, SLOT(initiateBuildingHqVersion()) ); - { - Dpi image_dpi = Dpm(m_image); - if (UnitsProvider::getInstance()->getDpi() != image_dpi) { - UnitsProvider::getInstance()->setDpi(image_dpi); - }; - } - setMouseTracking(true); m_cursorTrackerTimer.setSingleShot(true); m_cursorTrackerTimer.setInterval(150); // msec @@ -213,7 +209,7 @@ ImageViewBase::ImageViewBase(const QImage& image, if (!m_cursorPos.isNull()) { cursorPos = m_widgetToVirtual.map(m_cursorPos) - m_virtualImageCropArea.boundingRect().topLeft(); } - ImageViewInfoProvider::getInstance()->setMousePos(cursorPos); + m_infoProvider.setMousePos(cursorPos); } ); @@ -623,6 +619,16 @@ void ImageViewBase::leaveEvent(QEvent* event) { QAbstractScrollArea::leaveEvent(event); } +void ImageViewBase::showEvent(QShowEvent* event) { + QWidget::showEvent(event); + + if (auto* mainWindow = dynamic_cast(window())) { + if (auto* statusBarPanel = mainWindow->statusBar()->findChild()) { + statusBarPanel->setInfoProvider(&infoProvider()); + } + } +} + /** * Called when any of the transformations change. */ @@ -1050,7 +1056,11 @@ void ImageViewBase::updateCursorPos(const QPointF& pos) { } void ImageViewBase::updatePhysSize() { - ImageViewInfoProvider::getInstance()->setPhysSize(m_virtualImageCropArea.boundingRect().size()); + m_infoProvider.setPhysSize(m_virtualImageCropArea.boundingRect().size()); +} + +ImageViewInfoProvider& ImageViewBase::infoProvider() { + return m_infoProvider; } /*==================== ImageViewBase::HqTransformTask ======================*/ diff --git a/ImageViewBase.h b/ImageViewBase.h index 987c88649..5f0398843 100644 --- a/ImageViewBase.h +++ b/ImageViewBase.h @@ -24,6 +24,7 @@ #include "InteractionHandler.h" #include "InteractionState.h" #include "ImagePixmapUnion.h" +#include "ImageViewInfoProvider.h" #include #include #include @@ -261,6 +262,8 @@ Q_OBJECT static BackgroundExecutor& backgroundExecutor(); + ImageViewInfoProvider& infoProvider(); + protected: void paintEvent(QPaintEvent* event) override; @@ -284,6 +287,8 @@ Q_OBJECT void leaveEvent(QEvent* event) override; + void showEvent(QShowEvent *event) override; + /** * Returns the maximum viewport size (as if scrollbars are hidden) * reduced by margins. @@ -487,6 +492,8 @@ private slots: int m_ignoreResizeEvents; bool m_hqTransformEnabled; + + ImageViewInfoProvider m_infoProvider; }; diff --git a/ImageViewInfoObserver.cpp b/ImageViewInfoObserver.cpp deleted file mode 100644 index 0376e393a..000000000 --- a/ImageViewInfoObserver.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#include "ImageViewInfoObserver.h" -#include "ImageViewInfoProvider.h" - -ImageViewInfoObserver::ImageViewInfoObserver() { - ImageViewInfoProvider::getInstance()->attachObserver(this); -} - -ImageViewInfoObserver::~ImageViewInfoObserver() { - ImageViewInfoProvider::getInstance()->detachObserver(this); -} diff --git a/ImageViewInfoObserver.h b/ImageViewInfoObserver.h index 497ccc0c9..48f1d5614 100644 --- a/ImageViewInfoObserver.h +++ b/ImageViewInfoObserver.h @@ -6,15 +6,19 @@ #include #include +class Dpi; + class ImageViewInfoObserver { public: - ImageViewInfoObserver(); - - virtual ~ImageViewInfoObserver(); + virtual ~ImageViewInfoObserver() = default; virtual void updateMousePos(const QPointF& mousePos) = 0; virtual void updatePhysSize(const QSizeF& physSize) = 0; + + virtual void updateDpi(const Dpi& dpi) = 0; + + virtual void clearImageViewInfo() = 0; }; diff --git a/ImageViewInfoProvider.cpp b/ImageViewInfoProvider.cpp index 1f60c18b9..00238d715 100644 --- a/ImageViewInfoProvider.cpp +++ b/ImageViewInfoProvider.cpp @@ -1,58 +1,63 @@ #include +#include #include "ImageViewInfoProvider.h" +#include "Units.h" -std::unique_ptr ImageViewInfoProvider::instance = nullptr; +ImageViewInfoProvider::ImageViewInfoProvider(const Dpi& dpi) + : dpi(dpi) { +}; -ImageViewInfoProvider::ImageViewInfoProvider() - : physSize(QRectF().size()) { -} - -ImageViewInfoProvider* ImageViewInfoProvider::getInstance() { - if (instance == nullptr) { - instance.reset(new ImageViewInfoProvider()); +ImageViewInfoProvider::~ImageViewInfoProvider() { + for (ImageViewInfoObserver* observer : observers) { + observer->clearImageViewInfo(); } - - return instance.get(); } void ImageViewInfoProvider::attachObserver(ImageViewInfoObserver* observer) { + observer->updateDpi(dpi); + observer->updatePhysSize(physSize); + observer->updateMousePos(mousePos); + observers.push_back(observer); } void ImageViewInfoProvider::detachObserver(ImageViewInfoObserver* observer) { - auto it = std::find(observers.begin(), observers.end(), observer); - if (it != observers.end()) { - observers.erase(it); - } -} + observer->clearImageViewInfo(); -const QSizeF& ImageViewInfoProvider::getPhysSize() const { - return physSize; + observers.remove(observer); } void ImageViewInfoProvider::setPhysSize(const QSizeF& physSize) { ImageViewInfoProvider::physSize = physSize; - physSizeChanged(); -} - -const QPointF& ImageViewInfoProvider::getMousePos() const { - return mousePos; + physSizeChanged(physSize); } void ImageViewInfoProvider::setMousePos(const QPointF& mousePos) { ImageViewInfoProvider::mousePos = mousePos; - mousePosChanged(); + mousePosChanged(mousePos); } -void ImageViewInfoProvider::physSizeChanged() { +void ImageViewInfoProvider::physSizeChanged(const QSizeF& physSize) const { for (ImageViewInfoObserver* observer : observers) { observer->updatePhysSize(physSize); } } -void ImageViewInfoProvider::mousePosChanged() { +void ImageViewInfoProvider::mousePosChanged(const QPointF& mousePos) const { for (ImageViewInfoObserver* observer : observers) { observer->updateMousePos(mousePos); } } + +const Dpi& ImageViewInfoProvider::getDpi() const { + return dpi; +} + +const QPointF& ImageViewInfoProvider::getMousePos() const { + return mousePos; +} + +const QSizeF& ImageViewInfoProvider::getPhysSize() const { + return physSize; +} diff --git a/ImageViewInfoProvider.h b/ImageViewInfoProvider.h index 02bd888a4..97597aace 100644 --- a/ImageViewInfoProvider.h +++ b/ImageViewInfoProvider.h @@ -7,36 +7,41 @@ #include #include #include "ImageViewInfoObserver.h" +#include "Dpi.h" +#include "NonCopyable.h" class ImageViewInfoProvider { -private: - static std::unique_ptr instance; + DECLARE_NON_COPYABLE(ImageViewInfoProvider) +private: std::list observers; - QSizeF physSize; + Dpi dpi; QPointF mousePos; - - ImageViewInfoProvider(); + QSizeF physSize; public: - static ImageViewInfoProvider* getInstance(); + explicit ImageViewInfoProvider(const Dpi& dpi); + + ~ImageViewInfoProvider(); void attachObserver(ImageViewInfoObserver* observer); void detachObserver(ImageViewInfoObserver* observer); - const QSizeF& getPhysSize() const; - void setPhysSize(const QSizeF& physSize); + void setMousePos(const QPointF& mousePos); + + const Dpi& getDpi() const; + const QPointF& getMousePos() const; - void setMousePos(const QPointF& mousePos); + const QSizeF& getPhysSize() const; -protected: - void physSizeChanged(); +private: + void physSizeChanged(const QSizeF& physSize) const; - void mousePosChanged(); + void mousePosChanged(const QPointF& mousePos) const; }; diff --git a/MainWindow.cpp b/MainWindow.cpp index 94645fc1c..30c0f9787 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -74,7 +74,6 @@ #include "ui_BatchProcessingLowerPanel.h" #include "version.h" #include "Application.h" -#include "ImageViewInfoProvider.h" #include "UnitsProvider.h" #include "DefaultParamsDialog.h" #include @@ -787,7 +786,7 @@ void MainWindow::setOptionsWidget(FilterOptionsWidget* widget, const Ownership o void MainWindow::setImageWidget(QWidget* widget, const Ownership ownership, DebugImages* debug_images, - bool clearImageWidget) { + bool clear_image_widget) { if (isBatchProcessingInProgress() && (widget != m_ptrBatchProcessingWidget.get())) { if (ownership == TRANSFER_OWNERSHIP) { delete widget; @@ -797,16 +796,15 @@ void MainWindow::setImageWidget(QWidget* widget, } QWidget* current_widget = m_pImageFrameLayout->currentWidget(); - if (dynamic_cast(current_widget) != NULL) { - if (!clearImageWidget) { + if (dynamic_cast(current_widget) != nullptr) { + if (!clear_image_widget) { return; } } - bool current_widget_isImage = (dynamic_cast(current_widget) != NULL) - || (dynamic_cast(current_widget) != NULL); + bool current_widget_is_image = (Utils::castOrFindChild(current_widget) != nullptr); - if (clearImageWidget || !current_widget_isImage) { + if (clear_image_widget || !current_widget_is_image) { removeImageWidget(); } @@ -816,7 +814,7 @@ void MainWindow::setImageWidget(QWidget* widget, if (!debug_images || debug_images->empty()) { m_pImageFrameLayout->addWidget(widget); - if (!clearImageWidget && current_widget_isImage) { + if (!clear_image_widget && current_widget_is_image) { m_pImageFrameLayout->setCurrentWidget(widget); } } else { @@ -1671,8 +1669,6 @@ void MainWindow::updateMainArea() { } else if (isBatchProcessingInProgress()) { filterList->setBatchProcessingPossible(false); setImageWidget(m_ptrBatchProcessingWidget.get(), KEEP_OWNERSHIP); - ImageViewInfoProvider::getInstance()->setMousePos(QPointF()); - ImageViewInfoProvider::getInstance()->setPhysSize(QRectF().size()); } else { if (!(filterDockWidget->isEnabled() && thumbnailsDockWidget->isEnabled())) { filterDockWidget->setEnabled(true); @@ -1731,7 +1727,7 @@ void MainWindow::loadPageInteractive(const PageInfo& page) { m_ptrProcessingIndicationWidget->processingRestartedEffect(); } setImageWidget(m_ptrProcessingIndicationWidget.get(), KEEP_OWNERSHIP, 0, false); - m_ptrStages->filterAt(m_curFilter)->preUpdateUI(this, page.id()); + m_ptrStages->filterAt(m_curFilter)->preUpdateUI(this, page); } assert(m_ptrThumbnailCache); diff --git a/MainWindow.h b/MainWindow.h index 5d1088e84..96c576f2a 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -56,6 +56,7 @@ class PageSelectionAccessor; class FilterOptionsWidget; class ProcessingIndicationWidget; class ImageInfo; +class ImageViewBase; class PageInfo; class QStackedLayout; class WorkerThreadPool; @@ -211,7 +212,7 @@ private slots: void setImageWidget(QWidget* widget, Ownership ownership, DebugImages* debug_images = nullptr, - bool clearImageWidget = true) override; + bool clear_image_widget = true) override; intrusive_ptr> relinkingDialogRequester() override; diff --git a/StatusBarPanel.cpp b/StatusBarPanel.cpp index ba6c17e7c..0a1a730d1 100644 --- a/StatusBarPanel.cpp +++ b/StatusBarPanel.cpp @@ -13,10 +13,66 @@ StatusBarPanel::StatusBarPanel() { void StatusBarPanel::updateMousePos(const QPointF& mousePos) { const QMutexLocker locker(&mutex); - if (!mousePos.isNull()) { + StatusBarPanel::mousePos = mousePos; + mousePosChanged(); +} + +void StatusBarPanel::updatePhysSize(const QSizeF& physSize) { + const QMutexLocker locker(&mutex); + + StatusBarPanel::physSize = physSize; + physSizeChanged(); +} + +void StatusBarPanel::updateDpi(const Dpi& dpi) { + StatusBarPanel::dpi = dpi; +} + +void StatusBarPanel::clearImageViewInfo() { + infoProvider = nullptr; + updateMousePos(QPointF()); + updatePhysSize(QRectF().size()); + dpi = Dpi(); +} + +void StatusBarPanel::updatePage(int pageNumber, const PageId& pageId) { + ui.pageNoLabel->setText(tr("p. %1").arg(pageNumber)); + + QString pageFileInfo = QFileInfo(pageId.imageId().filePath()).baseName(); + if (pageFileInfo.size() > 15) { + pageFileInfo = "..." + pageFileInfo.right(13); + } + if (pageId.subPage() != PageId::SINGLE_PAGE) { + pageFileInfo = pageFileInfo.right(11) + ((pageId.subPage() == PageId::LEFT_PAGE) ? tr(" [L]") : tr(" [R]")); + } + + ui.pageInfoLine->setVisible(true); + ui.pageInfo->setText(pageFileInfo); +} + +void StatusBarPanel::clear() { + ui.mousePosLabel->clear(); + ui.physSizeLabel->clear(); + ui.pageNoLabel->clear(); + ui.pageInfo->clear(); + + ui.mousePosLine->setVisible(false); + ui.physSizeLine->setVisible(false); + ui.pageInfoLine->setVisible(false); +} + +void StatusBarPanel::updateUnits(Units) { + const QMutexLocker locker(&mutex); + + mousePosChanged(); + physSizeChanged(); +} + +void StatusBarPanel::mousePosChanged() { + if (!mousePos.isNull() && !dpi.isNull()) { double x = mousePos.x(); double y = mousePos.y(); - UnitsProvider::getInstance()->convertFrom(x, y, PIXELS); + UnitsProvider::getInstance()->convertFrom(x, y, PIXELS, dpi); switch (UnitsProvider::getInstance()->getUnits()) { case PIXELS: @@ -38,13 +94,11 @@ void StatusBarPanel::updateMousePos(const QPointF& mousePos) { } } -void StatusBarPanel::updatePhysSize(const QSizeF& physSize) { - const QMutexLocker locker(&mutex); - - if (!physSize.isNull()) { +void StatusBarPanel::physSizeChanged() { + if (!physSize.isNull() && !dpi.isNull()) { double width = physSize.width(); double height = physSize.height(); - UnitsProvider::getInstance()->convertFrom(width, height, PIXELS); + UnitsProvider::getInstance()->convertFrom(width, height, PIXELS, dpi); const Units units = UnitsProvider::getInstance()->getUnits(); switch (units) { @@ -74,33 +128,13 @@ void StatusBarPanel::updatePhysSize(const QSizeF& physSize) { } } -void StatusBarPanel::updatePage(int pageNumber, const PageId& pageId) { - ui.pageNoLabel->setText(tr("p. %1").arg(pageNumber)); - - QString pageFileInfo = QFileInfo(pageId.imageId().filePath()).baseName(); - if (pageFileInfo.size() > 15) { - pageFileInfo = "..." + pageFileInfo.right(13); +void StatusBarPanel::setInfoProvider(ImageViewInfoProvider* infoProvider) { + if (this->infoProvider) { + infoProvider->detachObserver(this); } - if (pageId.subPage() != PageId::SINGLE_PAGE) { - pageFileInfo = pageFileInfo.right(11) + ((pageId.subPage() == PageId::LEFT_PAGE) ? tr(" [L]") : tr(" [R]")); + if (infoProvider) { + infoProvider->attachObserver(this); } - ui.pageInfoLine->setVisible(true); - ui.pageInfo->setText(pageFileInfo); -} - -void StatusBarPanel::clear() { - ui.mousePosLabel->clear(); - ui.physSizeLabel->clear(); - ui.pageNoLabel->clear(); - ui.pageInfo->clear(); - - ui.mousePosLine->setVisible(false); - ui.physSizeLine->setVisible(false); - ui.pageInfoLine->setVisible(false); -} - -void StatusBarPanel::updateUnits(Units) { - updateMousePos(ImageViewInfoProvider::getInstance()->getMousePos()); - updatePhysSize(ImageViewInfoProvider::getInstance()->getPhysSize()); + this->infoProvider = infoProvider; } diff --git a/StatusBarPanel.h b/StatusBarPanel.h index a2af87bc4..d330f2cde 100644 --- a/StatusBarPanel.h +++ b/StatusBarPanel.h @@ -7,6 +7,8 @@ #include "ui_StatusBarPanel.h" #include "UnitsObserver.h" #include "ImageViewInfoObserver.h" +#include "Dpi.h" +#include "ImageViewInfoProvider.h" class PageId; @@ -15,6 +17,10 @@ Q_OBJECT private: mutable QMutex mutex; Ui::StatusBarPanel ui; + QPointF mousePos; + QSizeF physSize; + Dpi dpi; + ImageViewInfoProvider* infoProvider; public: StatusBarPanel(); @@ -26,11 +32,22 @@ Q_OBJECT void updatePhysSize(const QSizeF& physSize) override; + void updateDpi(const Dpi& dpi) override; + + void clearImageViewInfo() override; + void updatePage(int pageNumber, const PageId& pageId); void clear(); void updateUnits(Units) override; + + void setInfoProvider(ImageViewInfoProvider* infoProvider); + +private: + void mousePosChanged(); + + void physSizeChanged(); }; diff --git a/UnitsObserver.cpp b/UnitsObserver.cpp index 7d25a75cd..c84622b17 100644 --- a/UnitsObserver.cpp +++ b/UnitsObserver.cpp @@ -8,7 +8,4 @@ UnitsObserver::UnitsObserver() { UnitsObserver::~UnitsObserver() { UnitsProvider::getInstance()->detachObserver(this); -} - -void UnitsObserver::updateDpi(const Dpi& dpi) { } \ No newline at end of file diff --git a/UnitsObserver.h b/UnitsObserver.h index be654bf3a..ac0bf5323 100644 --- a/UnitsObserver.h +++ b/UnitsObserver.h @@ -13,8 +13,6 @@ class UnitsObserver { virtual ~UnitsObserver(); - virtual void updateDpi(const Dpi& dpi); - virtual void updateUnits(Units units) = 0; }; diff --git a/UnitsProvider.cpp b/UnitsProvider.cpp index 4c75e01e6..ab0f46bff 100644 --- a/UnitsProvider.cpp +++ b/UnitsProvider.cpp @@ -3,6 +3,7 @@ #include #include "UnitsProvider.h" #include "Dpm.h" +#include "UnitsConverter.h" std::unique_ptr UnitsProvider::instance = nullptr; @@ -18,15 +19,6 @@ UnitsProvider* UnitsProvider::getInstance() { return instance.get(); } -const Dpi& UnitsProvider::getDpi() const { - return unitsConverter.getDpi(); -} - -void UnitsProvider::setDpi(const Dpi& dpi) { - unitsConverter.setDpi(dpi); - dpiChanged(); -} - Units UnitsProvider::getUnits() const { return units; } @@ -41,16 +33,7 @@ void UnitsProvider::attachObserver(UnitsObserver* observer) { } void UnitsProvider::detachObserver(UnitsObserver* observer) { - auto it = std::find(observers.begin(), observers.end(), observer); - if (it != observers.end()) { - observers.erase(it); - } -} - -void UnitsProvider::dpiChanged() { - for (UnitsObserver* observer : observers) { - observer->updateDpi(unitsConverter.getDpi()); - } + observers.remove(observer); } void UnitsProvider::unitsChanged() { @@ -59,17 +42,10 @@ void UnitsProvider::unitsChanged() { } } -void UnitsProvider::convert(double& horizontalValue, - double& verticalValue, - Units fromUnits, - Units toUnits) const { - unitsConverter.convert(horizontalValue, verticalValue, fromUnits, toUnits); -} - -void UnitsProvider::convertFrom(double& horizontalValue, double& verticalValue, Units fromUnits) const { - convert(horizontalValue, verticalValue, fromUnits, units); +void UnitsProvider::convertFrom(double& horizontalValue, double& verticalValue, Units fromUnits, const Dpi& dpi) const { + UnitsConverter(dpi).convert(horizontalValue, verticalValue, fromUnits, units); } -void UnitsProvider::convertTo(double& horizontalValue, double& verticalValue, Units toUnits) const { - convert(horizontalValue, verticalValue, units, toUnits); +void UnitsProvider::convertTo(double& horizontalValue, double& verticalValue, Units toUnits, const Dpi& dpi) const { + UnitsConverter(dpi).convert(horizontalValue, verticalValue, units, toUnits); } diff --git a/UnitsProvider.h b/UnitsProvider.h index fe9f0ed3b..a90edbbf3 100644 --- a/UnitsProvider.h +++ b/UnitsProvider.h @@ -5,7 +5,6 @@ #include #include #include "UnitsObserver.h" -#include "UnitsConverter.h" class Dpi; @@ -15,17 +14,12 @@ class UnitsProvider { std::list observers; Units units; - UnitsConverter unitsConverter; UnitsProvider(); public: static UnitsProvider* getInstance(); - const Dpi& getDpi() const; - - void setDpi(const Dpi& dpi); - Units getUnits() const; void setUnits(Units units); @@ -34,14 +28,11 @@ class UnitsProvider { void detachObserver(UnitsObserver* observer); - void convert(double& horizontalValue, double& verticalValue, Units fromUnits, Units toUnits) const; - - void convertFrom(double& horizontalValue, double& verticalValue, Units fromUnits) const; + void convertFrom(double& horizontalValue, double& verticalValue, Units fromUnits, const Dpi& dpi) const; - void convertTo(double& horizontalValue, double& verticalValue, Units toUnits) const; + void convertTo(double& horizontalValue, double& verticalValue, Units toUnits, const Dpi& dpi) const; protected: - void dpiChanged(); void unitsChanged(); }; diff --git a/Utils.h b/Utils.h index cb2f59899..88fe49ff4 100644 --- a/Utils.h +++ b/Utils.h @@ -30,6 +30,9 @@ class Utils { template static typename M::iterator unorderedMapSetValue(M& map, const K& key, const V& val); + template + static T castOrFindChild(QObject* object); + /** * \brief If \p output_dir exists, creates a "cache" subdirectory under it. * @@ -95,4 +98,13 @@ typename M::iterator Utils::unorderedMapSetValue(M& map, const K& key, const V& } } +template +T Utils::castOrFindChild(QObject* object) { + if (auto result = dynamic_cast(object)) { + return result; + } else { + return object->findChild(); + } +} + #endif // ifndef UTILS_H_ diff --git a/filters/deskew/Filter.cpp b/filters/deskew/Filter.cpp index d24401df9..875506941 100644 --- a/filters/deskew/Filter.cpp +++ b/filters/deskew/Filter.cpp @@ -55,8 +55,8 @@ namespace deskew { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* const ui, const PageId& page_id) { - m_ptrOptionsWidget->preUpdateUI(page_id); + void Filter::preUpdateUI(FilterUiInterface* const ui, const PageInfo& page_info) { + m_ptrOptionsWidget->preUpdateUI(page_info.id()); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } @@ -121,7 +121,9 @@ namespace deskew { } } // Filter::loadSettings - void Filter::writePageSettings(QDomDocument& doc, QDomElement& filter_el, const PageId& page_id, + void Filter::writePageSettings(QDomDocument& doc, + QDomElement& filter_el, + const PageId& page_id, const int numeric_id) const { const std::unique_ptr params(m_ptrSettings->getPageParams(page_id)); if (!params) { diff --git a/filters/deskew/Filter.h b/filters/deskew/Filter.h index 567e844a5..a81c6b72d 100644 --- a/filters/deskew/Filter.h +++ b/filters/deskew/Filter.h @@ -55,7 +55,7 @@ namespace deskew { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; void updateStatistics() override; diff --git a/filters/deskew/Task.cpp b/filters/deskew/Task.cpp index 2a4bf24f5..45edeac9a 100644 --- a/filters/deskew/Task.cpp +++ b/filters/deskew/Task.cpp @@ -265,8 +265,6 @@ namespace deskew { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_image)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(m_uiData); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); diff --git a/filters/fix_orientation/Filter.cpp b/filters/fix_orientation/Filter.cpp index 3bf7f9ab8..ae51aad54 100644 --- a/filters/fix_orientation/Filter.cpp +++ b/filters/fix_orientation/Filter.cpp @@ -61,12 +61,12 @@ namespace fix_orientation { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* ui, const PageId& page_id) { + void Filter::preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) { if (m_ptrOptionsWidget.get()) { const OrthogonalRotation rotation( - m_ptrSettings->getRotationFor(page_id.imageId()) + m_ptrSettings->getRotationFor(page_info.id().imageId()) ); - m_ptrOptionsWidget->preUpdateUI(page_id, rotation); + m_ptrOptionsWidget->preUpdateUI(page_info.id(), rotation); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } } diff --git a/filters/fix_orientation/Filter.h b/filters/fix_orientation/Filter.h index 5b58583cd..b864e38b6 100644 --- a/filters/fix_orientation/Filter.h +++ b/filters/fix_orientation/Filter.h @@ -61,7 +61,7 @@ namespace fix_orientation { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId&) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const override; diff --git a/filters/fix_orientation/Task.cpp b/filters/fix_orientation/Task.cpp index ac92ef77e..399d7a12e 100644 --- a/filters/fix_orientation/Task.cpp +++ b/filters/fix_orientation/Task.cpp @@ -108,8 +108,6 @@ namespace fix_orientation { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_image)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(m_xform.preRotation()); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); diff --git a/filters/output/Filter.cpp b/filters/output/Filter.cpp index eb6f86bca..2ffd60efc 100644 --- a/filters/output/Filter.cpp +++ b/filters/output/Filter.cpp @@ -57,8 +57,8 @@ namespace output { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* ui, const PageId& page_id) { - m_ptrOptionsWidget->preUpdateUI(page_id); + void Filter::preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) { + m_ptrOptionsWidget->preUpdateUI(page_info.id()); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } diff --git a/filters/output/Filter.h b/filters/output/Filter.h index 78df2bea5..b7d68b9d5 100644 --- a/filters/output/Filter.h +++ b/filters/output/Filter.h @@ -54,7 +54,7 @@ namespace output { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const override; diff --git a/filters/output/TabbedImageView.cpp b/filters/output/TabbedImageView.cpp index 04cb920eb..6000d27d5 100644 --- a/filters/output/TabbedImageView.cpp +++ b/filters/output/TabbedImageView.cpp @@ -20,6 +20,7 @@ #include #include "DespeckleView.h" #include +#include "../../Utils.h" namespace output { TabbedImageView::TabbedImageView(QWidget* parent) @@ -65,7 +66,7 @@ namespace output { copyViewZoomAndPos(m_prevImageViewTabIndex, idx); - if (findImageViewBase(widget(idx)) != nullptr) { + if (Utils::castOrFindChild(widget(idx)) != nullptr) { m_prevImageViewTabIndex = idx; } } @@ -94,8 +95,8 @@ namespace output { const QRectF& old_view_rect = m_tabImageRectMap->at(old_view_tab); const QRectF& new_view_rect = m_tabImageRectMap->at(new_view_tab); - auto* old_image_view = findImageViewBase(widget(old_idx)); - auto* new_image_view = findImageViewBase(widget(new_idx)); + auto* old_image_view = Utils::castOrFindChild(widget(old_idx)); + auto* new_image_view = Utils::castOrFindChild(widget(new_idx)); if ((old_image_view == nullptr) || (new_image_view == nullptr)) { return; } @@ -148,24 +149,6 @@ namespace output { ver_bar.setValue(ver_value); } - ImageViewBase* TabbedImageView::findImageViewBase(QWidget* parent) const { - if (parent == nullptr) { - return nullptr; - } - - if (auto* resource = dynamic_cast(parent)) { - return resource; - } else { - for (QObject* child : parent->children()) { - if ((resource = findImageViewBase(dynamic_cast(child)))) { - return resource; - } - } - } - - return nullptr; - } - void TabbedImageView::keyReleaseEvent(QKeyEvent* event) { event->setAccepted(false); if (event->modifiers() != Qt::ControlModifier) { diff --git a/filters/output/TabbedImageView.h b/filters/output/TabbedImageView.h index 02bbcc1e2..c43e8250b 100644 --- a/filters/output/TabbedImageView.h +++ b/filters/output/TabbedImageView.h @@ -63,8 +63,6 @@ namespace output { void setFocus(QScrollBar& hor_bar, QScrollBar& ver_bar, const QRectF& rect, const QPointF& focal) const; - ImageViewBase* findImageViewBase(QWidget* parent) const; - std::unordered_map m_registry; std::unique_ptr m_tabImageRectMap; int m_prevImageViewTabIndex; diff --git a/filters/output/Task.cpp b/filters/output/Task.cpp index f689a7b1e..34627df29 100644 --- a/filters/output/Task.cpp +++ b/filters/output/Task.cpp @@ -563,8 +563,6 @@ namespace output { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_outputImage)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); @@ -612,15 +610,13 @@ namespace output { ); } else { picture_zone_editor = std::make_unique( - m_origImage, downscaled_orig_pixmap, m_pictureMask, m_xform.transform(), m_xform.resultingPreCropArea(), m_pageId, m_ptrSettings - ); QObject::connect( - picture_zone_editor.get(), SIGNAL(invalidateThumbnail(const PageId &)), - opt_widget, SIGNAL(invalidateThumbnail(const PageId &)) + picture_zone_editor.get(), SIGNAL(invalidateThumbnail(const PageId&)), + opt_widget, SIGNAL(invalidateThumbnail(const PageId&)) ); tab_image_rect_map->insert( std::pair(TAB_PICTURE_ZONES, m_xform.resultingPreCropArea().boundingRect())); diff --git a/filters/page_layout/Filter.cpp b/filters/page_layout/Filter.cpp index 8e70edaf5..87d3a30c1 100644 --- a/filters/page_layout/Filter.cpp +++ b/filters/page_layout/Filter.cpp @@ -94,10 +94,10 @@ namespace page_layout { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* ui, const PageId& page_id) { - const Margins margins_mm(m_ptrSettings->getHardMarginsMM(page_id)); - const Alignment alignment(m_ptrSettings->getPageAlignment(page_id)); - m_ptrOptionsWidget->preUpdateUI(page_id, margins_mm, alignment); + void Filter::preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) { + const Margins margins_mm(m_ptrSettings->getHardMarginsMM(page_info.id())); + const Alignment alignment(m_ptrSettings->getPageAlignment(page_info.id())); + m_ptrOptionsWidget->preUpdateUI(page_info, margins_mm, alignment); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } diff --git a/filters/page_layout/Filter.h b/filters/page_layout/Filter.h index 47309fc59..90259b152 100644 --- a/filters/page_layout/Filter.h +++ b/filters/page_layout/Filter.h @@ -69,7 +69,7 @@ namespace page_layout { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const override; diff --git a/filters/page_layout/ImageView.cpp b/filters/page_layout/ImageView.cpp index 7c7314475..a939c9ff6 100644 --- a/filters/page_layout/ImageView.cpp +++ b/filters/page_layout/ImageView.cpp @@ -688,7 +688,7 @@ namespace page_layout { void ImageView::updatePhysSize() { if (m_outerRect.isValid()) { - ImageViewInfoProvider::getInstance()->setPhysSize(m_outerRect.size()); + infoProvider().setPhysSize(m_outerRect.size()); } else { ImageViewBase::updatePhysSize(); } diff --git a/filters/page_layout/OptionsWidget.cpp b/filters/page_layout/OptionsWidget.cpp index 8158190d8..fbd9b1ed5 100644 --- a/filters/page_layout/OptionsWidget.cpp +++ b/filters/page_layout/OptionsWidget.cpp @@ -96,10 +96,11 @@ namespace page_layout { OptionsWidget::~OptionsWidget() = default; - void OptionsWidget::preUpdateUI(const PageId& page_id, const Margins& margins_mm, const Alignment& alignment) { + void OptionsWidget::preUpdateUI(const PageInfo& page_info, const Margins& margins_mm, const Alignment& alignment) { removeUiConnections(); - m_pageId = page_id; + m_pageId = page_info.id(); + m_dpi = page_info.metadata().dpi(); m_marginsMM = margins_mm; m_alignment = alignment; @@ -130,7 +131,7 @@ namespace page_layout { alignmentMode->blockSignals(false); updateAlignmentButtonsEnabled(); - autoMargins->setChecked(m_ptrSettings->isPageAutoMarginsEnabled(page_id)); + autoMargins->setChecked(m_ptrSettings->isPageAutoMarginsEnabled(m_pageId)); updateMarginsControlsEnabled(); m_leftRightLinked = m_leftRightLinked && (margins_mm.left() == margins_mm.right()); @@ -208,8 +209,8 @@ namespace page_layout { double dummy; double leftMarginSpinBoxValue = leftMarginSpinBox->value(); double rightMarginSpinBoxValue = rightMarginSpinBox->value(); - UnitsProvider::getInstance()->convertTo(leftMarginSpinBoxValue, dummy, MILLIMETRES); - UnitsProvider::getInstance()->convertTo(rightMarginSpinBoxValue, dummy, MILLIMETRES); + UnitsProvider::getInstance()->convertTo(leftMarginSpinBoxValue, dummy, MILLIMETRES, m_dpi); + UnitsProvider::getInstance()->convertTo(rightMarginSpinBoxValue, dummy, MILLIMETRES, m_dpi); m_marginsMM.setLeft(leftMarginSpinBoxValue); m_marginsMM.setRight(rightMarginSpinBoxValue); @@ -231,8 +232,8 @@ namespace page_layout { double dummy; double topMarginSpinBoxValue = topMarginSpinBox->value(); double bottomMarginSpinBoxValue = bottomMarginSpinBox->value(); - UnitsProvider::getInstance()->convertTo(dummy, topMarginSpinBoxValue, MILLIMETRES); - UnitsProvider::getInstance()->convertTo(dummy, bottomMarginSpinBoxValue, MILLIMETRES); + UnitsProvider::getInstance()->convertTo(dummy, topMarginSpinBoxValue, MILLIMETRES, m_dpi); + UnitsProvider::getInstance()->convertTo(dummy, bottomMarginSpinBoxValue, MILLIMETRES, m_dpi); m_marginsMM.setTop(topMarginSpinBoxValue); m_marginsMM.setBottom(bottomMarginSpinBoxValue); @@ -387,8 +388,8 @@ namespace page_layout { double bottomMarginValue = m_marginsMM.bottom(); double leftMarginValue = m_marginsMM.left(); double rightMarginValue = m_marginsMM.right(); - UnitsProvider::getInstance()->convertFrom(leftMarginValue, topMarginValue, MILLIMETRES); - UnitsProvider::getInstance()->convertFrom(rightMarginValue, bottomMarginValue, MILLIMETRES); + UnitsProvider::getInstance()->convertFrom(leftMarginValue, topMarginValue, MILLIMETRES, m_dpi); + UnitsProvider::getInstance()->convertFrom(rightMarginValue, bottomMarginValue, MILLIMETRES, m_dpi); topMarginSpinBox->setValue(topMarginValue); bottomMarginSpinBox->setValue(bottomMarginValue); diff --git a/filters/page_layout/OptionsWidget.h b/filters/page_layout/OptionsWidget.h index a2ebb8486..a3c0444fa 100644 --- a/filters/page_layout/OptionsWidget.h +++ b/filters/page_layout/OptionsWidget.h @@ -45,7 +45,7 @@ namespace page_layout { ~OptionsWidget() override; - void preUpdateUI(const PageId& page_id, const Margins& margins_mm, const Alignment& alignment); + void preUpdateUI(const PageInfo& page_info, const Margins& margins_mm, const Alignment& alignment); void postUpdateUI(); @@ -122,6 +122,7 @@ namespace page_layout { QIcon m_brokenChainIcon; AlignmentByButton m_alignmentByButton; PageId m_pageId; + Dpi m_dpi; Margins m_marginsMM; Alignment m_alignment; int m_ignoreMarginChanges; diff --git a/filters/page_layout/Task.cpp b/filters/page_layout/Task.cpp index be53910bb..ea7669f00 100644 --- a/filters/page_layout/Task.cpp +++ b/filters/page_layout/Task.cpp @@ -160,8 +160,6 @@ namespace page_layout { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_image)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); diff --git a/filters/page_split/Filter.cpp b/filters/page_split/Filter.cpp index 799603280..f3ecc76ed 100644 --- a/filters/page_split/Filter.cpp +++ b/filters/page_split/Filter.cpp @@ -71,8 +71,8 @@ namespace page_split { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* ui, const PageId& page_id) { - m_ptrOptionsWidget->preUpdateUI(page_id); + void Filter::preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) { + m_ptrOptionsWidget->preUpdateUI(page_info.id()); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } @@ -160,7 +160,9 @@ namespace page_split { m_ptrPages->autoSetLayoutTypeFor(image_id, orientation); } - void Filter::writeImageSettings(QDomDocument& doc, QDomElement& filter_el, const ImageId& image_id, + void Filter::writeImageSettings(QDomDocument& doc, + QDomElement& filter_el, + const ImageId& image_id, const int numeric_id) const { const Settings::Record record(m_ptrSettings->getPageRecord(image_id)); diff --git a/filters/page_split/Filter.h b/filters/page_split/Filter.h index dca123fa1..f3c392f3c 100644 --- a/filters/page_split/Filter.h +++ b/filters/page_split/Filter.h @@ -63,7 +63,7 @@ namespace page_split { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; QDomElement saveSettings(const ProjectWriter& wirter, QDomDocument& doc) const override; diff --git a/filters/page_split/Task.cpp b/filters/page_split/Task.cpp index 1fa29bdb3..24d6c212d 100644 --- a/filters/page_split/Task.cpp +++ b/filters/page_split/Task.cpp @@ -249,8 +249,6 @@ namespace page_split { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_image)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(m_uiData); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); diff --git a/filters/select_content/Filter.cpp b/filters/select_content/Filter.cpp index 762930d1c..d4b18303f 100644 --- a/filters/select_content/Filter.cpp +++ b/filters/select_content/Filter.cpp @@ -83,8 +83,8 @@ namespace select_content { m_ptrSettings->performRelinking(relinker); } - void Filter::preUpdateUI(FilterUiInterface* ui, const PageId& page_id) { - m_ptrOptionsWidget->preUpdateUI(page_id); + void Filter::preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) { + m_ptrOptionsWidget->preUpdateUI(page_info); ui->setOptionsWidget(m_ptrOptionsWidget.get(), ui->KEEP_OWNERSHIP); } diff --git a/filters/select_content/Filter.h b/filters/select_content/Filter.h index 7d4f3a0ad..acc9d40b6 100644 --- a/filters/select_content/Filter.h +++ b/filters/select_content/Filter.h @@ -65,7 +65,7 @@ namespace select_content { void performRelinking(const AbstractRelinker& relinker) override; - void preUpdateUI(FilterUiInterface* ui, const PageId& page_id) override; + void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; void updateStatistics() override; diff --git a/filters/select_content/OptionsWidget.cpp b/filters/select_content/OptionsWidget.cpp index 5149541da..430ecbbfb 100644 --- a/filters/select_content/OptionsWidget.cpp +++ b/filters/select_content/OptionsWidget.cpp @@ -38,11 +38,11 @@ namespace select_content { OptionsWidget::~OptionsWidget() = default; - void OptionsWidget::preUpdateUI(const PageId& page_id) { + void OptionsWidget::preUpdateUI(const PageInfo& page_info) { removeUiConnections(); - m_pageId = page_id; - + m_pageId = page_info.id(); + m_dpi = page_info.metadata().dpi(); contentDetectAutoBtn->setEnabled(false); contentDetectManualBtn->setEnabled(false); contentDetectDisableBtn->setEnabled(false); @@ -120,7 +120,7 @@ namespace select_content { double width = size.width(); double height = size.height(); - UnitsProvider::getInstance()->convertFrom(width, height, PIXELS); + UnitsProvider::getInstance()->convertFrom(width, height, PIXELS, m_dpi); widthSpinBox->setValue(width); heightSpinBox->setValue(height); @@ -235,7 +235,7 @@ namespace select_content { double widthSpinBoxValue = widthSpinBox->value(); double heightSpinBoxValue = heightSpinBox->value(); - UnitsProvider::getInstance()->convertTo(widthSpinBoxValue, heightSpinBoxValue, PIXELS); + UnitsProvider::getInstance()->convertTo(widthSpinBoxValue, heightSpinBoxValue, PIXELS, m_dpi); QRectF newPageRect = m_uiData.pageRect(); newPageRect.setSize(QSizeF(widthSpinBoxValue, heightSpinBoxValue)); diff --git a/filters/select_content/OptionsWidget.h b/filters/select_content/OptionsWidget.h index 1b892dd16..0f0fb5442 100644 --- a/filters/select_content/OptionsWidget.h +++ b/filters/select_content/OptionsWidget.h @@ -100,7 +100,7 @@ namespace select_content { ~OptionsWidget() override; - void preUpdateUI(const PageId& page_id); + void preUpdateUI(const PageInfo& page_info); void postUpdateUI(const UiData& ui_data); @@ -157,6 +157,7 @@ namespace select_content { UiData m_uiData; PageSelectionAccessor m_pageSelectionAccessor; PageId m_pageId; + Dpi m_dpi; int m_ignorePageSizeChanges; }; } // namespace select_content diff --git a/filters/select_content/Task.cpp b/filters/select_content/Task.cpp index eeb24a268..b06709752 100644 --- a/filters/select_content/Task.cpp +++ b/filters/select_content/Task.cpp @@ -216,8 +216,6 @@ namespace select_content { void Task::UiUpdater::updateUI(FilterUiInterface* ui) { // This function is executed from the GUI thread. - UnitsProvider::getInstance()->setDpi(Dpm(m_image)); - OptionsWidget* const opt_widget = m_ptrFilter->optionsWidget(); opt_widget->postUpdateUI(m_uiData); ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP); diff --git a/imageproc/GrayImage.cpp b/imageproc/GrayImage.cpp index e338c40d6..798a02c26 100644 --- a/imageproc/GrayImage.cpp +++ b/imageproc/GrayImage.cpp @@ -46,4 +46,20 @@ namespace imageproc { void GrayImage::invert() { m_image.invertPixels(QImage::InvertRgb); } + + int GrayImage::dotsPerMeterX() const { + return m_image.dotsPerMeterX(); + } + + int GrayImage::dotsPerMeterY() const { + return m_image.dotsPerMeterY(); + } + + void GrayImage::setDotsPerMeterX(int value) { + m_image.setDotsPerMeterX(value); + } + + void GrayImage::setDotsPerMeterY(int value) { + m_image.setDotsPerMeterY(value); + } } // namespace imageproc diff --git a/imageproc/GrayImage.h b/imageproc/GrayImage.h index 3f685ca2a..84e1692a1 100644 --- a/imageproc/GrayImage.h +++ b/imageproc/GrayImage.h @@ -110,6 +110,14 @@ namespace imageproc { GrayImage inverted() const; + int dotsPerMeterX() const; + + int dotsPerMeterY() const; + + void setDotsPerMeterX(int value); + + void setDotsPerMeterY(int value); + private: QImage m_image; }; diff --git a/imageproc/Transform.cpp b/imageproc/Transform.cpp index 6f2f3162c..78573a7fd 100644 --- a/imageproc/Transform.cpp +++ b/imageproc/Transform.cpp @@ -300,6 +300,24 @@ namespace imageproc { } } } // transformGeneric + + void fixDpiInPlace(QImage& image, const QTransform& xform) { + if (xform.isScaling()) { + QRect dpi_rect(QPoint(0, 0), QSize(image.dotsPerMeterX(), image.dotsPerMeterY())); + xform.mapRect(dpi_rect); + image.setDotsPerMeterX(dpi_rect.width()); + image.setDotsPerMeterX(dpi_rect.width()); + } + } + + void fixDpiInPlace(GrayImage& image, const QTransform& xform) { + if (xform.isScaling()) { + QRect dpi_rect(QPoint(0, 0), QSize(image.dotsPerMeterX(), image.dotsPerMeterY())); + xform.mapRect(dpi_rect); + image.setDotsPerMeterX(dpi_rect.width()); + image.setDotsPerMeterX(dpi_rect.width()); + } + } } // namespace QImage transform(const QImage& src, @@ -342,6 +360,8 @@ namespace imageproc { min_mapping_area ); + fixDpiInPlace(gray_dst, xform); + return gray_dst; } default: @@ -358,6 +378,8 @@ namespace imageproc { outside_pixels.rgb(), outside_pixels.flags(), min_mapping_area ); + fixDpiInPlace(dst, xform); + return dst; } else { const QImage src_argb32(src.convertToFormat(QImage::Format_ARGB32)); @@ -373,6 +395,8 @@ namespace imageproc { outside_pixels.rgba(), outside_pixels.flags(), min_mapping_area ); + fixDpiInPlace(dst, xform); + return dst; } } @@ -406,6 +430,8 @@ namespace imageproc { min_mapping_area ); + fixDpiInPlace(dst, xform); + return dst; } } // namespace imageproc \ No newline at end of file From ad5168be9760383b274e9eb5ea7ae3e7f026d5c9 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Mon, 12 Feb 2018 06:59:03 +0300 Subject: [PATCH 09/28] ~ Fix for the margins links of the default params dialog. --- DefaultParamsDialog.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 8886f09a2..04daf8cdc 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -83,8 +83,8 @@ DefaultParamsDialog::DefaultParamsDialog(QWidget* parent) brokenChainIcon.addPixmap( QPixmap(QString::fromLatin1(":/icons/stock-vchain-broken-24.png")) ); - setLinkButtonLinked(topBottomLink, leftRightLinkEnabled); - setLinkButtonLinked(leftRightLink, topBottomLinkEnabled); + setLinkButtonLinked(topBottomLink, topBottomLinkEnabled); + setLinkButtonLinked(leftRightLink, leftRightLinkEnabled); Utils::unorderedMapSetValue( alignmentByButton, alignTopLeftBtn, @@ -233,10 +233,10 @@ void DefaultParamsDialog::updatePageLayoutDisplay(const DefaultParams::PageLayou bottomMarginSpinBox->setValue(margins.bottom()); leftMarginSpinBox->setValue(margins.left()); - leftRightLinkEnabled = (margins.top() == margins.bottom()); - topBottomLinkEnabled = (margins.left() == margins.right()); - setLinkButtonLinked(topBottomLink, leftRightLinkEnabled); - setLinkButtonLinked(leftRightLink, topBottomLinkEnabled); + topBottomLinkEnabled= (margins.top() == margins.bottom()); + leftRightLinkEnabled = (margins.left() == margins.right()); + setLinkButtonLinked(topBottomLink, topBottomLinkEnabled); + setLinkButtonLinked(leftRightLink, leftRightLinkEnabled); const Alignment& alignment = params.getAlignment(); if (alignment.vertical() == Alignment::VAUTO) { @@ -841,7 +841,7 @@ void DefaultParamsDialog::setLinkButtonLinked(QToolButton* button, bool linked) void DefaultParamsDialog::topBottomLinkClicked() { topBottomLinkEnabled = !topBottomLinkEnabled; - setLinkButtonLinked(topBottomLink, leftRightLinkEnabled); + setLinkButtonLinked(topBottomLink, topBottomLinkEnabled); if (topBottomLinkEnabled && (topMarginSpinBox->value() != bottomMarginSpinBox->value())) { const double new_margin = std::min( topMarginSpinBox->value(), bottomMarginSpinBox->value() @@ -853,7 +853,7 @@ void DefaultParamsDialog::topBottomLinkClicked() { void DefaultParamsDialog::leftRightLinkClicked() { leftRightLinkEnabled = !leftRightLinkEnabled; - setLinkButtonLinked(leftRightLink, topBottomLinkEnabled); + setLinkButtonLinked(leftRightLink, leftRightLinkEnabled); if (leftRightLinkEnabled && (leftMarginSpinBox->value() != rightMarginSpinBox->value())) { const double new_margin = std::min( leftMarginSpinBox->value(), rightMarginSpinBox->value() From db559de4cf1c8cf95455b7517727b6e096f07265 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Tue, 13 Feb 2018 15:41:49 +0300 Subject: [PATCH 10/28] Improved: a default params profile can now be edited. --- DefaultParamsDialog.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 04daf8cdc..6982eda7f 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -982,9 +982,9 @@ void DefaultParamsDialog::profileChanged(const int index) { } else { std::unique_ptr profile = profileManager.readProfile(profileCB->itemData(index).toString()); if (profile != nullptr) { - profileSaveButton->setEnabled(false); + profileSaveButton->setEnabled(true); profileDeleteButton->setEnabled(true); - setTabWidgetsEnabled(false); + setTabWidgetsEnabled(true); loadParams(*profile); } else { @@ -1014,15 +1014,16 @@ void DefaultParamsDialog::profileSavePressed() { } if (profileManager.writeProfile(*buildParams(), profileCB->currentText())) { - const QString profileName = profileCB->currentText(); - profileCB->setItemData(profileCB->currentIndex(), profileName); - profileCB->setItemText(profileCB->currentIndex(), profileName); - customProfileItemIdx = profileCB->count(); - profileCB->addItem(tr("Custom"), "Custom"); - - profileSaveButton->setEnabled(false); - profileDeleteButton->setEnabled(true); - setTabWidgetsEnabled(false); + if (profileCB->currentIndex() == customProfileItemIdx) { + const QString profileName = profileCB->currentText(); + profileCB->setItemData(profileCB->currentIndex(), profileName); + profileCB->setItemText(profileCB->currentIndex(), profileName); + customProfileItemIdx = profileCB->count(); + profileCB->addItem(tr("Custom"), "Custom"); + + profileCB->setEditable(false); + profileDeleteButton->setEnabled(true); + } } else { QMessageBox::critical(this, tr("Error"), tr("Error saving the profile.")); } From 12a902d8f93fa71a93250799f1f302b4ad5a3bbc Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Mon, 12 Feb 2018 08:58:21 +0300 Subject: [PATCH 11/28] Added page count info beside page number into status bar. --- MainWindow.cpp | 5 +++-- StatusBarPanel.cpp | 4 ++-- StatusBarPanel.h | 2 +- ui/StatusBarPanel.ui | 7 +++++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 30c0f9787..0539c7419 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -153,7 +153,8 @@ MainWindow::MainWindow() connect(m_ptrThumbSequence.get(), &ThumbnailSequence::newSelectionLeader, [this](const PageInfo& page_info) { PageSequence pageSequence = m_ptrThumbSequence->toPageSequence(); if (pageSequence.numPages() > 0) { - m_statusBarPanel->updatePage(pageSequence.pageNo(page_info.id()) + 1, page_info.id()); + m_statusBarPanel->updatePage(pageSequence.pageNo(page_info.id()) + 1, + pageSequence.numPages(), page_info.id()); } else { m_statusBarPanel->clear(); } @@ -1684,7 +1685,7 @@ void MainWindow::updateMainArea() { filterList->setBatchProcessingPossible(true); PageSequence pageSequence = m_ptrThumbSequence->toPageSequence(); if (pageSequence.numPages() > 0) { - m_statusBarPanel->updatePage(pageSequence.pageNo(page.id()) + 1, page.id()); + m_statusBarPanel->updatePage(pageSequence.pageNo(page.id()) + 1, pageSequence.numPages(), page.id()); } loadPageInteractive(page); } diff --git a/StatusBarPanel.cpp b/StatusBarPanel.cpp index 0a1a730d1..c1a7a294b 100644 --- a/StatusBarPanel.cpp +++ b/StatusBarPanel.cpp @@ -35,8 +35,8 @@ void StatusBarPanel::clearImageViewInfo() { dpi = Dpi(); } -void StatusBarPanel::updatePage(int pageNumber, const PageId& pageId) { - ui.pageNoLabel->setText(tr("p. %1").arg(pageNumber)); +void StatusBarPanel::updatePage(int pageNumber, size_t pageCount, const PageId& pageId) { + ui.pageNoLabel->setText(tr("p. %1 / %2").arg(pageNumber).arg(pageCount)); QString pageFileInfo = QFileInfo(pageId.imageId().filePath()).baseName(); if (pageFileInfo.size() > 15) { diff --git a/StatusBarPanel.h b/StatusBarPanel.h index d330f2cde..deffa021e 100644 --- a/StatusBarPanel.h +++ b/StatusBarPanel.h @@ -36,7 +36,7 @@ Q_OBJECT void clearImageViewInfo() override; - void updatePage(int pageNumber, const PageId& pageId); + void updatePage(int pageNumber, size_t pageCount, const PageId& pageId); void clear(); diff --git a/ui/StatusBarPanel.ui b/ui/StatusBarPanel.ui index ed4a791c2..9bb35adf2 100644 --- a/ui/StatusBarPanel.ui +++ b/ui/StatusBarPanel.ui @@ -6,7 +6,7 @@ 0 0 - 343 + 327 16 @@ -103,7 +103,7 @@ - 31 + 35 0 @@ -113,6 +113,9 @@ Position of the selected page in current order. + + + Qt::AlignCenter From d2dd4d2f6ac8eca5373fafbfeb2ea5f4f8691489 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Mon, 12 Feb 2018 08:55:14 +0300 Subject: [PATCH 12/28] ~ Added missing update for the links when margins changed at page layout stage. --- filters/page_layout/OptionsWidget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/filters/page_layout/OptionsWidget.cpp b/filters/page_layout/OptionsWidget.cpp index fbd9b1ed5..10c9bf792 100644 --- a/filters/page_layout/OptionsWidget.cpp +++ b/filters/page_layout/OptionsWidget.cpp @@ -395,6 +395,11 @@ namespace page_layout { bottomMarginSpinBox->setValue(bottomMarginValue); leftMarginSpinBox->setValue(leftMarginValue); rightMarginSpinBox->setValue(rightMarginValue); + + m_leftRightLinked = m_leftRightLinked && (leftMarginSpinBox->value() == rightMarginSpinBox->value()); + m_topBottomLinked = m_topBottomLinked && (topMarginSpinBox->value() == bottomMarginSpinBox->value()); + updateLinkDisplay(topBottomLink, m_topBottomLinked); + updateLinkDisplay(leftRightLink, m_leftRightLinked); } void OptionsWidget::updateLinkDisplay(QToolButton* button, const bool linked) { From d9a06c4002fc8a2539371657e6dc4a21a5a1ef3e Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Mon, 12 Feb 2018 09:12:45 +0300 Subject: [PATCH 13/28] ~ Disable auto-margins when the margins are changed externally. --- filters/page_layout/OptionsWidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/filters/page_layout/OptionsWidget.cpp b/filters/page_layout/OptionsWidget.cpp index 10c9bf792..ec5388c8a 100644 --- a/filters/page_layout/OptionsWidget.cpp +++ b/filters/page_layout/OptionsWidget.cpp @@ -161,6 +161,13 @@ namespace page_layout { void OptionsWidget::marginsSetExternally(const Margins& margins_mm) { m_marginsMM = margins_mm; + + if (autoMargins->isChecked()) { + autoMargins->setChecked(false); + m_ptrSettings->setPageAutoMarginsEnabled(m_pageId, false); + updateMarginsControlsEnabled(); + } + updateMarginsDisplay(); } From 7c0ad45173b081d05d6b86dffc1cfe8715687dd7 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Mon, 12 Feb 2018 17:46:37 +0300 Subject: [PATCH 14/28] ~ [ui] Improved showing the main window dock widgets. --- MainWindow.cpp | 15 +++++++-------- MainWindow.h | 2 ++ ui/MainWindow.ui | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 0539c7419..4201e3b5e 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1661,20 +1661,14 @@ PageView MainWindow::getCurrentView() const { void MainWindow::updateMainArea() { if (m_ptrPages->numImages() == 0) { filterList->setBatchProcessingPossible(false); - if (filterDockWidget->isEnabled() || thumbnailsDockWidget->isEnabled()) { - filterDockWidget->setEnabled(false); - thumbnailsDockWidget->setEnabled(false); - } + setDockWidgetsVisible(false); showNewOpenProjectPanel(); m_statusBarPanel->clear(); } else if (isBatchProcessingInProgress()) { filterList->setBatchProcessingPossible(false); setImageWidget(m_ptrBatchProcessingWidget.get(), KEEP_OWNERSHIP); } else { - if (!(filterDockWidget->isEnabled() && thumbnailsDockWidget->isEnabled())) { - filterDockWidget->setEnabled(true); - thumbnailsDockWidget->setEnabled(true); - } + setDockWidgetsVisible(true); const PageInfo page(m_ptrThumbSequence->selectionLeader()); if (page.isNull()) { filterList->setBatchProcessingPossible(false); @@ -2234,3 +2228,8 @@ void MainWindow::changeEvent(QEvent* event) { } } +void MainWindow::setDockWidgetsVisible(bool state) { + filterDockWidget->setVisible(state); + thumbnailsDockWidget->setVisible(state); +} + diff --git a/MainWindow.h b/MainWindow.h index 96c576f2a..120dfd839 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -297,6 +297,8 @@ private slots: PageSelectionAccessor newPageSelectionAccessor(); + void setDockWidgetsVisible(bool state); + QSizeF m_maxLogicalThumbSize; intrusive_ptr m_ptrPages; intrusive_ptr m_ptrStages; diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui index 69a45b113..3fb696461 100644 --- a/ui/MainWindow.ui +++ b/ui/MainWindow.ui @@ -156,7 +156,7 @@ - + 1 1 From 63314d4cf6b8a6d4afb218fd859eea983619d7c6 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 16 Feb 2018 01:43:17 +0300 Subject: [PATCH 15/28] Posterization improved: * Posterization can now correctly handle with pale images. It's become possible due to normalizing the source image before posterizing. * Added normalize option so as to add the ability to get normalized output. * Force b&w option has been reworked. --- DefaultParamsDialog.cpp | 16 ++- filters/output/ColorCommonOptions.cpp | 98 +++++++++++---- filters/output/ColorCommonOptions.h | 51 ++++++-- filters/output/OptionsWidget.cpp | 42 +++++-- filters/output/OptionsWidget.h | 2 + filters/output/OutputGenerator.cpp | 44 +++---- filters/output/OutputGenerator.h | 2 + filters/output/RenderParams.cpp | 4 +- filters/output/ui/OutputOptionsWidget.ui | 51 +++++++- imageproc/ColorTable.cpp | 148 +++++++++++++++++++---- imageproc/ColorTable.h | 12 +- ui/DefaultParamsDialog.ui | 55 ++++++++- 12 files changed, 422 insertions(+), 103 deletions(-) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 6982eda7f..3418aeed6 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -282,9 +282,10 @@ void DefaultParamsDialog::updateOutputDisplay(const DefaultParams::OutputParams& redAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getRedThresholdAdjustment()); greenAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getGreenThresholdAdjustment()); blueAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getBlueThresholdAdjustment()); - posterizeCB->setChecked(colorCommonOptions.isPosterizeEnabled()); - posterizeLevelSB->setValue(colorCommonOptions.getPosterizationLevel()); - posterizeForceBwCB->setChecked(colorCommonOptions.isForceBlackAndWhite()); + posterizeCB->setChecked(colorCommonOptions.getPosterizationOptions().isEnabled()); + posterizeLevelSB->setValue(colorCommonOptions.getPosterizationOptions().getLevel()); + posterizeNormalizationCB->setChecked(colorCommonOptions.getPosterizationOptions().isNormalizationEnabled()); + posterizeForceBwCB->setChecked(colorCommonOptions.getPosterizationOptions().isForceBlackAndWhite()); thresholdMethodBox->setCurrentIndex( thresholdMethodBox->findData(blackWhiteOptions.getBinarizationMethod()) @@ -705,9 +706,12 @@ std::unique_ptr DefaultParamsDialog::buildParams() const { ); colorCommonOptions.setCutMargins(cutMarginsCB->isChecked()); colorCommonOptions.setNormalizeIllumination(equalizeIlluminationColorCB->isChecked()); - colorCommonOptions.setPosterizeEnabled(posterizeCB->isChecked()); - colorCommonOptions.setPosterizationLevel(posterizeLevelSB->value()); - colorCommonOptions.setForceBlackAndWhite(posterizeForceBwCB->isChecked()); + ColorCommonOptions::PosterizationOptions posterizationOptions = colorCommonOptions.getPosterizationOptions(); + posterizationOptions.setEnabled(posterizeCB->isChecked()); + posterizationOptions.setLevel(posterizeLevelSB->value()); + posterizationOptions.setNormalizationEnabled(posterizeNormalizationCB->isChecked()); + posterizationOptions.setForceBlackAndWhite(posterizeForceBwCB->isChecked()); + colorCommonOptions.setPosterizationOptions(posterizationOptions); colorParams.setColorCommonOptions(colorCommonOptions); BlackWhiteOptions blackWhiteOptions; diff --git a/filters/output/ColorCommonOptions.cpp b/filters/output/ColorCommonOptions.cpp index 547b305e6..c417170f6 100644 --- a/filters/output/ColorCommonOptions.cpp +++ b/filters/output/ColorCommonOptions.cpp @@ -23,19 +23,14 @@ namespace output { ColorCommonOptions::ColorCommonOptions() : m_cutMargins(true), m_normalizeIllumination(false), - m_fillingColor(FILL_BACKGROUND), - m_posterizeEnabled(false), - m_posterizationLevel(4), - m_forceBlackAndWhite(true) { + m_fillingColor(FILL_BACKGROUND) { } ColorCommonOptions::ColorCommonOptions(const QDomElement& el) : m_cutMargins(el.attribute("cutMargins") == "1"), m_normalizeIllumination(el.attribute("normalizeIlluminationColor") == "1"), m_fillingColor(parseFillingColor(el.attribute("fillingColor"))), - m_posterizeEnabled(el.attribute("posterizeEnabled") == "1"), - m_posterizationLevel(el.attribute("posterizationLevel").toInt()), - m_forceBlackAndWhite(el.attribute("forceBlackAndWhite") == "1") { + posterizationOptions(el.namedItem("posterization-options").toElement()) { } QDomElement ColorCommonOptions::toXml(QDomDocument& doc, const QString& name) const { @@ -43,9 +38,7 @@ namespace output { el.setAttribute("cutMargins", m_cutMargins ? "1" : "0"); el.setAttribute("normalizeIlluminationColor", m_normalizeIllumination ? "1" : "0"); el.setAttribute("fillingColor", formatFillingColor(m_fillingColor)); - el.setAttribute("posterizeEnabled", m_posterizeEnabled ? "1" : "0"); - el.setAttribute("posterizationLevel", m_posterizationLevel); - el.setAttribute("forceBlackAndWhite", m_forceBlackAndWhite ? "1" : "0"); + el.appendChild(posterizationOptions.toXml(doc, "posterization-options")); return el; } @@ -54,9 +47,7 @@ namespace output { return (m_normalizeIllumination == other.m_normalizeIllumination) && (m_cutMargins == other.m_cutMargins) && (m_fillingColor == other.m_fillingColor) - && (m_posterizeEnabled == other.m_posterizeEnabled) - && (m_posterizationLevel == other.m_posterizationLevel) - && (m_forceBlackAndWhite == other.m_forceBlackAndWhite); + && (posterizationOptions == other.posterizationOptions); } bool ColorCommonOptions::operator!=(const ColorCommonOptions& other) const { @@ -109,27 +100,84 @@ namespace output { m_normalizeIllumination = val; } - bool ColorCommonOptions::isPosterizeEnabled() const { - return m_posterizeEnabled; + const ColorCommonOptions::PosterizationOptions& ColorCommonOptions::getPosterizationOptions() const { + return posterizationOptions; } - void ColorCommonOptions::setPosterizeEnabled(bool posterizeEnabled) { - ColorCommonOptions::m_posterizeEnabled = posterizeEnabled; + void + ColorCommonOptions::setPosterizationOptions(const ColorCommonOptions::PosterizationOptions& posterizationOptions) { + ColorCommonOptions::posterizationOptions = posterizationOptions; } - int ColorCommonOptions::getPosterizationLevel() const { - return m_posterizationLevel; +/*=============================== ColorCommonOptions::PosterizationOptions ==================================*/ + + ColorCommonOptions::PosterizationOptions::PosterizationOptions() + : enabled(false), + level(4), + normalizationEnabled(false), + forceBlackAndWhite(true) { + } + + ColorCommonOptions::PosterizationOptions::PosterizationOptions(const QDomElement& el) + : enabled(el.attribute("enabled") == "1"), + level(el.attribute("level").toInt()), + normalizationEnabled(el.attribute("normalizationEnabled") == "1"), + forceBlackAndWhite(el.attribute("forceBlackAndWhite") == "1") { + } + + QDomElement ColorCommonOptions::PosterizationOptions::toXml(QDomDocument& doc, const QString& name) const { + QDomElement el(doc.createElement(name)); + el.setAttribute("enabled", enabled ? "1" : "0"); + el.setAttribute("level", level); + el.setAttribute("normalizationEnabled", normalizationEnabled ? "1" : "0"); + el.setAttribute("forceBlackAndWhite", forceBlackAndWhite ? "1" : "0"); + + return el; + } + + bool + ColorCommonOptions::PosterizationOptions::operator==(const ColorCommonOptions::PosterizationOptions& other) const { + return (enabled == other.enabled) + && (level == other.level) + && (normalizationEnabled == other.normalizationEnabled) + && (forceBlackAndWhite == other.forceBlackAndWhite); + } + + bool + ColorCommonOptions::PosterizationOptions::operator!=(const ColorCommonOptions::PosterizationOptions& other) const { + return !(*this == other); + } + + bool ColorCommonOptions::PosterizationOptions::isEnabled() const { + return enabled; + } + + void ColorCommonOptions::PosterizationOptions::setEnabled(bool enabled) { + PosterizationOptions::enabled = enabled; + } + + int ColorCommonOptions::PosterizationOptions::getLevel() const { + return level; + } + + void ColorCommonOptions::PosterizationOptions::setLevel(int level) { + PosterizationOptions::level = level; } - void ColorCommonOptions::setPosterizationLevel(int posterizationLevel) { - ColorCommonOptions::m_posterizationLevel = posterizationLevel; + bool ColorCommonOptions::PosterizationOptions::isNormalizationEnabled() const { + return normalizationEnabled; } - bool ColorCommonOptions::isForceBlackAndWhite() const { - return m_forceBlackAndWhite; + void ColorCommonOptions::PosterizationOptions::setNormalizationEnabled(bool normalizationEnabled) { + PosterizationOptions::normalizationEnabled = normalizationEnabled; } - void ColorCommonOptions::setForceBlackAndWhite(bool forceBlackAndWhite) { - ColorCommonOptions::m_forceBlackAndWhite = forceBlackAndWhite; + bool ColorCommonOptions::PosterizationOptions::isForceBlackAndWhite() const { + return forceBlackAndWhite; } + + void ColorCommonOptions::PosterizationOptions::setForceBlackAndWhite(bool forceBlackAndWhite) { + PosterizationOptions::forceBlackAndWhite = forceBlackAndWhite; + } + } // namespace output \ No newline at end of file diff --git a/filters/output/ColorCommonOptions.h b/filters/output/ColorCommonOptions.h index b9554933f..d08e4d895 100644 --- a/filters/output/ColorCommonOptions.h +++ b/filters/output/ColorCommonOptions.h @@ -33,6 +33,41 @@ namespace output { class ColorCommonOptions { public: + class PosterizationOptions { + public: + PosterizationOptions(); + + explicit PosterizationOptions(const QDomElement& el); + + QDomElement toXml(QDomDocument& doc, const QString& name) const; + + bool operator==(const PosterizationOptions& other) const; + + bool operator!=(const PosterizationOptions& other) const; + + bool isEnabled() const; + + void setEnabled(bool enabled); + + int getLevel() const; + + void setLevel(int level); + + bool isNormalizationEnabled() const; + + void setNormalizationEnabled(bool normalizationEnabled); + + bool isForceBlackAndWhite() const; + + void setForceBlackAndWhite(bool forceBlackAndWhite); + + private: + bool enabled; + int level; + bool normalizationEnabled; + bool forceBlackAndWhite; + }; + ColorCommonOptions(); explicit ColorCommonOptions(const QDomElement& el); @@ -55,17 +90,9 @@ namespace output { bool operator!=(const ColorCommonOptions& other) const; - bool isPosterizeEnabled() const; - - void setPosterizeEnabled(bool posterizeEnabled); - - int getPosterizationLevel() const; - - void setPosterizationLevel(int posterizationLevel); - - bool isForceBlackAndWhite() const; + const PosterizationOptions& getPosterizationOptions() const; - void setForceBlackAndWhite(bool forceBlackAndWhite); + void setPosterizationOptions(const PosterizationOptions& posterizationOptions); private: static FillingColor parseFillingColor(const QString& str); @@ -76,9 +103,7 @@ namespace output { bool m_cutMargins; bool m_normalizeIllumination; FillingColor m_fillingColor; - bool m_posterizeEnabled; - int m_posterizationLevel; - bool m_forceBlackAndWhite; + PosterizationOptions posterizationOptions; }; } // namespace output #endif // ifndef OUTPUT_COLOR_GRAYSCALE_OPTIONS_H_ diff --git a/filters/output/OptionsWidget.cpp b/filters/output/OptionsWidget.cpp index 681cdcbb4..fb782fc99 100644 --- a/filters/output/OptionsWidget.cpp +++ b/filters/output/OptionsWidget.cpp @@ -624,19 +624,20 @@ namespace output { if (threshold_options_visible) { posterizeCB->setEnabled(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); posterizeOptionsWidget->setEnabled(blackWhiteOptions.getColorSegmenterOptions().isEnabled() - && colorCommonOptions.isPosterizeEnabled()); + && colorCommonOptions.getPosterizationOptions().isEnabled()); } else { posterizeCB->setEnabled(true); - posterizeOptionsWidget->setEnabled(colorCommonOptions.isPosterizeEnabled()); + posterizeOptionsWidget->setEnabled(colorCommonOptions.getPosterizationOptions().isEnabled()); } colorSegmentationCB->setChecked(blackWhiteOptions.getColorSegmenterOptions().isEnabled()); reduceNoiseSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getNoiseReduction()); redAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getRedThresholdAdjustment()); greenAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getGreenThresholdAdjustment()); blueAdjustmentSB->setValue(blackWhiteOptions.getColorSegmenterOptions().getBlueThresholdAdjustment()); - posterizeCB->setChecked(colorCommonOptions.isPosterizeEnabled()); - posterizeLevelSB->setValue(colorCommonOptions.getPosterizationLevel()); - posterizeForceBwCB->setChecked(colorCommonOptions.isForceBlackAndWhite()); + posterizeCB->setChecked(colorCommonOptions.getPosterizationOptions().isEnabled()); + posterizeLevelSB->setValue(colorCommonOptions.getPosterizationOptions().getLevel()); + posterizeNormalizationCB->setChecked(colorCommonOptions.getPosterizationOptions().isNormalizationEnabled()); + posterizeForceBwCB->setChecked(colorCommonOptions.getPosterizationOptions().isForceBlackAndWhite()); if (picture_shape_visible) { const int picture_shape_idx = pictureShapeSelector->findData(m_pictureShapeOptions.getPictureShape()); @@ -821,7 +822,9 @@ namespace output { void OptionsWidget::posterizeToggled(bool checked) { ColorCommonOptions colorCommonOptions = m_colorParams.colorCommonOptions(); - colorCommonOptions.setPosterizeEnabled(checked); + ColorCommonOptions::PosterizationOptions posterizationOptions = colorCommonOptions.getPosterizationOptions(); + posterizationOptions.setEnabled(checked); + colorCommonOptions.setPosterizationOptions(posterizationOptions); m_colorParams.setColorCommonOptions(colorCommonOptions); m_ptrSettings->setColorParams(m_pageId, m_colorParams); @@ -832,16 +835,31 @@ namespace output { void OptionsWidget::posterizeLevelChanged(int value) { ColorCommonOptions colorCommonOptions = m_colorParams.colorCommonOptions(); - colorCommonOptions.setPosterizationLevel(value); + ColorCommonOptions::PosterizationOptions posterizationOptions = colorCommonOptions.getPosterizationOptions(); + posterizationOptions.setLevel(value); + colorCommonOptions.setPosterizationOptions(posterizationOptions); m_colorParams.setColorCommonOptions(colorCommonOptions); m_ptrSettings->setColorParams(m_pageId, m_colorParams); delayedReloadRequest.start(750); } + void OptionsWidget::posterizeNormalizationToggled(bool checked) { + ColorCommonOptions colorCommonOptions = m_colorParams.colorCommonOptions(); + ColorCommonOptions::PosterizationOptions posterizationOptions = colorCommonOptions.getPosterizationOptions(); + posterizationOptions.setNormalizationEnabled(checked); + colorCommonOptions.setPosterizationOptions(posterizationOptions); + m_colorParams.setColorCommonOptions(colorCommonOptions); + m_ptrSettings->setColorParams(m_pageId, m_colorParams); + + emit reloadRequested(); + } + void OptionsWidget::posterizeForceBwToggled(bool checked) { ColorCommonOptions colorCommonOptions = m_colorParams.colorCommonOptions(); - colorCommonOptions.setForceBlackAndWhite(checked); + ColorCommonOptions::PosterizationOptions posterizationOptions = colorCommonOptions.getPosterizationOptions(); + posterizationOptions.setForceBlackAndWhite(checked); + colorCommonOptions.setPosterizationOptions(posterizationOptions); m_colorParams.setColorCommonOptions(colorCommonOptions); m_ptrSettings->setColorParams(m_pageId, m_colorParams); @@ -934,6 +952,10 @@ namespace output { posterizeLevelSB, SIGNAL(valueChanged(int)), this, SLOT(posterizeLevelChanged(int)) ); + connect( + posterizeNormalizationCB, SIGNAL(clicked(bool)), + this, SLOT(posterizeNormalizationToggled(bool)) + ); connect( posterizeForceBwCB, SIGNAL(clicked(bool)), this, SLOT(posterizeForceBwToggled(bool)) @@ -1079,6 +1101,10 @@ namespace output { posterizeLevelSB, SIGNAL(valueChanged(int)), this, SLOT(posterizeLevelChanged(int)) ); + disconnect( + posterizeNormalizationCB, SIGNAL(clicked(bool)), + this, SLOT(posterizeNormalizationToggled(bool)) + ); disconnect( posterizeForceBwCB, SIGNAL(clicked(bool)), this, SLOT(posterizeForceBwToggled(bool)) diff --git a/filters/output/OptionsWidget.h b/filters/output/OptionsWidget.h index ab97fef82..1002f1e26 100644 --- a/filters/output/OptionsWidget.h +++ b/filters/output/OptionsWidget.h @@ -112,6 +112,8 @@ namespace output { void posterizeLevelChanged(int value); + void posterizeNormalizationToggled(bool checked); + void posterizeForceBwToggled(bool checked); void cutMarginsToggled(bool checked); diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index ef244368e..f18c91cf0 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -725,9 +725,11 @@ namespace output { status.throwIfCancelled(); if (render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - segmented_image = ColorTable(segmented_image).posterize(posterize_level, force_bw).getImage(); + segmented_image = posterizeImage(segmented_image, outsideBackgroundColor); + + if (dbg) { + dbg->add(segmented_image, "posterized"); + } status.throwIfCancelled(); } @@ -934,11 +936,7 @@ namespace output { status.throwIfCancelled(); if (render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - segmented_image = ColorTable(segmented_image) - .posterize(posterize_level, force_bw) - .getImage(); + segmented_image = posterizeImage(segmented_image, outsideBackgroundColor); if (dbg) { dbg->add(segmented_image, "posterized"); @@ -1038,9 +1036,7 @@ namespace output { } if (!render_params.mixedOutput() && render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - dst = ColorTable(dst).posterize(posterize_level, force_bw).getImage(); + dst = posterizeImage(dst); if (dbg) { dbg->add(dst, "posterized"); @@ -1665,9 +1661,7 @@ namespace output { status.throwIfCancelled(); if (render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - segmented_image = ColorTable(segmented_image).posterize(posterize_level, force_bw).getImage(); + segmented_image = posterizeImage(segmented_image, outsideBackgroundColor); if (dbg) { dbg->add(segmented_image, "posterized"); @@ -1845,11 +1839,7 @@ namespace output { status.throwIfCancelled(); if (render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - segmented_image = ColorTable(segmented_image) - .posterize(posterize_level, force_bw) - .getImage(); + segmented_image = posterizeImage(segmented_image, outsideBackgroundColor); if (dbg) { dbg->add(segmented_image, "posterized"); @@ -1927,9 +1917,7 @@ namespace output { } if (!render_params.mixedOutput() && render_params.posterize()) { - const int posterize_level = m_colorParams.colorCommonOptions().getPosterizationLevel(); - const bool force_bw = m_colorParams.colorCommonOptions().isForceBlackAndWhite(); - dewarped = ColorTable(dewarped).posterize(posterize_level, force_bw).getImage(); + dewarped = posterizeImage(dewarped); if (dbg) { dbg->add(dewarped, "posterized"); @@ -2862,4 +2850,16 @@ namespace output { ).getImage(); } } + + QImage OutputGenerator::posterizeImage(const QImage& image, const QColor& background_color) const { + const ColorCommonOptions::PosterizationOptions& posterizationOptions + = m_colorParams.colorCommonOptions().getPosterizationOptions(); + + return ColorTable(image).posterize( + posterizationOptions.getLevel(), + posterizationOptions.isNormalizationEnabled(), + posterizationOptions.isForceBlackAndWhite(), + 0, qRound(background_color.lightnessF() * 255) + ).getImage(); + } } // namespace output \ No newline at end of file diff --git a/filters/output/OutputGenerator.h b/filters/output/OutputGenerator.h index cb0a45f1e..79dc9219a 100644 --- a/filters/output/OutputGenerator.h +++ b/filters/output/OutputGenerator.h @@ -319,6 +319,8 @@ namespace output { QImage segmentImage(const BinaryImage& image, const QImage& color_image) const; + QImage posterizeImage(const QImage& image, const QColor& background_color = Qt::white) const; + Dpi m_dpi; ColorParams m_colorParams; SplittingOptions m_splittingOptions; diff --git a/filters/output/RenderParams.cpp b/filters/output/RenderParams.cpp index 7bdceef49..a622ec39c 100644 --- a/filters/output/RenderParams.cpp +++ b/filters/output/RenderParams.cpp @@ -52,12 +52,12 @@ namespace output { } if (blackWhiteOptions.getColorSegmenterOptions().isEnabled()) { m_mask |= COLOR_SEGMENTATION; - if (colorCommonOptions.isPosterizeEnabled()) { + if (colorCommonOptions.getPosterizationOptions().isEnabled()) { m_mask |= POSTERIZE; } } } else { - if (colorCommonOptions.isPosterizeEnabled()) { + if (colorCommonOptions.getPosterizationOptions().isEnabled()) { m_mask |= POSTERIZE; } } diff --git a/filters/output/ui/OutputOptionsWidget.ui b/filters/output/ui/OutputOptionsWidget.ui index 03526dd1a..9724f6112 100644 --- a/filters/output/ui/OutputOptionsWidget.ui +++ b/filters/output/ui/OutputOptionsWidget.ui @@ -88,7 +88,7 @@ 0 0 228 - 1165 + 1190 @@ -852,6 +852,55 @@ + + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + + + + + + + Normalize + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + diff --git a/imageproc/ColorTable.cpp b/imageproc/ColorTable.cpp index cb41ffd96..49795eb4e 100644 --- a/imageproc/ColorTable.cpp +++ b/imageproc/ColorTable.cpp @@ -1,6 +1,7 @@ #include #include +#include #include "ColorTable.h" #include "BinaryImage.h" @@ -49,18 +50,23 @@ namespace imageproc { } - ColorTable& ColorTable::posterize(const int level, const bool forceBlackAndWhite) { + ColorTable& ColorTable::posterize(const int level, + bool normalize, + const bool forceBlackAndWhite, + const int normalizeBlackLevel, + const int normalizeWhiteLevel) { if ((level < 2) || (level > 255)) { throw std::invalid_argument("error: level must be a value between 2 and 255 inclusive"); } - if (level == 255) { + if ((level == 255) && !normalize && !forceBlackAndWhite) { return *this; } std::unordered_map oldToNewColorMap; - size_t newColorsCount; + size_t newColorTableSize; { + // Get the palette with statistics. std::unordered_map paletteStatMap; switch (image.format()) { case QImage::Format_Indexed8: @@ -74,31 +80,27 @@ namespace imageproc { return *this; } + // We have to normalize palette in order posterize to work with pale images. + std::unordered_map colorToNormalizedMap + = normalizePalette(paletteStatMap, normalizeBlackLevel, normalizeWhiteLevel); + + // Build color groups resulted from splitting RGB space std::unordered_map> groupMap; const double levelStride = 255.0 / level; for (const auto& colorAndStat : paletteStatMap) { const uint32_t color = colorAndStat.first; + const uint32_t normalized_color = colorToNormalizedMap[color]; - auto redGroupIdx = static_cast(qRed(color) / levelStride); - auto blueGroupIdx = static_cast(qGreen(color) / levelStride); - auto greenGroupIdx = static_cast(qBlue(color) / levelStride); + auto redGroupIdx = static_cast(qRed(normalized_color) / levelStride); + auto blueGroupIdx = static_cast(qGreen(normalized_color) / levelStride); + auto greenGroupIdx = static_cast(qBlue(normalized_color) / levelStride); auto group = static_cast((redGroupIdx << 16) | (greenGroupIdx << 8) | (blueGroupIdx)); - groupMap[group].push_back(color); - } - - if (forceBlackAndWhite) { - const int maxLevelIdx = level - 1; - auto whiteGroup = static_cast((maxLevelIdx << 16) | (maxLevelIdx << 8) | (maxLevelIdx)); - uint32_t blackGroup = 0; - - groupMap[whiteGroup].push_back(0xffffffffu); - groupMap[blackGroup].push_back(0xff000000u); - paletteStatMap[0xffffffffu] = std::numeric_limits::max(); - paletteStatMap[0xff000000u] = std::numeric_limits::max(); + groupMap[group].push_back(color); } + // Find the most often occurring color in the group and map the other colors in the group to this. for (const auto& groupAndColors : groupMap) { const std::list& colors = groupAndColors.second; assert(!colors.empty()); @@ -110,18 +112,27 @@ namespace imageproc { } } - for (uint32_t color : colors) { - oldToNewColorMap[color] = mostOftenColorInGroup; + if (forceBlackAndWhite) { + if (normalize) { + colorToNormalizedMap[0xff000000u] = 0xff000000u; + colorToNormalizedMap[0xffffffffu] = 0xffffffffu; + } + makeGrayBlackAndWhiteInPlace(mostOftenColorInGroup, colorToNormalizedMap[mostOftenColorInGroup]); + } + + for (const uint32_t& color : colors) { + oldToNewColorMap[color] = normalize ? colorToNormalizedMap[mostOftenColorInGroup] + : mostOftenColorInGroup; } } - newColorsCount = groupMap.size(); + newColorTableSize = groupMap.size(); } if (image.format() == QImage::Format_Indexed8) { remapColorsInIndexedImage(oldToNewColorMap); } else { - if (newColorsCount <= 256) { + if (newColorTableSize <= 256) { buildIndexedImageFromRgb(oldToNewColorMap); } else { remapColorsInRgbImage(oldToNewColorMap); @@ -263,4 +274,97 @@ namespace imageproc { image = image.convertToFormat(QImage::Format_Indexed8, newColorTable); } + std::unordered_map ColorTable::normalizePalette( + const std::unordered_map& palette, + const int normalizeBlackLevel, + const int normalizeWhiteLevel) const { + const int pixelCount = image.width() * image.height(); + const double threshold = 0.0005; // must be larger then (1 / 256) + + int min_level = 255; + int max_level = 0; + { + // Build RGB histogram from colors with statistics + int red_hist[256] = { }; + int green_hist[256] = { }; + int blue_hist[256] = { }; + for (const auto& colorAndStat : palette) { + const uint32_t color = colorAndStat.first; + const int statistics = colorAndStat.second; + + if (color == 0xff000000u) { + red_hist[normalizeBlackLevel] += statistics; + green_hist[normalizeBlackLevel] += statistics; + blue_hist[normalizeBlackLevel] += statistics; + continue; + } + if (color == 0xffffffffu) { + red_hist[normalizeWhiteLevel] += statistics; + green_hist[normalizeWhiteLevel] += statistics; + blue_hist[normalizeWhiteLevel] += statistics; + continue; + } + + red_hist[qRed(color)] += statistics; + green_hist[qGreen(color)] += statistics; + blue_hist[qBlue(color)] += statistics; + } + + // Find the max and min levels discarding a noise + for (int level = 0; level < 256; ++level) { + if (((double(red_hist[level]) / pixelCount) >= threshold) + || ((double(green_hist[level]) / pixelCount) >= threshold) + || ((double(blue_hist[level]) / pixelCount) >= threshold)) { + if (level < min_level) { + min_level = level; + } + if (level > max_level) { + max_level = level; + } + } + } + + assert(max_level >= min_level); + } + + std::unordered_map colorToNormalizedMap; + for (const auto& colorAndStat : palette) { + const uint32_t color = colorAndStat.first; + if (color == 0xff000000u) { + colorToNormalizedMap[0xff000000u] = 0xff000000u; + continue; + } + if (color == 0xffffffffu) { + colorToNormalizedMap[0xffffffffu] = 0xffffffffu; + continue; + } + + int normalizedRed = qRound((double(qRed(color) - min_level) / (max_level - min_level)) * 255); + int normalizedGreen = qRound((double(qGreen(color) - min_level) / (max_level - min_level)) * 255); + int normalizedBlue = qRound((double(qBlue(color) - min_level) / (max_level - min_level)) * 255); + normalizedRed = qBound(0, normalizedRed, 255); + normalizedGreen = qBound(0, normalizedGreen, 255); + normalizedBlue = qBound(0, normalizedBlue, 255); + + colorToNormalizedMap[color] = qRgb(normalizedRed, normalizedGreen, normalizedBlue); + } + + return colorToNormalizedMap; + } + + void ColorTable::makeGrayBlackAndWhiteInPlace(QRgb& rgb, const QRgb& normalized) const { + QColor color = QColor(normalized).toHsv(); + + const bool isGray = (color.saturationF() * color.valueF()) < 0.2; + if (isGray) { + const int grayLevel = qGray(normalized); + const QColor grayColor = QColor(grayLevel, grayLevel, grayLevel).toHsv(); + if (grayColor.lightnessF() <= 0.5) { + rgb = 0xff000000u; + } else if (color.lightnessF() >= 0.8) { + rgb = 0xffffffffu; + } + } + } + } // namespace imageproc \ No newline at end of file diff --git a/imageproc/ColorTable.h b/imageproc/ColorTable.h index 3f493be63..83d21b5ee 100644 --- a/imageproc/ColorTable.h +++ b/imageproc/ColorTable.h @@ -14,7 +14,11 @@ namespace imageproc { public: explicit ColorTable(const QImage& image); - ColorTable& posterize(int level, bool forceBlackAndWhite = false); + ColorTable& posterize(int level, + bool normalize = false, + bool forceBlackAndWhite = false, + int normalizeBlackLevel = 0, + int normalizeWhiteLevel = 255); QVector getPalette() const; @@ -32,6 +36,12 @@ namespace imageproc { void remapColorsInRgbImage(const std::unordered_map& colorMap); void buildIndexedImageFromRgb(const std::unordered_map& colorMap); + + std::unordered_map normalizePalette(const std::unordered_map& palette, + int normalizeBlackLevel = 0, + int normalizeWhiteLevel = 255) const; + + void makeGrayBlackAndWhiteInPlace(QRgb& rgb, const QRgb& normalized) const; }; } diff --git a/ui/DefaultParamsDialog.ui b/ui/DefaultParamsDialog.ui index 9f5e7d43b..d0502ba2f 100644 --- a/ui/DefaultParamsDialog.ui +++ b/ui/DefaultParamsDialog.ui @@ -112,7 +112,7 @@ QTabWidget::North - 0 + 5 true @@ -1768,9 +1768,9 @@ 0 - 0 + -27 646 - 452 + 477 @@ -2333,6 +2333,55 @@ + + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 15 + 1 + + + + + + + + + + + + + + Normalize + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + From 9467b6ca384158c1534f850c3646ca0146d2bb66 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Wed, 14 Feb 2018 23:17:46 +0300 Subject: [PATCH 16/28] Improved: Follow page button is no more activated automatically on switching a filter if the button was inactivated before. Instead of that behaviour ST now saves the sliders positions. Note: The old behaviour caused some problems of loosing the position of the page sequence worked on if not natural sorting enabled. --- MainWindow.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 4201e3b5e..9bdbbfa5c 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1143,9 +1143,16 @@ void MainWindow::filterSelectionChanged(const QItemSelection& selected) { } // Otherwise probably no project is loaded. } - focusButton->setChecked(true); // Should go before resetThumbSequence(). + const int hor_scroll_bar_pos = thumbView->horizontalScrollBar()->value(); + const int ver_scroll_bar_pos = thumbView->verticalScrollBar()->value(); + resetThumbSequence(currentPageOrderProvider()); + if (!focusButton->isChecked()) { + thumbView->horizontalScrollBar()->setValue(hor_scroll_bar_pos); + thumbView->verticalScrollBar()->setValue(ver_scroll_bar_pos); + } + // load default settings for all the pages for (const PageInfo& pageInfo : m_ptrThumbSequence->toPageSequence()) { for (int i = 0; i < m_ptrStages->count(); i++) { From 7af2710f8307801dff1101eb85a0600613b1c91e Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Wed, 14 Feb 2018 23:42:48 +0300 Subject: [PATCH 17/28] ~ [ui] Increased the height of horizontal sliders. --- resources/dark_scheme/stylesheet.qss | 6 +++--- resources/light_scheme/stylesheet.qss | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/dark_scheme/stylesheet.qss b/resources/dark_scheme/stylesheet.qss index d9c8de6fe..987023928 100644 --- a/resources/dark_scheme/stylesheet.qss +++ b/resources/dark_scheme/stylesheet.qss @@ -557,19 +557,19 @@ QAbstractSpinBox::up-arrow:pressed { } QSlider:horizontal { - margin: 7px 1px 9px 1px; + margin: 9px 1px 9px 1px; } QSlider::groove:horizontal { border: none; - height: 3px; + height: 6px; background-color: #757575; } QSlider::handle:horizontal { height: 19px; width: 12px; - margin: -19px 0px; + margin: -21px 0px; image: url(:/dark_scheme/icon_slider_handle.png); } diff --git a/resources/light_scheme/stylesheet.qss b/resources/light_scheme/stylesheet.qss index 15899895b..a98040c8f 100644 --- a/resources/light_scheme/stylesheet.qss +++ b/resources/light_scheme/stylesheet.qss @@ -558,19 +558,19 @@ QAbstractSpinBox::up-arrow:pressed { } QSlider:horizontal { - margin: 7px 1px 9px 1px; + margin: 9px 1px 9px 1px; } QSlider::groove:horizontal { border: none; - height: 3px; + height: 6px; background-color: #cccccc; } QSlider::handle:horizontal { height: 19px; width: 12px; - margin: -19px 0px; + margin: -21px 0px; image: url(:/light_scheme/icon_slider_handle.png); } From 22f6d0d530378850449c9c6aef11c878b704f9b9 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Wed, 14 Feb 2018 23:59:22 +0300 Subject: [PATCH 18/28] ~ Reserve pure black and white for color images if filling with white selected. It allows to extrapolate these margins in an image editor like Photoshop by using select by color feature. --- filters/output/OutputGenerator.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index f18c91cf0..43aba45a7 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -977,6 +977,9 @@ namespace output { outsideBackgroundColor = Qt::white; } else if (m_colorParams.colorCommonOptions().getFillingColor() == FILL_WHITE) { outsideBackgroundColor = input.isBlackOnWhite() ? Qt::white : Qt::black; + if (!render_params.needBinarization()) { + reserveBlackAndWhite(maybe_normalized); + } } fillMarginsInPlace(maybe_normalized, normalize_illumination_crop_area, outsideBackgroundColor); dst.fill(outsideBackgroundColor); @@ -1866,6 +1869,9 @@ namespace output { outsideBackgroundColor = Qt::white; } else if (m_colorParams.colorCommonOptions().getFillingColor() == FILL_WHITE) { outsideBackgroundColor = input.isBlackOnWhite() ? Qt::white : Qt::black; + if (!render_params.needBinarization()) { + reserveBlackAndWhite(dewarped); + } } fillMarginsInPlace(dewarped, dewarping_content_area_mask, outsideBackgroundColor); From ee6483dcc93993a934cd9ca57fefc784c7324842 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 16 Feb 2018 01:33:32 +0300 Subject: [PATCH 19/28] Collapsible filter options. Now group boxes containing filter options can be collapsed/expanded. The collapse status is preserved between restarts of the application. --- CMakeLists.txt | 3 +- CollapsibleGroupBox.cpp | 217 ++++++++++++++++++ CollapsibleGroupBox.h | 83 +++++++ README.md | 9 +- filters/deskew/ui/DeskewOptionsWidget.ui | 10 +- .../ui/OrientationOptionsWidget.ui | 10 +- filters/output/ui/OutputOptionsWidget.ui | 30 ++- .../page_layout/ui/PageLayoutOptionsWidget.ui | 14 +- .../page_split/ui/PageSplitOptionsWidget.ui | 12 +- .../ui/SelectContentOptionsWidget.ui | 12 +- 10 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 CollapsibleGroupBox.cpp create mode 100644 CollapsibleGroupBox.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ee65cf899..028f5501f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -582,7 +582,8 @@ SET( MainWindow.cpp MainWindow.h main.cpp StatusBarPanel.cpp StatusBarPanel.h - DefaultParamsDialog.cpp DefaultParamsDialog.h) + DefaultParamsDialog.cpp DefaultParamsDialog.h + CollapsibleGroupBox.cpp CollapsibleGroupBox.h) SET( cli_only_sources diff --git a/CollapsibleGroupBox.cpp b/CollapsibleGroupBox.cpp new file mode 100644 index 000000000..51a188bbd --- /dev/null +++ b/CollapsibleGroupBox.cpp @@ -0,0 +1,217 @@ + +#include "CollapsibleGroupBox.h" +#include +#include +#include +#include + +CollapsibleGroupBox::CollapsibleGroupBox(QWidget* parent) + : QGroupBox(parent) { + initialize(); +} + +CollapsibleGroupBox::CollapsibleGroupBox(const QString& title, QWidget* parent) + : QGroupBox(title, parent) { + initialize(); +} + +void CollapsibleGroupBox::initialize() { + collapseIcon.addPixmap( + QPixmap(QString::fromLatin1(":/icons/minus-16.png")) + ); + expandIcon.addPixmap( + QPixmap(QString::fromLatin1(":/icons/plus-16.png")) + ); + collapseButton = new QToolButton(this); + collapseButton->setObjectName("collapseButton"); + collapseButton->setAutoRaise(true); + collapseButton->setFixedSize(14, 14); + collapseButton->setIconSize(QSize(12, 12)); + collapseButton->setIcon(collapseIcon); + setFocusProxy(collapseButton); + setFocusPolicy(Qt::StrongFocus); + + connect(collapseButton, &QAbstractButton::clicked, this, &CollapsibleGroupBox::toggleCollapsed); + connect(this, &QGroupBox::toggled, this, &CollapsibleGroupBox::checkToggled); + connect(this, &QGroupBox::clicked, this, &CollapsibleGroupBox::checkClicked); +} + +void CollapsibleGroupBox::setCollapsed(const bool collapse) { + const bool changed = (collapse != collapsed); + + if (changed) { + collapsed = collapse; + collapseButton->setIcon(collapse ? expandIcon : collapseIcon); + + updateWidgets(); + + emit collapsedStateChanged(isCollapsed()); + } +} + +bool CollapsibleGroupBox::isCollapsed() const { + return collapsed; +} + +void CollapsibleGroupBox::checkToggled(bool) { + collapseButton->setEnabled(true); +} + +void CollapsibleGroupBox::checkClicked(bool checked) { + if (checked && isCollapsed()) { + setCollapsed(false); + } else if (!checked && !isCollapsed()) { + setCollapsed(true); + } +} + +void CollapsibleGroupBox::toggleCollapsed() { + // verify if sender is this group box's collapse button + auto* sender = dynamic_cast(QObject::sender()); + const bool isSenderCollapseButton = (sender && (sender == collapseButton)); + + if (isSenderCollapseButton) { + setCollapsed(!isCollapsed()); + } +} + +void CollapsibleGroupBox::updateWidgets() { + const ScopedIncDec guard(ignoreVisibilityEvents); + + if (collapsed) { + for (QObject* child : children()) { + auto* widget = dynamic_cast(child); + if (widget && (widget != collapseButton) && widget->isVisible()) { + collapsedWidgets.insert(widget); + widget->hide(); + } + } + } else { + for (QObject* child : children()) { + auto* widget = dynamic_cast(child); + if (widget && (widget != collapseButton) && (collapsedWidgets.find(widget) != collapsedWidgets.end())) { + collapsedWidgets.erase(widget); + widget->show(); + } + } + } +} + +void CollapsibleGroupBox::showEvent(QShowEvent* event) { + // initialize widget on first show event only + if (shown) { + event->accept(); + return; + } + shown = true; + + loadState(); + + QWidget::showEvent(event); +} + +void CollapsibleGroupBox::changeEvent(QEvent* event) { + QGroupBox::changeEvent(event); + + if ((event->type() == QEvent::EnabledChange) && isEnabled()) { + collapseButton->setEnabled(true); + } +} + +void CollapsibleGroupBox::childEvent(QChildEvent* event) { + auto* childWidget = dynamic_cast(event->child()); + if (childWidget && (event)->type() == QEvent::ChildAdded) { + if (collapsed) { + if (childWidget->isVisible()) { + collapsedWidgets.insert(childWidget); + childWidget->hide(); + } + } + + childWidget->installEventFilter(this); + } + + QGroupBox::childEvent(event); +} + +bool CollapsibleGroupBox::eventFilter(QObject* watched, QEvent* event) { + if (collapsed && !ignoreVisibilityEvents) { + auto* childWidget = dynamic_cast(watched); + if (childWidget) { + if (event->type() == QEvent::ShowToParent) { + const ScopedIncDec guard(ignoreVisibilityEvents); + + collapsedWidgets.insert(childWidget); + childWidget->hide(); + } else if (event->type() == QEvent::HideToParent) { + collapsedWidgets.erase(childWidget); + } + } + } + + return QObject::eventFilter(watched, event); +} + +CollapsibleGroupBox::~CollapsibleGroupBox() { + saveState(); +} + +void CollapsibleGroupBox::loadState() { + if (!isEnabled()) { + return; + } + + const QString key = getSettingsKey(); + if (key.isEmpty()) { + return; + } + + setUpdatesEnabled(false); + + QSettings settings; + + if (isCheckable()) { + QVariant val = settings.value(key + "/checked"); + if (!val.isNull()) { + setChecked(val.toBool()); + } + } + + { + QVariant val = settings.value(key + "/collapsed"); + if (!val.isNull()) { + setCollapsed(val.toBool()); + } + } + + setUpdatesEnabled(true); +} + +void CollapsibleGroupBox::saveState() { + if (!shown || !isEnabled()) { + return; + } + + const QString key = getSettingsKey(); + if (key.isEmpty()) { + return; + } + + QSettings settings; + + if (isCheckable()) { + settings.setValue(key + "/checked", isChecked()); + } + settings.setValue(key + "/collapsed", isCollapsed()); +} + +QString CollapsibleGroupBox::getSettingsKey() const { + if (objectName().isEmpty()) { + return QString(); + } + + QString saveKey = '/' + objectName(); + saveKey = "CollapsibleGroupBox" + saveKey; + return saveKey; +} + diff --git a/CollapsibleGroupBox.h b/CollapsibleGroupBox.h new file mode 100644 index 000000000..05368943d --- /dev/null +++ b/CollapsibleGroupBox.h @@ -0,0 +1,83 @@ + +#ifndef SCANTAILOR_COLLAPSIBLEGROUPBOX_H +#define SCANTAILOR_COLLAPSIBLEGROUPBOX_H + + +#include +#include +#include + +class CollapsibleGroupBox : public QGroupBox { +Q_OBJECT + + /** + * The collapsed state of this group box. If it is set to true, all content is hidden + * if it is set to false all content is shown. + */ + Q_PROPERTY(bool collapsed READ isCollapsed WRITE setCollapsed USER true) + +public: + explicit CollapsibleGroupBox(QWidget* parent = nullptr); + + explicit CollapsibleGroupBox(const QString& title, QWidget* parent = nullptr); + + ~CollapsibleGroupBox() override; + + /** + * Returns the current collapsed state of this group box. + */ + bool isCollapsed() const; + + /** + * Collapse or expand this group box. + * + * \param collapse Will collapse on true and expand on false + */ + void setCollapsed(bool collapse); + +signals: + + /** Signal emitted when the group box collapsed/expanded state is changed, and when first shown */ + void collapsedStateChanged(bool collapsed); + +public slots: + + void checkToggled(bool); + + void checkClicked(bool checked); + + void toggleCollapsed(); + +protected: + void updateWidgets(); + + void showEvent(QShowEvent* event) override; + + void changeEvent(QEvent* event) override; + + void childEvent(QChildEvent *event) override; + + bool eventFilter(QObject* watched, QEvent* event) override; + + void initialize(); + + void loadState(); + + void saveState(); + + QString getSettingsKey() const; + +private: + bool collapsed = false; + bool shown = false; + QToolButton* collapseButton = nullptr; + + QIcon collapseIcon; + QIcon expandIcon; + + int ignoreVisibilityEvents = 0; + std::unordered_set collapsedWidgets; +}; + + +#endif //SCANTAILOR_COLLAPSIBLEGROUPBOX_H diff --git a/README.md b/README.md index a643f9112..8a22aaf9a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ brings new ones and fixes. * [Measurement units system](#measurement-units-system) * [Status bar panel](#status-bar-panel) * [Default parameters](#default-parameters) + * [Collapsible filter options](#collapsible-filter-options) * [Building](#building) Description @@ -169,7 +170,7 @@ Features 5. Optimized memory usage on the output stage. 6. Reworking on [multi column thumbnails view](#multi-column-thumbnails-view-reworked) feature from ver. Enhanced. - Now thumbnails is shown evenly. + Now thumbnails are shown evenly. 7. Added option to control highlighting (with red asterisks) the thumbnails of pages with high deviation. The option refreshes the thumbnails instantly. @@ -304,8 +305,12 @@ Features 1. There are two default profiles: "Default" and "Source". The "Default" profile represents default ST filter settings, the "Source" one represents the settings giving the source as output without any changes. 2. A user can create its own profiles. User profiles are stored in config/profiles folder. - 3. The system consider the units settings from the [measurement units system](#measurement-units-system). Units are stored in the profile and ST converts the values needed at processing. + 3. The system consider the units settings from the [measurement units system](#measurement-units-system). Units are stored in the profile and ST automatically converts the values if needed. + * ##### Collapsible filter options. + Now group boxes containing filter options can be collapsed/expanded. + The collapse status is preserved between restarts of the application. + Building ---------- diff --git a/filters/deskew/ui/DeskewOptionsWidget.ui b/filters/deskew/ui/DeskewOptionsWidget.ui index 1e1dec90d..ba06cf307 100644 --- a/filters/deskew/ui/DeskewOptionsWidget.ui +++ b/filters/deskew/ui/DeskewOptionsWidget.ui @@ -15,7 +15,7 @@ - + Deskew @@ -148,6 +148,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
diff --git a/filters/fix_orientation/ui/OrientationOptionsWidget.ui b/filters/fix_orientation/ui/OrientationOptionsWidget.ui index efcc77b75..c96c7d41d 100644 --- a/filters/fix_orientation/ui/OrientationOptionsWidget.ui +++ b/filters/fix_orientation/ui/OrientationOptionsWidget.ui @@ -15,7 +15,7 @@ - + Rotate @@ -216,6 +216,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
diff --git a/filters/output/ui/OutputOptionsWidget.ui b/filters/output/ui/OutputOptionsWidget.ui index 9724f6112..2c46f948a 100644 --- a/filters/output/ui/OutputOptionsWidget.ui +++ b/filters/output/ui/OutputOptionsWidget.ui @@ -93,7 +93,7 @@ - + @@ -201,7 +201,7 @@ - + Mode @@ -249,7 +249,7 @@
- + @@ -359,7 +359,7 @@ - + Filling @@ -414,7 +414,7 @@ - + Threshold @@ -478,7 +478,7 @@ - + @@ -972,7 +972,7 @@ - + Picture Shape @@ -1118,7 +1118,7 @@ - + @@ -1252,7 +1252,7 @@ - + Despeckling @@ -1440,7 +1440,7 @@ - + Depth perception @@ -1511,7 +1511,7 @@ - + Dewarping @@ -1618,6 +1618,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
diff --git a/filters/page_layout/ui/PageLayoutOptionsWidget.ui b/filters/page_layout/ui/PageLayoutOptionsWidget.ui index a8bf51951..7738187c6 100644 --- a/filters/page_layout/ui/PageLayoutOptionsWidget.ui +++ b/filters/page_layout/ui/PageLayoutOptionsWidget.ui @@ -6,7 +6,7 @@ 0 0 - 262 + 235 496 @@ -15,7 +15,7 @@ - + Margins @@ -260,7 +260,7 @@ - + Alignment @@ -659,6 +659,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
topBottomLink topMarginSpinBox diff --git a/filters/page_split/ui/PageSplitOptionsWidget.ui b/filters/page_split/ui/PageSplitOptionsWidget.ui index f9af8c5a8..da183ec43 100644 --- a/filters/page_split/ui/PageSplitOptionsWidget.ui +++ b/filters/page_split/ui/PageSplitOptionsWidget.ui @@ -21,7 +21,7 @@ - + Page Layout @@ -179,7 +179,7 @@ - + Split Line @@ -244,6 +244,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
diff --git a/filters/select_content/ui/SelectContentOptionsWidget.ui b/filters/select_content/ui/SelectContentOptionsWidget.ui index 6e342a5d3..fc25a9fc9 100644 --- a/filters/select_content/ui/SelectContentOptionsWidget.ui +++ b/filters/select_content/ui/SelectContentOptionsWidget.ui @@ -15,7 +15,7 @@ - + Page Box @@ -191,7 +191,7 @@ - + Content Box @@ -293,6 +293,14 @@ + + + CollapsibleGroupBox + QGroupBox +
CollapsibleGroupBox.h
+ 1 +
+
From 19173c98b04453b55b4b8b4a67c7186c5bc7b198 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Fri, 16 Feb 2018 15:44:37 +0300 Subject: [PATCH 20/28] Added "Higher search sensitivity" option into picture shape options. It gives the same search result as they were in Enhanced (& Featured) versions. --- DefaultParamsDialog.cpp | 5 ++- filters/output/OptionsWidget.cpp | 23 ++++++++++++- filters/output/OptionsWidget.h | 2 ++ filters/output/OutputGenerator.cpp | 11 ++++++- filters/output/OutputGenerator.h | 6 ++-- filters/output/PictureShapeOptions.cpp | 18 +++++++++-- filters/output/PictureShapeOptions.h | 6 ++++ filters/output/ui/OutputOptionsWidget.ui | 41 ++++++++++++++++++++++-- imageproc/Grayscale.h | 2 +- ui/DefaultParamsDialog.ui | 41 ++++++++++++++++++++++-- 10 files changed, 141 insertions(+), 14 deletions(-) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 3418aeed6..6c1e7aa1c 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -304,6 +304,7 @@ void DefaultParamsDialog::updateOutputDisplay(const DefaultParams::OutputParams& pictureShapeSelector->findData(pictureShapeOptions.getPictureShape()) ); pictureShapeSensitivitySB->setValue(pictureShapeOptions.getSensitivity()); + higherSearchSensitivityCB->setChecked(pictureShapeOptions.isHigherSearchSensitivity()); int dpiIndex = dpiSelector->findData(QString::number(params.getDpi().vertical())); if (dpiIndex != -1) { @@ -581,8 +582,9 @@ void DefaultParamsDialog::thresholdMethodChanged(const int idx) { } void DefaultParamsDialog::pictureShapeChanged(const int idx) { - const auto shapeMode = static_cast(colorModeSelector->itemData(idx).toInt()); + const auto shapeMode = static_cast(pictureShapeSelector->itemData(idx).toInt()); pictureShapeSensitivityOptions->setEnabled(shapeMode == RECTANGULAR_SHAPE); + higherSearchSensitivityCB->setEnabled(shapeMode != OFF_SHAPE); } void DefaultParamsDialog::equalizeIlluminationToggled(const bool checked) { @@ -749,6 +751,7 @@ std::unique_ptr DefaultParamsDialog::buildParams() const { PictureShapeOptions pictureShapeOptions; pictureShapeOptions.setPictureShape(static_cast(pictureShapeSelector->currentData().toInt())); pictureShapeOptions.setSensitivity(pictureShapeSensitivitySB->value()); + pictureShapeOptions.setHigherSearchSensitivity(higherSearchSensitivityCB->isChecked()); DewarpingOptions dewarpingOptions; dewarpingOptions.setDewarpingMode(static_cast(dewarpingModeCB->currentData().toInt())); diff --git a/filters/output/OptionsWidget.cpp b/filters/output/OptionsWidget.cpp index fb782fc99..107c0aa26 100644 --- a/filters/output/OptionsWidget.cpp +++ b/filters/output/OptionsWidget.cpp @@ -163,9 +163,13 @@ namespace output { } void OptionsWidget::pictureShapeChanged(const int idx) { - m_pictureShapeOptions.setPictureShape((PictureShape) (pictureShapeSelector->itemData(idx).toInt())); + const auto shapeMode = static_cast(pictureShapeSelector->itemData(idx).toInt()); + m_pictureShapeOptions.setPictureShape(shapeMode); m_ptrSettings->setPictureShapeOptions(m_pageId, m_pictureShapeOptions); + pictureShapeSensitivityOptions->setVisible(shapeMode == RECTANGULAR_SHAPE); + higherSearchSensitivityCB->setVisible(shapeMode != OFF_SHAPE); + emit reloadRequested(); } @@ -176,6 +180,13 @@ namespace output { delayedReloadRequest.start(750); } + void OptionsWidget::higherSearchSensivityToggled(const bool checked) { + m_pictureShapeOptions.setHigherSearchSensitivity(checked); + m_ptrSettings->setPictureShapeOptions(m_pageId, m_pictureShapeOptions); + + emit reloadRequested(); + } + void OptionsWidget::cutMarginsToggled(const bool checked) { ColorCommonOptions colorCommonOptions(m_colorParams.colorCommonOptions()); colorCommonOptions.setCutMargins(checked); @@ -644,6 +655,8 @@ namespace output { pictureShapeSelector->setCurrentIndex(picture_shape_idx); pictureShapeSensitivitySB->setValue(m_pictureShapeOptions.getSensitivity()); pictureShapeSensitivityOptions->setVisible(m_pictureShapeOptions.getPictureShape() == RECTANGULAR_SHAPE); + higherSearchSensitivityCB->setChecked(m_pictureShapeOptions.isHigherSearchSensitivity()); + higherSearchSensitivityCB->setVisible(m_pictureShapeOptions.getPictureShape() != OFF_SHAPE); } if (threshold_options_visible) { @@ -923,6 +936,10 @@ namespace output { pictureShapeSensitivitySB, SIGNAL(valueChanged(int)), this, SLOT(pictureShapeSensitivityChanged(int)) ); + connect( + higherSearchSensitivityCB, SIGNAL(clicked(bool)), + this, SLOT(higherSearchSensivityToggled(bool)) + ); connect( colorSegmentationCB, SIGNAL(clicked(bool)), @@ -1072,6 +1089,10 @@ namespace output { pictureShapeSensitivitySB, SIGNAL(valueChanged(int)), this, SLOT(pictureShapeSensitivityChanged(int)) ); + disconnect( + higherSearchSensitivityCB, SIGNAL(clicked(bool)), + this, SLOT(higherSearchSensivityToggled(bool)) + ); disconnect( colorSegmentationCB, SIGNAL(clicked(bool)), diff --git a/filters/output/OptionsWidget.h b/filters/output/OptionsWidget.h index 1002f1e26..218f544ac 100644 --- a/filters/output/OptionsWidget.h +++ b/filters/output/OptionsWidget.h @@ -98,6 +98,8 @@ namespace output { void pictureShapeSensitivityChanged(int value); + void higherSearchSensivityToggled(bool checked); + void colorSegmentationToggled(bool checked); void reduceNoiseChanged(int value); diff --git a/filters/output/OutputGenerator.cpp b/filters/output/OutputGenerator.cpp index 43aba45a7..0fc888731 100644 --- a/filters/output/OutputGenerator.cpp +++ b/filters/output/OutputGenerator.cpp @@ -2124,7 +2124,7 @@ namespace output { GrayImage OutputGenerator::detectPictures(const GrayImage& input_300dpi, const TaskStatus& status, - DebugImages* const dbg) { + DebugImages* const dbg) const { // We stretch the range of gray levels to cover the whole // range of [0, 255]. We do it because we want text // and background to be equally far from the center @@ -2192,6 +2192,15 @@ namespace output { dbg->add(holes_filled, "holes_filled"); } + if (m_pictureShapeOptions.isHigherSearchSensitivity()) { + GrayImage stretched2(stretchGrayRange(holes_filled, 5.0, 0.01)); + if (dbg) { + dbg->add(stretched2, "stretched2"); + } + + return stretched2; + } + return holes_filled; } // OutputGenerator::detectPictures diff --git a/filters/output/OutputGenerator.h b/filters/output/OutputGenerator.h index 79dc9219a..0cb298223 100644 --- a/filters/output/OutputGenerator.h +++ b/filters/output/OutputGenerator.h @@ -222,9 +222,9 @@ namespace output { imageproc::GrayImage* background = nullptr, DebugImages* dbg = nullptr); - static imageproc::GrayImage detectPictures(const imageproc::GrayImage& input_300dpi, - const TaskStatus& status, - DebugImages* dbg = nullptr); + imageproc::GrayImage detectPictures(const imageproc::GrayImage& input_300dpi, + const TaskStatus& status, + DebugImages* dbg = nullptr) const; imageproc::BinaryImage estimateBinarizationMask(const TaskStatus& status, const imageproc::GrayImage& gray_source, diff --git a/filters/output/PictureShapeOptions.cpp b/filters/output/PictureShapeOptions.cpp index ea5f46d11..10a39a475 100644 --- a/filters/output/PictureShapeOptions.cpp +++ b/filters/output/PictureShapeOptions.cpp @@ -5,25 +5,29 @@ namespace output { PictureShapeOptions::PictureShapeOptions() : pictureShape(FREE_SHAPE), - sensitivity(100) { + sensitivity(100), + higherSearchSensitivity(false) { } PictureShapeOptions::PictureShapeOptions(const QDomElement& el) : pictureShape(parsePictureShape(el.attribute("pictureShape"))), - sensitivity(el.attribute("sensitivity").toInt()) { + sensitivity(el.attribute("sensitivity").toInt()), + higherSearchSensitivity(el.attribute("higherSearchSensitivity") == "1") { } QDomElement PictureShapeOptions::toXml(QDomDocument& doc, const QString& name) const { QDomElement el(doc.createElement(name)); el.setAttribute("pictureShape", formatPictureShape(pictureShape)); el.setAttribute("sensitivity", sensitivity); + el.setAttribute("higherSearchSensitivity", higherSearchSensitivity ? "1" : "0"); return el; } bool PictureShapeOptions::operator==(const PictureShapeOptions& other) const { return (pictureShape == other.pictureShape) - && (sensitivity == other.sensitivity); + && (sensitivity == other.sensitivity) + && (higherSearchSensitivity == other.higherSearchSensitivity); } bool PictureShapeOptions::operator!=(const PictureShapeOptions& other) const { @@ -73,4 +77,12 @@ namespace output { PictureShapeOptions::sensitivity = sensitivity; } + bool PictureShapeOptions::isHigherSearchSensitivity() const { + return higherSearchSensitivity; + } + + void PictureShapeOptions::setHigherSearchSensitivity(bool higherSearchSensitivity) { + PictureShapeOptions::higherSearchSensitivity = higherSearchSensitivity; + } + } diff --git a/filters/output/PictureShapeOptions.h b/filters/output/PictureShapeOptions.h index 0013a1224..7fc7f701c 100644 --- a/filters/output/PictureShapeOptions.h +++ b/filters/output/PictureShapeOptions.h @@ -33,13 +33,19 @@ namespace output { void setSensitivity(int sensitivity); + bool isHigherSearchSensitivity() const; + + void setHigherSearchSensitivity(bool higherSearchSensitivity); + private: static PictureShape parsePictureShape(const QString& str); static QString formatPictureShape(PictureShape type); + PictureShape pictureShape; int sensitivity; + bool higherSearchSensitivity; }; } diff --git a/filters/output/ui/OutputOptionsWidget.ui b/filters/output/ui/OutputOptionsWidget.ui index 2c46f948a..18af1c2b4 100644 --- a/filters/output/ui/OutputOptionsWidget.ui +++ b/filters/output/ui/OutputOptionsWidget.ui @@ -86,9 +86,9 @@ 0 - 0 + -231 228 - 1190 + 1215 @@ -1074,6 +1074,43 @@ + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + Higher search sensitivity + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + diff --git a/imageproc/Grayscale.h b/imageproc/Grayscale.h index c60e2d8e7..0e56e3edc 100644 --- a/imageproc/Grayscale.h +++ b/imageproc/Grayscale.h @@ -75,7 +75,7 @@ namespace imageproc { QImage toGrayscale(const QImage& src); /** - * \brief Stetch the distribution of gray levels to cover the whole range. + * \brief Stretch the distribution of gray levels to cover the whole range. * * \param src The source image. It doesn't have to be grayscale. * \param black_clip_fraction The fraction of pixels (fractions of 1) that are diff --git a/ui/DefaultParamsDialog.ui b/ui/DefaultParamsDialog.ui index d0502ba2f..32b883fa2 100644 --- a/ui/DefaultParamsDialog.ui +++ b/ui/DefaultParamsDialog.ui @@ -112,7 +112,7 @@ QTabWidget::North - 5 + 0 true @@ -1768,7 +1768,7 @@ 0 - -27 + 0 646 477 @@ -3041,6 +3041,43 @@ + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + Higher search sensivity + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + From aea054dd3bce0b50fa8f99fd7c4c287142f5b45a Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sat, 17 Feb 2018 22:51:28 +0300 Subject: [PATCH 21/28] ~ Deviation provider implemented. All the code has been cleaned off the old implementation. --- AbstractFilter.h | 3 - CMakeLists.txt | 1 + DefaultParamsDialog.cpp | 18 +-- DeviationProvider.h | 145 +++++++++++++++++++++ ImageId.cpp | 2 +- ImageId.h | 10 ++ MainWindow.cpp | 3 +- PageId.cpp | 2 +- PageId.h | 10 ++ Utils.h | 28 ++-- filters/deskew/CacheDrivenTask.cpp | 2 +- filters/deskew/Filter.cpp | 14 -- filters/deskew/Filter.h | 2 - filters/deskew/OptionsWidget.cpp | 5 +- filters/deskew/Params.cpp | 20 +-- filters/deskew/Params.h | 7 - filters/deskew/Settings.cpp | 49 ++++--- filters/deskew/Settings.h | 33 +---- filters/deskew/Task.cpp | 2 - filters/page_layout/CacheDrivenTask.cpp | 3 +- filters/page_layout/Params.cpp | 4 - filters/page_layout/Params.h | 2 - filters/page_layout/Settings.cpp | 48 +++++++ filters/page_layout/Settings.h | 3 + filters/page_layout/Thumbnail.cpp | 8 +- filters/page_layout/Thumbnail.h | 4 +- filters/select_content/CacheDrivenTask.cpp | 2 +- filters/select_content/Filter.cpp | 12 -- filters/select_content/Filter.h | 2 - filters/select_content/OptionsWidget.cpp | 1 - filters/select_content/Params.cpp | 26 +--- filters/select_content/Params.h | 9 -- filters/select_content/Settings.cpp | 71 ++++------ filters/select_content/Settings.h | 19 +-- filters/select_content/Task.cpp | 1 - filters/select_content/Thumbnail.cpp | 44 +++---- 36 files changed, 339 insertions(+), 276 deletions(-) create mode 100644 DeviationProvider.h diff --git a/AbstractFilter.h b/AbstractFilter.h index a08bf9f2e..20a1344b6 100644 --- a/AbstractFilter.h +++ b/AbstractFilter.h @@ -62,9 +62,6 @@ class AbstractFilter : public ref_countable { virtual void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) = 0; - virtual void updateStatistics() { - } - virtual QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const = 0; virtual void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) = 0; diff --git a/CMakeLists.txt b/CMakeLists.txt index 028f5501f..af53505f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -556,6 +556,7 @@ SET( DefaultParams.cpp DefaultParams.h DefaultParamsProfileManager.cpp DefaultParamsProfileManager.h DefaultParamsProvider.cpp DefaultParamsProvider.h + DeviationProvider.h version.h config.h.in ${common_ui_files}) diff --git a/DefaultParamsDialog.cpp b/DefaultParamsDialog.cpp index 6c1e7aa1c..4794012a9 100644 --- a/DefaultParamsDialog.cpp +++ b/DefaultParamsDialog.cpp @@ -86,39 +86,39 @@ DefaultParamsDialog::DefaultParamsDialog(QWidget* parent) setLinkButtonLinked(topBottomLink, topBottomLinkEnabled); setLinkButtonLinked(leftRightLink, leftRightLinkEnabled); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignTopLeftBtn, Alignment(Alignment::TOP, Alignment::LEFT) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignTopBtn, Alignment(Alignment::TOP, Alignment::HCENTER) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignTopRightBtn, Alignment(Alignment::TOP, Alignment::RIGHT) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignLeftBtn, Alignment(Alignment::VCENTER, Alignment::LEFT) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignCenterBtn, Alignment(Alignment::VCENTER, Alignment::HCENTER) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignRightBtn, Alignment(Alignment::VCENTER, Alignment::RIGHT) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignBottomLeftBtn, Alignment(Alignment::BOTTOM, Alignment::LEFT) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignBottomBtn, Alignment(Alignment::BOTTOM, Alignment::HCENTER) ); - Utils::unorderedMapSetValue( + Utils::mapSetValue( alignmentByButton, alignBottomRightBtn, Alignment(Alignment::BOTTOM, Alignment::RIGHT) ); diff --git a/DeviationProvider.h b/DeviationProvider.h new file mode 100644 index 000000000..cccdf98b2 --- /dev/null +++ b/DeviationProvider.h @@ -0,0 +1,145 @@ + +#ifndef SCANTAILOR_DEVIATION_H +#define SCANTAILOR_DEVIATION_H + +#include +#include +#include + +template> +class DeviationProvider { +private: + std::function computeValueByKey; + std::unordered_map keyValueMap; + + // Cached values. + mutable bool needUpdate = false; + mutable double meanValue = 0.0; + mutable double standardDeviation = 0.0; + +public: + DeviationProvider() = default; + + explicit DeviationProvider(const std::function& computeValueByKey); + + bool isDeviant(const K& key, double coefficient = 1.0, double threshold = 0.0) const; + + double getDeviationValue(const K& key) const; + + void addOrUpdate(const K& key); + + void addOrUpdate(const K& key, double value); + + void remove(const K& key); + + void clear(); + + void setComputeValueByKey(const std::function& computeValueByKey); + +protected: + void update() const; +}; + + +template +DeviationProvider::DeviationProvider(const std::function& computeValueByKey) + : computeValueByKey(computeValueByKey) { +} + +template +bool DeviationProvider::isDeviant(const K& key, const double coefficient, const double threshold) const { + if (keyValueMap.find(key) == keyValueMap.end()) { + return false; + } + if (keyValueMap.size() < 3) { + return false; + } + + update(); + + return (std::abs(keyValueMap.at(key) - meanValue) > std::max((coefficient * standardDeviation), threshold)); +} + +template +double DeviationProvider::getDeviationValue(const K& key) const { + if (keyValueMap.find(key) == keyValueMap.end()) { + return .0; + } + if (keyValueMap.size() < 2) { + return .0; + } + + update(); + + return std::abs(keyValueMap.at(key) - meanValue); +} + +template +void DeviationProvider::addOrUpdate(const K& key) { + needUpdate = true; + + keyValueMap[key] = computeValueByKey(key); +} + +template +void DeviationProvider::addOrUpdate(const K& key, const double value) { + needUpdate = true; + + keyValueMap[key] = value; +} + +template +void DeviationProvider::remove(const K& key) { + needUpdate = true; + + if (keyValueMap.find(key) == keyValueMap.end()) { + return; + } + + keyValueMap.erase(key); +} + +template +void DeviationProvider::update() const { + if (!needUpdate) { + return; + } + if (keyValueMap.size() < 2) { + return; + } + + { + double sum = .0; + for (const std::pair& keyAndValue : keyValueMap) { + sum += keyAndValue.second; + } + meanValue = sum / keyValueMap.size(); + } + + { + double differencesSum = .0; + for (const std::pair& keyAndValue : keyValueMap) { + differencesSum += std::pow(keyAndValue.second - meanValue, 2); + } + standardDeviation = std::sqrt(differencesSum / (keyValueMap.size() - 1)); + } + + needUpdate = false; +} + +template +void DeviationProvider::setComputeValueByKey(const std::function& computeValueByKey) { + DeviationProvider::computeValueByKey = computeValueByKey; +} + +template +void DeviationProvider::clear() { + keyValueMap.clear(); + + needUpdate = false; + meanValue = 0.0; + standardDeviation = 0.0; +} + + +#endif //SCANTAILOR_DEVIATION_H diff --git a/ImageId.cpp b/ImageId.cpp index 8055d69a1..8fcf10e82 100644 --- a/ImageId.cpp +++ b/ImageId.cpp @@ -30,7 +30,7 @@ ImageId::ImageId(const QFileInfo& file_info, const int page) } bool operator==(const ImageId& lhs, const ImageId& rhs) { - return lhs.page() == rhs.page() && lhs.filePath() == rhs.filePath(); + return ((lhs.page() == rhs.page()) && (lhs.filePath() == rhs.filePath())); } bool operator!=(const ImageId& lhs, const ImageId& rhs) { diff --git a/ImageId.h b/ImageId.h index 116985843..314e0099a 100644 --- a/ImageId.h +++ b/ImageId.h @@ -80,4 +80,14 @@ bool operator!=(const ImageId& lhs, const ImageId& rhs); bool operator<(const ImageId& lhs, const ImageId& rhs); +namespace std { + template<> + struct hash { + std::size_t operator()(const ImageId& imageId) const noexcept { + return (hash()(imageId.filePath().toStdString()) + ^ hash()(imageId.page()) << 1); + } + }; +} + #endif // ifndef IMAGEID_H_ diff --git a/MainWindow.cpp b/MainWindow.cpp index 9bdbbfa5c..a25b6405b 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1275,8 +1275,7 @@ void MainWindow::stopBatchProcessing(MainAreaAction main_area) { removeImageWidget(); break; } - - m_ptrStages->filterAt(m_curFilter)->updateStatistics(); + resetThumbSequence(currentPageOrderProvider()); } diff --git a/PageId.cpp b/PageId.cpp index 9b5b75208..e99194907 100644 --- a/PageId.cpp +++ b/PageId.cpp @@ -70,7 +70,7 @@ PageId::SubPage PageId::subPageFromString(const QString& string, bool* ok) { } bool operator==(const PageId& lhs, const PageId& rhs) { - return lhs.subPage() == rhs.subPage() && lhs.imageId() == rhs.imageId(); + return ((lhs.subPage() == rhs.subPage()) && (lhs.imageId() == rhs.imageId())); } bool operator!=(const PageId& lhs, const PageId& rhs) { diff --git a/PageId.h b/PageId.h index 62c6eda61..f829846cb 100644 --- a/PageId.h +++ b/PageId.h @@ -83,4 +83,14 @@ bool operator!=(const PageId& lhs, const PageId& rhs); bool operator<(const PageId& lhs, const PageId& rhs); +namespace std { + template<> + struct hash { + std::size_t operator()(const PageId& pageId) const noexcept { + return (hash()(pageId.imageId()) + ^ hash()(pageId.subPage()) << 1); + } + }; +} + #endif // ifndef PAGEID_H_ diff --git a/Utils.h b/Utils.h index 88fe49ff4..1d3475faf 100644 --- a/Utils.h +++ b/Utils.h @@ -24,11 +24,13 @@ class Utils { public: - template - static typename M::iterator mapSetValue(M& map, const K& key, const V& val); + template + static typename std::map::iterator + mapSetValue(std::map& map, const K& key, const V& val); - template - static typename M::iterator unorderedMapSetValue(M& map, const K& key, const V& val); + template + static typename std::unordered_map::iterator + mapSetValue(std::unordered_map& map, const K& key, const V& val); template static T castOrFindChild(QObject* object); @@ -74,11 +76,12 @@ class Utils { }; -template -typename M::iterator Utils::mapSetValue(M& map, const K& key, const V& val) { - const typename M::iterator it(map.lower_bound(key)); +template +typename std::map::iterator +Utils::mapSetValue(std::map& map, const K& key, const V& val) { + const auto it(map.lower_bound(key)); if ((it == map.end()) || map.key_comp()(key, it->first)) { - return map.insert(it, typename M::value_type(key, val)); + return map.insert(it, typename std::map::value_type(key, val)); } else { it->second = val; @@ -86,11 +89,12 @@ typename M::iterator Utils::mapSetValue(M& map, const K& key, const V& val) { } } -template -typename M::iterator Utils::unorderedMapSetValue(M& map, const K& key, const V& val) { - const typename M::iterator it(map.find(key)); +template +typename std::unordered_map::iterator +Utils::mapSetValue(std::unordered_map& map, const K& key, const V& val) { + const auto it(map.find(key)); if (it == map.end()) { - return map.insert(it, typename M::value_type(key, val)); + return map.insert(it, typename std::unordered_map::value_type(key, val)); } else { it->second = val; diff --git a/filters/deskew/CacheDrivenTask.cpp b/filters/deskew/CacheDrivenTask.cpp index 8e3bf8da5..25fcb86e6 100644 --- a/filters/deskew/CacheDrivenTask.cpp +++ b/filters/deskew/CacheDrivenTask.cpp @@ -74,7 +74,7 @@ namespace deskew { thumb_col->thumbnailCache(), thumb_col->maxLogicalThumbSize(), page_info.imageId(), new_xform, - params->isDeviant(m_ptrSettings->std(), m_ptrSettings->maxDeviation()) + m_ptrSettings->deviationProvider().isDeviant(page_info.id(), 1.2, 0.3) ) ) ); diff --git a/filters/deskew/Filter.cpp b/filters/deskew/Filter.cpp index 875506941..a4a4156cc 100644 --- a/filters/deskew/Filter.cpp +++ b/filters/deskew/Filter.cpp @@ -65,10 +65,6 @@ namespace deskew { QDomElement filter_el(doc.createElement("deskew")); - filter_el.setAttribute("average", m_ptrSettings->avg()); - filter_el.setAttribute("sigma", m_ptrSettings->std()); - filter_el.setAttribute("maxDeviation", m_ptrSettings->maxDeviation()); - writer.enumPages( [&](const PageId& page_id, const int numeric_id) { this->writePageSettings(doc, filter_el, page_id, numeric_id); @@ -83,12 +79,6 @@ namespace deskew { const QDomElement filter_el(filters_el.namedItem("deskew").toElement()); - m_ptrSettings->setAvg(filter_el.attribute("average").toDouble()); - m_ptrSettings->setStd(filter_el.attribute("sigma").toDouble()); - m_ptrSettings->setMaxDeviation( - filter_el.attribute("maxDeviation", QString::number(5.0)).toDouble() - ); - const QString page_tag_name("page"); QDomNode node(filter_el.firstChild()); for (; !node.isNull(); node = node.nextSibling()) { @@ -173,8 +163,4 @@ namespace deskew { OptionsWidget* Filter::optionsWidget() { return m_ptrOptionsWidget.get(); } - - void Filter::updateStatistics() { - m_ptrSettings->updateDeviation(); - } } // namespace deskew \ No newline at end of file diff --git a/filters/deskew/Filter.h b/filters/deskew/Filter.h index a81c6b72d..6afe509c0 100644 --- a/filters/deskew/Filter.h +++ b/filters/deskew/Filter.h @@ -57,8 +57,6 @@ namespace deskew { void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; - void updateStatistics() override; - QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const override; void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; diff --git a/filters/deskew/OptionsWidget.cpp b/filters/deskew/OptionsWidget.cpp index b004cb9df..ed762fd4a 100644 --- a/filters/deskew/OptionsWidget.cpp +++ b/filters/deskew/OptionsWidget.cpp @@ -69,7 +69,7 @@ namespace deskew { m_uiData.effectiveDeskewAngle(), m_uiData.dependencies(), m_uiData.mode() ); - m_ptrSettings->setDegress(pages, params); + m_ptrSettings->setDegrees(pages, params); if (pages.size() > 1) { emit invalidateAllThumbnails(); @@ -89,7 +89,7 @@ namespace deskew { m_uiData.effectiveDeskewAngle(), m_uiData.dependencies(), m_uiData.mode() ); - m_ptrSettings->setDegress(pages, params); + m_ptrSettings->setDegrees(pages, params); emit invalidateAllThumbnails(); } @@ -194,7 +194,6 @@ namespace deskew { m_uiData.effectiveDeskewAngle(), m_uiData.dependencies(), m_uiData.mode() ); - params.computeDeviation(m_ptrSettings->avg()); m_ptrSettings->setPageParams(m_pageId, params); } diff --git a/filters/deskew/Params.cpp b/filters/deskew/Params.cpp index 42d84affe..b0a511508 100644 --- a/filters/deskew/Params.cpp +++ b/filters/deskew/Params.cpp @@ -24,15 +24,13 @@ namespace deskew { Params::Params(const double deskew_angle_deg, const Dependencies& deps, const AutoManualMode mode) : m_deskewAngleDeg(deskew_angle_deg), m_deps(deps), - m_mode(mode), - m_deviation(0.0) { + m_mode(mode) { } Params::Params(const QDomElement& deskew_el) : m_deskewAngleDeg(deskew_el.attribute("angle").toDouble()), m_deps(deskew_el.namedItem("dependencies").toElement()), - m_mode(deskew_el.attribute("mode") == "manual" ? MODE_MANUAL : MODE_AUTO), - m_deviation(deskew_el.attribute("deviation").toDouble()) { + m_mode(deskew_el.attribute("mode") == "manual" ? MODE_MANUAL : MODE_AUTO) { } Params::~Params() = default; @@ -41,7 +39,6 @@ namespace deskew { QDomElement el(doc.createElement(name)); el.setAttribute("mode", m_mode == MODE_AUTO ? "auto" : "manual"); el.setAttribute("angle", Utils::doubleToString(m_deskewAngleDeg)); - el.setAttribute("deviation", m_deviation); el.appendChild(m_deps.toXml(doc, "dependencies")); return el; @@ -50,19 +47,6 @@ namespace deskew { double Params::deskewAngle() const { return m_deskewAngleDeg; } - - double Params::deviation() const { - return m_deviation; - } - - void Params::computeDeviation(double avg) { - m_deviation = avg - m_deskewAngleDeg; - } - - bool Params::isDeviant(double std, double max_dev) const { - return std::max(1.5 * std, max_dev) < fabs(m_deviation); - } - const Dependencies& Params::dependencies() const { return m_deps; } diff --git a/filters/deskew/Params.h b/filters/deskew/Params.h index 18eb82530..7e03afd82 100644 --- a/filters/deskew/Params.h +++ b/filters/deskew/Params.h @@ -41,12 +41,6 @@ namespace deskew { double deskewAngle() const; - double deviation() const; - - void computeDeviation(double avg); - - bool isDeviant(double std, double max_dev) const; - const Dependencies& dependencies() const; AutoManualMode mode() const; @@ -57,7 +51,6 @@ namespace deskew { double m_deskewAngleDeg; Dependencies m_deps; AutoManualMode m_mode; - double m_deviation; }; } // namespace deskew #endif // ifndef DESKEW_PARAMS_H_ diff --git a/filters/deskew/Settings.cpp b/filters/deskew/Settings.cpp index 197b676ec..940e20379 100644 --- a/filters/deskew/Settings.cpp +++ b/filters/deskew/Settings.cpp @@ -22,10 +22,19 @@ #include "AbstractRelinker.h" namespace deskew { - Settings::Settings() - : m_avg(0.0), - m_sigma(0.0), - m_maxDeviation(5.0) { + Settings::Settings() { + m_deviationProvider.setComputeValueByKey( + [this](const PageId& pageId) -> double { + auto it(m_perPageParams.find(pageId)); + if (it != m_perPageParams.end()) { + const Params& params = it->second; + + return params.deskewAngle(); + } else { + return .0; + }; + } + ); } Settings::~Settings() = default; @@ -33,6 +42,7 @@ namespace deskew { void Settings::clear() { QMutexLocker locker(&m_mutex); m_perPageParams.clear(); + m_deviationProvider.clear(); } void Settings::performRelinking(const AbstractRelinker& relinker) { @@ -47,39 +57,23 @@ namespace deskew { } m_perPageParams.swap(new_params); - } - void Settings::updateDeviation() { - m_avg = 0.0; + m_deviationProvider.clear(); for (const PerPageParams::value_type& kv : m_perPageParams) { - m_avg += kv.second.deskewAngle(); - } - m_avg = m_avg / m_perPageParams.size(); -#ifdef DEBUG - std::cout << "avg skew = " << m_avg << std::endl; -#endif - - double sigma2 = 0.0; - for (PerPageParams::value_type& kv : m_perPageParams) { - kv.second.computeDeviation(m_avg); - sigma2 += kv.second.deviation() * kv.second.deviation(); + m_deviationProvider.addOrUpdate(kv.first); } - sigma2 = sigma2 / m_perPageParams.size(); - m_sigma = sqrt(sigma2); -#ifdef DEBUG - std::cout << "sigma2 = " << sigma2 << std::endl; - std::cout << "sigma = " << m_sigma << std::endl; -#endif } void Settings::setPageParams(const PageId& page_id, const Params& params) { QMutexLocker locker(&m_mutex); Utils::mapSetValue(m_perPageParams, page_id, params); + m_deviationProvider.addOrUpdate(page_id); } void Settings::clearPageParams(const PageId& page_id) { QMutexLocker locker(&m_mutex); m_perPageParams.erase(page_id); + m_deviationProvider.remove(page_id); } std::unique_ptr @@ -94,10 +88,11 @@ namespace deskew { } } - void Settings::setDegress(const std::set& pages, const Params& params) { + void Settings::setDegrees(const std::set& pages, const Params& params) { const QMutexLocker locker(&m_mutex); for (const PageId& page : pages) { Utils::mapSetValue(m_perPageParams, page, params); + m_deviationProvider.addOrUpdate(page); } } @@ -106,4 +101,8 @@ namespace deskew { return m_perPageParams.find(page_id) == m_perPageParams.end(); } + + const DeviationProvider& Settings::deviationProvider() const { + return m_deviationProvider; + } } // namespace deskew \ No newline at end of file diff --git a/filters/deskew/Settings.h b/filters/deskew/Settings.h index f3e62806d..af796edab 100644 --- a/filters/deskew/Settings.h +++ b/filters/deskew/Settings.h @@ -27,6 +27,7 @@ #include #include #include +#include class AbstractRelinker; @@ -43,8 +44,6 @@ namespace deskew { void performRelinking(const AbstractRelinker& relinker); - void updateDeviation(); - void setPageParams(const PageId& page_id, const Params& params); void clearPageParams(const PageId& page_id); @@ -53,40 +52,16 @@ namespace deskew { bool isParamsNull(const PageId& page_id) const; - void setDegress(const std::set& pages, const Params& params); - - double maxDeviation() const { - return m_maxDeviation; - } - - void setMaxDeviation(double md) { - m_maxDeviation = md; - } - - double avg() const { - return m_avg; - } - - void setAvg(double a) { - m_avg = a; - } - - double std() const { - return m_sigma; - } + void setDegrees(const std::set& pages, const Params& params); - void setStd(double s) { - m_sigma = s; - } + const DeviationProvider& deviationProvider() const; private: typedef std::map PerPageParams; mutable QMutex m_mutex; PerPageParams m_perPageParams; - double m_avg; - double m_sigma; - double m_maxDeviation; + DeviationProvider m_deviationProvider; }; } // namespace deskew #endif // ifndef DESKEW_SETTINGS_H_ diff --git a/filters/deskew/Task.cpp b/filters/deskew/Task.cpp index 45edeac9a..7b13e920d 100644 --- a/filters/deskew/Task.cpp +++ b/filters/deskew/Task.cpp @@ -108,7 +108,6 @@ namespace deskew { Params new_params( ui_data.effectiveDeskewAngle(), deps, ui_data.mode() ); - new_params.computeDeviation(m_ptrSettings->avg()); m_ptrSettings->setPageParams(m_pageId, new_params); } } @@ -166,7 +165,6 @@ namespace deskew { Params new_params( ui_data.effectiveDeskewAngle(), deps, ui_data.mode() ); - new_params.computeDeviation(m_ptrSettings->avg()); m_ptrSettings->setPageParams(m_pageId, new_params); status.throwIfCancelled(); diff --git a/filters/page_layout/CacheDrivenTask.cpp b/filters/page_layout/CacheDrivenTask.cpp index 764a5491d..50efe134f 100644 --- a/filters/page_layout/CacheDrivenTask.cpp +++ b/filters/page_layout/CacheDrivenTask.cpp @@ -90,7 +90,8 @@ namespace page_layout { thumb_col->maxLogicalThumbSize(), page_info.imageId(), *params, xform, content_rect_phys, - xform.transform().map(page_rect_phys).boundingRect() + xform.transform().map(page_rect_phys).boundingRect(), + m_ptrSettings->deviationProvider().isDeviant(page_info.id()) ) ) ); diff --git a/filters/page_layout/Params.cpp b/filters/page_layout/Params.cpp index 61f96321d..994011451 100644 --- a/filters/page_layout/Params.cpp +++ b/filters/page_layout/Params.cpp @@ -97,8 +97,4 @@ namespace page_layout { bool Params::isAutoMarginsEnabled() const { return m_autoMargins; } - - bool Params::isDeviant() const { - return m_alignment.isNull(); - } } // namespace page_layout diff --git a/filters/page_layout/Params.h b/filters/page_layout/Params.h index 030c28fb1..059878b71 100644 --- a/filters/page_layout/Params.h +++ b/filters/page_layout/Params.h @@ -53,8 +53,6 @@ namespace page_layout { bool isAutoMarginsEnabled() const; - bool isDeviant() const; - QDomElement toXml(QDomDocument& doc, const QString& name) const; private: diff --git a/filters/page_layout/Settings.cpp b/filters/page_layout/Settings.cpp index cd2366c13..945eb64f1 100644 --- a/filters/page_layout/Settings.cpp +++ b/filters/page_layout/Settings.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace ::boost; @@ -188,6 +189,8 @@ namespace page_layout { void setPageAutoMarginsEnabled(const PageId& page_id, bool state); + const DeviationProvider& deviationProvider() const; + private: class SequencedTag; class DescWidthTag; @@ -244,6 +247,7 @@ namespace page_layout { QRectF m_contentRect; QRectF m_pageRect; const bool m_autoMarginsDefault; + DeviationProvider m_deviationProvider; }; @@ -355,6 +359,10 @@ namespace page_layout { return m_ptrImpl->isParamsNull(page_id); } + const DeviationProvider& Settings::deviationProvider() const { + return m_ptrImpl->deviationProvider(); + } + /*============================== Settings::Item =============================*/ Settings::Item::Item(const PageId& page_id, @@ -401,6 +409,23 @@ namespace page_layout { m_defaultHardMarginsMM(Margins(10.0, 5.0, 10.0, 5.0)), m_defaultAlignment(Alignment::TOP, Alignment::HCENTER), m_autoMarginsDefault(false) { + m_deviationProvider.setComputeValueByKey( + [this](const PageId& pageId) -> double { + auto it(m_items.find(pageId)); + if (it != m_items.end()) { + if (it->alignment.isNull()) { + const Margins& hardMarginsMM = it->hardMarginsMM; + const QSizeF& contentSizeMM = it->contentSizeMM; + + return std::sqrt(it->hardWidthMM() * it->hardHeightMM() / 4 / 25.4); + } else { + return .0; + } + } else { + return .0; + }; + } + ); } Settings::Impl::~Impl() = default; @@ -408,6 +433,7 @@ namespace page_layout { void Settings::Impl::clear() { const QMutexLocker locker(&m_mutex); m_items.clear(); + m_deviationProvider.clear(); } void Settings::Impl::performRelinking(const AbstractRelinker& relinker) { @@ -422,6 +448,11 @@ namespace page_layout { } m_items.swap(new_items); + + m_deviationProvider.clear(); + for (const Item& item : m_unorderedItems) { + m_deviationProvider.addOrUpdate(item.pageId); + } } void Settings::Impl::removePagesMissingFrom(const PageSequence& pages) { @@ -441,6 +472,7 @@ namespace page_layout { if (std::binary_search(sorted_pages.begin(), sorted_pages.end(), it->pageId)) { ++it; } else { + m_deviationProvider.remove(it->pageId); m_unorderedItems.erase(it++); } } @@ -491,6 +523,8 @@ namespace page_layout { } else { m_items.replace(it, new_item); } + + m_deviationProvider.addOrUpdate(page_id); } Params Settings::Impl::updateContentSizeAndGetParams(const PageId& page_id, @@ -526,6 +560,8 @@ namespace page_layout { updateContentRect(); } + m_deviationProvider.addOrUpdate(page_id); + return Params( item_it->hardMarginsMM, item_it->pageRect, item_it->contentRect, item_it->contentSizeMM, item_it->alignment, item_it->autoMargins @@ -596,6 +632,8 @@ namespace page_layout { } else { m_items.modify(it, ModifyMargins(margins_mm, it->autoMargins)); } + + m_deviationProvider.addOrUpdate(page_id); } Alignment Settings::Impl::getPageAlignment(const PageId& page_id) const { @@ -625,6 +663,8 @@ namespace page_layout { m_items.modify(it, ModifyAlignment(alignment)); } + m_deviationProvider.addOrUpdate(page_id); + const QSizeF agg_size_after(getAggregateHardSizeMMLocked()); if (agg_size_before == agg_size_after) { return AGGREGATE_SIZE_UNCHANGED; @@ -650,6 +690,8 @@ namespace page_layout { m_items.modify(it, ModifyContentSize(content_size_mm, m_invalidRect, it->pageRect)); } + m_deviationProvider.addOrUpdate(page_id); + const QSizeF agg_size_after(getAggregateHardSizeMMLocked()); if (agg_size_before == agg_size_after) { return AGGREGATE_SIZE_UNCHANGED; @@ -665,6 +707,8 @@ namespace page_layout { if (it != m_items.end()) { m_items.modify(it, ModifyContentSize(m_invalidSize, m_invalidRect, it->pageRect)); } + + m_deviationProvider.addOrUpdate(page_id); } QSizeF Settings::Impl::getAggregateHardSizeMM() const { @@ -770,4 +814,8 @@ namespace page_layout { return (m_items.find(page_id) == m_items.end()); } + + const DeviationProvider& Settings::Impl::deviationProvider() const { + return m_deviationProvider; + } } // namespace page_layout \ No newline at end of file diff --git a/filters/page_layout/Settings.h b/filters/page_layout/Settings.h index 5c8412188..675ce34e9 100644 --- a/filters/page_layout/Settings.h +++ b/filters/page_layout/Settings.h @@ -23,6 +23,7 @@ #include "ref_countable.h" #include "Margins.h" #include +#include class PageId; class Margins; @@ -170,6 +171,8 @@ namespace page_layout { void setPageAutoMarginsEnabled(const PageId& page_id, bool state); + const DeviationProvider& deviationProvider() const; + private: class Impl; class Item; diff --git a/filters/page_layout/Thumbnail.cpp b/filters/page_layout/Thumbnail.cpp index 1cf8e8954..42bc26855 100644 --- a/filters/page_layout/Thumbnail.cpp +++ b/filters/page_layout/Thumbnail.cpp @@ -31,11 +31,13 @@ namespace page_layout { const Params& params, const ImageTransformation& xform, const QPolygonF& phys_content_rect, - QRectF displayArea) + const QRectF& displayArea, + bool deviant) : ThumbnailBase(std::move(thumbnail_cache), max_size, image_id, xform, displayArea), m_params(params), m_virtContentRect(xform.transform().map(phys_content_rect).boundingRect()), - m_virtOuterRect(displayArea) { + m_virtOuterRect(displayArea), + m_deviant(deviant) { setExtendedClipArea(true); } @@ -106,7 +108,7 @@ namespace page_layout { painter.setPen(pen); painter.drawRect(outer_rect); - if (m_params.isDeviant()) { + if (m_deviant) { paintDeviant(painter); } } // Thumbnail::paintOverImage diff --git a/filters/page_layout/Thumbnail.h b/filters/page_layout/Thumbnail.h index 4aa831b06..387d737bc 100644 --- a/filters/page_layout/Thumbnail.h +++ b/filters/page_layout/Thumbnail.h @@ -38,7 +38,8 @@ namespace page_layout { const Params& params, const ImageTransformation& xform, const QPolygonF& phys_content_rect, - QRectF displayArea); + const QRectF& displayArea, + bool deviant); void paintOverImage(QPainter& painter, const QTransform& image_to_display, @@ -48,6 +49,7 @@ namespace page_layout { Params m_params; QRectF m_virtContentRect; QRectF m_virtOuterRect; + bool m_deviant; }; } // namespace page_layout #endif diff --git a/filters/select_content/CacheDrivenTask.cpp b/filters/select_content/CacheDrivenTask.cpp index 17cbbf627..724b24208 100644 --- a/filters/select_content/CacheDrivenTask.cpp +++ b/filters/select_content/CacheDrivenTask.cpp @@ -79,7 +79,7 @@ namespace select_content { params->contentRect(), params->pageRect(), params->isPageDetectionEnabled(), - params->isDeviant(m_ptrSettings->std(), m_ptrSettings->maxDeviation()) + m_ptrSettings->deviationProvider().isDeviant(page_info.id()) ) ) ); diff --git a/filters/select_content/Filter.cpp b/filters/select_content/Filter.cpp index d4b18303f..6df2988b9 100644 --- a/filters/select_content/Filter.cpp +++ b/filters/select_content/Filter.cpp @@ -93,9 +93,6 @@ namespace select_content { QDomElement filter_el(doc.createElement("select-content")); - filter_el.setAttribute("average", m_ptrSettings->avg()); - filter_el.setAttribute("sigma", m_ptrSettings->std()); - filter_el.setAttribute("maxDeviation", m_ptrSettings->maxDeviation()); filter_el.setAttribute("pageDetectionBoxWidth", m_ptrSettings->pageDetectionBox().width()); filter_el.setAttribute("pageDetectionBoxHeight", m_ptrSettings->pageDetectionBox().height()); filter_el.setAttribute("pageDetectionTolerance", m_ptrSettings->pageDetectionTolerance()); @@ -130,12 +127,6 @@ namespace select_content { filters_el.namedItem("select-content").toElement() ); - m_ptrSettings->setAvg(filter_el.attribute("average").toDouble()); - m_ptrSettings->setStd(filter_el.attribute("sigma").toDouble()); - m_ptrSettings->setMaxDeviation( - filter_el.attribute("maxDeviation", QString::number(1.0)).toDouble() - ); - QSizeF box(0.0, 0.0); box.setWidth(filter_el.attribute("pageDetectionBoxWidth", "0.0").toDouble()); box.setHeight(filter_el.attribute("pageDetectionBoxHeight", "0.0").toDouble()); @@ -225,7 +216,4 @@ namespace select_content { return m_ptrOptionsWidget.get(); } - void Filter::updateStatistics() { - m_ptrSettings->updateDeviation(); - } } // namespace select_content \ No newline at end of file diff --git a/filters/select_content/Filter.h b/filters/select_content/Filter.h index acc9d40b6..96758da8d 100644 --- a/filters/select_content/Filter.h +++ b/filters/select_content/Filter.h @@ -67,8 +67,6 @@ namespace select_content { void preUpdateUI(FilterUiInterface* ui, const PageInfo& page_info) override; - void updateStatistics() override; - QDomElement saveSettings(const ProjectWriter& writer, QDomDocument& doc) const override; void loadSettings(const ProjectReader& reader, const QDomElement& filters_el) override; diff --git a/filters/select_content/OptionsWidget.cpp b/filters/select_content/OptionsWidget.cpp index 430ecbbfb..16d5d5a2c 100644 --- a/filters/select_content/OptionsWidget.cpp +++ b/filters/select_content/OptionsWidget.cpp @@ -264,7 +264,6 @@ namespace select_content { m_uiData.isContentDetectionEnabled(), m_uiData.isPageDetectionEnabled(), m_uiData.isFineTuningCornersEnabled() ); - params.computeDeviation(m_ptrSettings->avg()); m_ptrSettings->setPageParams(m_pageId, params); } diff --git a/filters/select_content/Params.cpp b/filters/select_content/Params.cpp index 61cdd11d9..f10ee9ce8 100644 --- a/filters/select_content/Params.cpp +++ b/filters/select_content/Params.cpp @@ -27,8 +27,7 @@ namespace select_content { m_pageDetectionMode(MODE_AUTO), m_contentDetectEnabled(true), m_pageDetectEnabled(false), - m_fineTuneCorners(false), - m_deviation(0.0) { + m_fineTuneCorners(false) { } Params::Params(const QRectF& content_rect, @@ -48,8 +47,7 @@ namespace select_content { m_pageDetectionMode(page_detection_mode), m_contentDetectEnabled(contentDetect), m_pageDetectEnabled(pageDetect), - m_fineTuneCorners(fineTuning), - m_deviation(0.0) { + m_fineTuneCorners(fineTuning) { } Params::Params(const QDomElement& filter_el) @@ -73,8 +71,7 @@ namespace select_content { m_pageDetectionMode(filter_el.attribute("pageDetectionMode") == "manual" ? MODE_MANUAL : MODE_AUTO), m_contentDetectEnabled(filter_el.attribute("content-detect") == "1"), m_pageDetectEnabled(filter_el.attribute("page-detect") == "1"), - m_fineTuneCorners(filter_el.attribute("fine-tune-corners") == "1"), - m_deviation(filter_el.attribute("deviation").toDouble()) { + m_fineTuneCorners(filter_el.attribute("fine-tune-corners") == "1") { } Params::~Params() = default; @@ -88,7 +85,6 @@ namespace select_content { el.setAttribute("content-detect", m_contentDetectEnabled ? "1" : "0"); el.setAttribute("page-detect", m_pageDetectEnabled ? "1" : "0"); el.setAttribute("fine-tune-corners", m_fineTuneCorners ? "1" : "0"); - el.setAttribute("deviation", m_deviation); el.appendChild(marshaller.rectF(m_contentRect, "content-rect")); el.appendChild(marshaller.rectF(m_pageRect, "page-rect")); el.appendChild(marshaller.sizeF(m_contentSizeMM, "content-size-mm")); @@ -121,22 +117,6 @@ namespace select_content { return m_pageDetectionMode; } - double Params::deviation() const { - return m_deviation; - } - - void Params::setDeviation(double d) { - m_deviation = d; - } - - void Params::computeDeviation(double avg) { - m_deviation = avg - sqrt(m_contentSizeMM.width() * m_contentSizeMM.height() / 4); - } - - bool Params::isDeviant(double std, double max_dev) { - return (max_dev * std) < fabs(m_deviation); - } - bool Params::isContentDetectionEnabled() const { return m_contentDetectEnabled; } diff --git a/filters/select_content/Params.h b/filters/select_content/Params.h index e481095f4..e06ac13db 100644 --- a/filters/select_content/Params.h +++ b/filters/select_content/Params.h @@ -65,14 +65,6 @@ namespace select_content { AutoManualMode pageDetectionMode() const; - double deviation() const; - - void setDeviation(double d); - - void computeDeviation(double avg); - - bool isDeviant(double std, double max_dev); - bool isContentDetectionEnabled() const; bool isPageDetectionEnabled() const; @@ -107,7 +99,6 @@ namespace select_content { bool m_contentDetectEnabled; bool m_pageDetectEnabled; bool m_fineTuneCorners; - double m_deviation; }; } // namespace select_content #endif // ifndef SELECT_CONTENT_PARAMS_H_ diff --git a/filters/select_content/Settings.cpp b/filters/select_content/Settings.cpp index c86838c9e..be1bb800f 100644 --- a/filters/select_content/Settings.cpp +++ b/filters/select_content/Settings.cpp @@ -24,11 +24,21 @@ namespace select_content { Settings::Settings() - : m_avg(0.0), - m_sigma(0.0), - m_pageDetectionBox(0.0, 0.0), - m_pageDetectionTolerance(0.1), - m_maxDeviation(1.0) { + : m_pageDetectionBox(0.0, 0.0), + m_pageDetectionTolerance(0.1) { + m_deviationProvider.setComputeValueByKey( + [this](const PageId& pageId) -> double { + auto it(m_pageParams.find(pageId)); + if (it != m_pageParams.end()) { + const Params& params = it->second; + const QSizeF& contentSize = params.contentRect().size(); + + return std::sqrt(contentSize.width() * contentSize.height() / 4 / 600); + } else { + return .0; + }; + } + ); } Settings::~Settings() = default; @@ -36,6 +46,7 @@ namespace select_content { void Settings::clear() { QMutexLocker locker(&m_mutex); m_pageParams.clear(); + m_deviationProvider.clear(); } void Settings::performRelinking(const AbstractRelinker& relinker) { @@ -50,40 +61,23 @@ namespace select_content { } m_pageParams.swap(new_params); - } - void Settings::updateDeviation() { - m_avg = 0.0; - for (PageParams::value_type& kv : m_pageParams) { - kv.second.computeDeviation(0.0); - m_avg += -1 * kv.second.deviation(); - } - m_avg = m_avg / double(m_pageParams.size()); -#ifdef DEBUG - std::cout << "avg_content = " << m_avg << std::endl; -#endif - - double sigma2 = 0.0; - for (PageParams::value_type& kv : m_pageParams) { - kv.second.computeDeviation(m_avg); - sigma2 += kv.second.deviation() * kv.second.deviation(); + m_deviationProvider.clear(); + for (const PageParams::value_type& kv : m_pageParams) { + m_deviationProvider.addOrUpdate(kv.first); } - sigma2 = sigma2 / double(m_pageParams.size()); - m_sigma = sqrt(sigma2); -#if DEBUG - std::cout << "sigma2 = " << sigma2 << std::endl; - std::cout << "sigma = " << m_sigma << std::endl; -#endif } void Settings::setPageParams(const PageId& page_id, const Params& params) { QMutexLocker locker(&m_mutex); Utils::mapSetValue(m_pageParams, page_id, params); + m_deviationProvider.addOrUpdate(page_id); } void Settings::clearPageParams(const PageId& page_id) { QMutexLocker locker(&m_mutex); m_pageParams.erase(page_id); + m_deviationProvider.remove(page_id); } std::unique_ptr @@ -104,14 +98,6 @@ namespace select_content { return m_pageParams.find(page_id) == m_pageParams.end(); } - double Settings::maxDeviation() const { - return m_maxDeviation; - } - - void Settings::setMaxDeviation(double md) { - m_maxDeviation = md; - } - QSizeF Settings::pageDetectionBox() const { return m_pageDetectionBox; } @@ -128,19 +114,8 @@ namespace select_content { m_pageDetectionTolerance = tolerance; } - double Settings::avg() const { - return m_avg; - } - - void Settings::setAvg(double a) { - m_avg = a; + const DeviationProvider & Settings::deviationProvider() const { + return m_deviationProvider; } - double Settings::std() const { - return m_sigma; - } - - void Settings::setStd(double s) { - m_sigma = s; - } } // namespace select_content \ No newline at end of file diff --git a/filters/select_content/Settings.h b/filters/select_content/Settings.h index 13288043e..bb0995b03 100644 --- a/filters/select_content/Settings.h +++ b/filters/select_content/Settings.h @@ -26,6 +26,7 @@ #include #include #include +#include class AbstractRelinker; @@ -42,8 +43,6 @@ namespace select_content { void performRelinking(const AbstractRelinker& relinker); - void updateDeviation(); - void setPageParams(const PageId& page_id, const Params& params); void clearPageParams(const PageId& page_id); @@ -52,10 +51,6 @@ namespace select_content { bool isParamsNull(const PageId& page_id) const; - double maxDeviation() const; - - void setMaxDeviation(double md); - QSizeF pageDetectionBox() const; void setPageDetectionBox(QSizeF size); @@ -64,24 +59,16 @@ namespace select_content { void setPageDetectionTolerance(double tolerance); - double avg() const; - - void setAvg(double a); - - double std() const; - - void setStd(double s); + const DeviationProvider& deviationProvider() const; private: typedef std::map PageParams; mutable QMutex m_mutex; PageParams m_pageParams; - double m_avg; - double m_sigma; - double m_maxDeviation; QSizeF m_pageDetectionBox; double m_pageDetectionTolerance; + DeviationProvider m_deviationProvider; }; } // namespace select_content #endif // ifndef SELECT_CONTENT_SETTINGS_H_ diff --git a/filters/select_content/Task.cpp b/filters/select_content/Task.cpp index b06709752..ba153e178 100644 --- a/filters/select_content/Task.cpp +++ b/filters/select_content/Task.cpp @@ -175,7 +175,6 @@ namespace select_content { new_params.setContentSizeMM(ui_data.contentSizeMM()); } - new_params.computeDeviation(m_ptrSettings->avg()); m_ptrSettings->setPageParams(m_pageId, new_params); status.throwIfCancelled(); diff --git a/filters/select_content/Thumbnail.cpp b/filters/select_content/Thumbnail.cpp index 7e8a0c24b..2580a0aff 100644 --- a/filters/select_content/Thumbnail.cpp +++ b/filters/select_content/Thumbnail.cpp @@ -38,39 +38,37 @@ namespace select_content { void Thumbnail::paintOverImage(QPainter& painter, const QTransform& image_to_display, const QTransform& thumb_to_display) { - if (m_contentRect.isNull()) { - return; - } + if (!m_contentRect.isNull()) { + QRectF page_rect(virtToThumb().mapRect(m_pageRect)); + + painter.setRenderHint(QPainter::Antialiasing, false); - QRectF page_rect(virtToThumb().mapRect(m_pageRect)); + if (m_pageRectEnabled) { + QPen pen(QColor(0xff, 0x7f, 0x00)); + pen.setWidth(1); + pen.setCosmetic(true); + painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing, false); + painter.setBrush(Qt::NoBrush); - if (m_pageRectEnabled) { - QPen pen(QColor(0xff, 0x7f, 0x00)); + painter.drawRect(page_rect); + } + + QPen pen(QColor(0x00, 0x00, 0xff)); pen.setWidth(1); pen.setCosmetic(true); painter.setPen(pen); - painter.setBrush(Qt::NoBrush); - - painter.drawRect(page_rect); - } - - QPen pen(QColor(0x00, 0x00, 0xff)); - pen.setWidth(1); - pen.setCosmetic(true); - painter.setPen(pen); + painter.setBrush(QColor(0x00, 0x00, 0xff, 50)); - painter.setBrush(QColor(0x00, 0x00, 0xff, 50)); + QRectF content_rect(virtToThumb().mapRect(m_contentRect)); - QRectF content_rect(virtToThumb().mapRect(m_contentRect)); + // Adjust to compensate for pen width. + content_rect.adjust(-1, -1, 1, 1); + content_rect = content_rect.intersected(page_rect); - // Adjust to compensate for pen width. - content_rect.adjust(-1, -1, 1, 1); - content_rect = content_rect.intersected(page_rect); - - painter.drawRect(content_rect); + painter.drawRect(content_rect); + } if (m_deviant) { paintDeviant(painter); From 24587debede130fb8c510edde387779a212790c2 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sat, 17 Feb 2018 23:37:33 +0300 Subject: [PATCH 22/28] ~ Added ordering by decreasing deviation. --- CMakeLists.txt | 1 + OrderByDeviationProvider.cpp | 18 ++++++++++++++++++ OrderByDeviationProvider.h | 24 ++++++++++++++++++++++++ PageOrderProvider.h | 4 +++- filters/deskew/Filter.cpp | 25 ++++++++++++++++++++++++- filters/deskew/Filter.h | 10 ++++++++++ filters/page_layout/Filter.cpp | 3 +++ filters/select_content/Filter.cpp | 3 +++ 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 OrderByDeviationProvider.cpp create mode 100644 OrderByDeviationProvider.h diff --git a/CMakeLists.txt b/CMakeLists.txt index af53505f1..1f998ccf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,6 +557,7 @@ SET( DefaultParamsProfileManager.cpp DefaultParamsProfileManager.h DefaultParamsProvider.cpp DefaultParamsProvider.h DeviationProvider.h + OrderByDeviationProvider.cpp OrderByDeviationProvider.h version.h config.h.in ${common_ui_files}) diff --git a/OrderByDeviationProvider.cpp b/OrderByDeviationProvider.cpp new file mode 100644 index 000000000..256dbff51 --- /dev/null +++ b/OrderByDeviationProvider.cpp @@ -0,0 +1,18 @@ + +#include "OrderByDeviationProvider.h" + +OrderByDeviationProvider::OrderByDeviationProvider(const DeviationProvider& settings) + : m_deviationProvider(&settings) { +} + +bool OrderByDeviationProvider::precedes(const PageId& lhs_page, + bool lhs_incomplete, + const PageId& rhs_page, + bool rhs_incomplete) const { + if (lhs_incomplete != rhs_incomplete) { + // Invalid (unknown) sizes go to the back. + return lhs_incomplete; + } + + return (m_deviationProvider->getDeviationValue(lhs_page) > m_deviationProvider->getDeviationValue(rhs_page)); +} diff --git a/OrderByDeviationProvider.h b/OrderByDeviationProvider.h new file mode 100644 index 000000000..8d3601c01 --- /dev/null +++ b/OrderByDeviationProvider.h @@ -0,0 +1,24 @@ + +#ifndef SCANTAILOR_ORDERBYDEVIATIONPROVIDER_H +#define SCANTAILOR_ORDERBYDEVIATIONPROVIDER_H + + +#include "DeviationProvider.h" +#include "PageId.h" +#include "PageOrderProvider.h" + +class OrderByDeviationProvider : public PageOrderProvider { + public: + explicit OrderByDeviationProvider(const DeviationProvider& deviationProvider); + + bool precedes(const PageId& lhs_page, + bool lhs_incomplete, + const PageId& rhs_page, + bool rhs_incomplete) const override; + + private: + const DeviationProvider* m_deviationProvider; +}; + + +#endif //SCANTAILOR_ORDERBYDEVIATIONPROVIDER_H diff --git a/PageOrderProvider.h b/PageOrderProvider.h index fe733b14f..9ce98dbf8 100644 --- a/PageOrderProvider.h +++ b/PageOrderProvider.h @@ -33,7 +33,9 @@ class PageOrderProvider : public ref_countable { * \p lhs_incomplete and \p rhs_incomplete indicate whether * a page is represented by IncompleteThumbnail. */ - virtual bool precedes(const PageId& lhs_page, bool lhs_incomplete, const PageId& rhs_page, + virtual bool precedes(const PageId& lhs_page, + bool lhs_incomplete, + const PageId& rhs_page, bool rhs_incomplete) const = 0; }; diff --git a/filters/deskew/Filter.cpp b/filters/deskew/Filter.cpp index a4a4156cc..7ced3b4f8 100644 --- a/filters/deskew/Filter.cpp +++ b/filters/deskew/Filter.cpp @@ -32,13 +32,22 @@ #include #include #include +#include namespace deskew { Filter::Filter(const PageSelectionAccessor& page_selection_accessor) - : m_ptrSettings(new Settings) { + : m_ptrSettings(new Settings), + m_selectedPageOrder(0) { if (CommandLine::get().isGui()) { m_ptrOptionsWidget.reset(new OptionsWidget(m_ptrSettings, page_selection_accessor)); } + + typedef PageOrderOption::ProviderPtr ProviderPtr; + + const ProviderPtr default_order; + const ProviderPtr order_by_deviation(new OrderByDeviationProvider(m_ptrSettings->deviationProvider())); + m_pageOrderOptions.emplace_back(tr("Natural order"), default_order); + m_pageOrderOptions.emplace_back(tr("Order by decreasing deviation"), order_by_deviation); } Filter::~Filter() = default; @@ -147,6 +156,20 @@ namespace deskew { ); } + std::vector + Filter::pageOrderOptions() const { + return m_pageOrderOptions; + } + + int Filter::selectedPageOrder() const { + return m_selectedPageOrder; + } + + void Filter::selectPageOrder(int option) { + assert((unsigned) option < m_pageOrderOptions.size()); + m_selectedPageOrder = option; + } + void Filter::loadDefaultSettings(const PageInfo& page_info) { if (!m_ptrSettings->isParamsNull(page_info.id())) { return; diff --git a/filters/deskew/Filter.h b/filters/deskew/Filter.h index 6afe509c0..a376aaec1 100644 --- a/filters/deskew/Filter.h +++ b/filters/deskew/Filter.h @@ -26,6 +26,7 @@ #include "FilterResult.h" #include "SafeDeletingQObjectPtr.h" #include "Settings.h" +#include class QString; class PageSelectionAccessor; @@ -44,6 +45,7 @@ namespace deskew { class Filter : public AbstractFilter { DECLARE_NON_COPYABLE(Filter) + Q_DECLARE_TR_FUNCTIONS(deskew::Filter) public: explicit Filter(const PageSelectionAccessor& page_selection_accessor); @@ -73,11 +75,19 @@ namespace deskew { OptionsWidget* optionsWidget(); + std::vector pageOrderOptions() const override; + + int selectedPageOrder() const override; + + void selectPageOrder(int option) override; + private: void writePageSettings(QDomDocument& doc, QDomElement& filter_el, const PageId& page_id, int numeric_id) const; intrusive_ptr m_ptrSettings; SafeDeletingQObjectPtr m_ptrOptionsWidget; + std::vector m_pageOrderOptions; + int m_selectedPageOrder; }; } // namespace deskew #endif // ifndef DESKEW_FILTER_H_ diff --git a/filters/page_layout/Filter.cpp b/filters/page_layout/Filter.cpp index 87d3a30c1..da6886205 100644 --- a/filters/page_layout/Filter.cpp +++ b/filters/page_layout/Filter.cpp @@ -40,6 +40,7 @@ #include #include #include +#include namespace page_layout { Filter::Filter(intrusive_ptr pages, const PageSelectionAccessor& page_selection_accessor) @@ -57,9 +58,11 @@ namespace page_layout { const ProviderPtr default_order; const ProviderPtr order_by_width(new OrderByWidthProvider(m_ptrSettings)); const ProviderPtr order_by_height(new OrderByHeightProvider(m_ptrSettings)); + const ProviderPtr order_by_deviation(new OrderByDeviationProvider(m_ptrSettings->deviationProvider())); m_pageOrderOptions.emplace_back(tr("Natural order"), default_order); m_pageOrderOptions.emplace_back(tr("Order by increasing width"), order_by_width); m_pageOrderOptions.emplace_back(tr("Order by increasing height"), order_by_height); + m_pageOrderOptions.emplace_back(tr("Order by decreasing deviation"), order_by_deviation); } Filter::~Filter() = default; diff --git a/filters/select_content/Filter.cpp b/filters/select_content/Filter.cpp index 6df2988b9..c39c1653d 100644 --- a/filters/select_content/Filter.cpp +++ b/filters/select_content/Filter.cpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace select_content { Filter::Filter(const PageSelectionAccessor& page_selection_accessor) @@ -50,9 +51,11 @@ namespace select_content { const ProviderPtr default_order; const ProviderPtr order_by_width(new OrderByWidthProvider(m_ptrSettings)); const ProviderPtr order_by_height(new OrderByHeightProvider(m_ptrSettings)); + const ProviderPtr order_by_deviation(new OrderByDeviationProvider(m_ptrSettings->deviationProvider())); m_pageOrderOptions.emplace_back(tr("Natural order"), default_order); m_pageOrderOptions.emplace_back(tr("Order by increasing width"), order_by_width); m_pageOrderOptions.emplace_back(tr("Order by increasing height"), order_by_height); + m_pageOrderOptions.emplace_back(tr("Order by decreasing deviation"), order_by_deviation); } Filter::~Filter() = default; From 03e0da7872991c9711c177bf1e5425c9ac6210c4 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 12:38:28 +0300 Subject: [PATCH 23/28] ~ Performance optimization: std::map => std::unordered_map. --- Despeckle.cpp | 27 +++++++++++++++++------ ImageId.h | 2 +- PageId.h | 2 +- ProjectPages.cpp | 2 +- ProjectReader.h | 10 ++++----- ProjectWriter.h | 4 ++-- filters/deskew/Settings.cpp | 2 +- filters/deskew/Settings.h | 4 ++-- filters/fix_orientation/Settings.cpp | 2 +- filters/fix_orientation/Settings.h | 4 ++-- filters/output/Settings.cpp | 32 ++++++++++++++-------------- filters/output/Settings.h | 10 ++++----- filters/page_layout/OptionsWidget.h | 4 ++-- filters/page_split/Settings.cpp | 10 ++++----- filters/page_split/Settings.h | 4 ++-- filters/select_content/Settings.cpp | 2 +- filters/select_content/Settings.h | 4 ++-- 17 files changed, 69 insertions(+), 56 deletions(-) diff --git a/Despeckle.cpp b/Despeckle.cpp index c58d454e5..25f235e50 100644 --- a/Despeckle.cpp +++ b/Despeckle.cpp @@ -252,6 +252,18 @@ namespace { return greater_label < rhs.greater_label; } } + + bool operator==(const Connection& other) const { + return (lesser_label == other.lesser_label) + && (greater_label == other.greater_label); + } + + struct hash { + std::size_t operator()(const Connection& connection) const noexcept { + return std::hash()(connection.lesser_label) + ^ std::hash()(connection.greater_label << 1); + } + }; }; /** @@ -289,12 +301,15 @@ namespace { * \brief If the association didn't exist, create it, * otherwise the minimum distance. */ - void updateDistance(std::map& conns, uint32_t label1, uint32_t label2, uint32_t sqdist) { - typedef std::map Connections; + void updateDistance(std::unordered_map& conns, + uint32_t label1, + uint32_t label2, + uint32_t sqdist) { + typedef std::unordered_map Connections; const Connection conn(label1, label2); - auto it(conns.lower_bound(conn)); - if ((it == conns.end()) || (conn < it->first)) { + auto it(conns.find(conn)); + if (it == conns.end()) { conns.insert(Connections::value_type(conn, sqdist)); } else if (sqdist < it->second) { it->second = sqdist; @@ -630,7 +645,7 @@ namespace { */ void voronoiDistances(const ConnectivityMap& cmap, const std::vector& distance_matrix, - std::map& conns) { + std::unordered_map& conns) { const int width = cmap.size().width(); const int height = cmap.size().height(); @@ -770,7 +785,7 @@ void Despeckle::despeckleInPlace(BinaryImage& image, // Now build a bidirectional map of distances between neighboring // connected components. - typedef std::map Connections; // conn -> sqdist + typedef std::unordered_map Connections; // conn -> sqdist Connections conns; voronoiDistances(cmap, distance_matrix, conns); diff --git a/ImageId.h b/ImageId.h index 314e0099a..d1e2e2c0c 100644 --- a/ImageId.h +++ b/ImageId.h @@ -83,7 +83,7 @@ bool operator<(const ImageId& lhs, const ImageId& rhs); namespace std { template<> struct hash { - std::size_t operator()(const ImageId& imageId) const noexcept { + size_t operator()(const ImageId& imageId) const noexcept { return (hash()(imageId.filePath().toStdString()) ^ hash()(imageId.page()) << 1); } diff --git a/PageId.h b/PageId.h index f829846cb..16148c319 100644 --- a/PageId.h +++ b/PageId.h @@ -86,7 +86,7 @@ bool operator<(const PageId& lhs, const PageId& rhs); namespace std { template<> struct hash { - std::size_t operator()(const PageId& pageId) const noexcept { + size_t operator()(const PageId& pageId) const noexcept { return (hash()(pageId.imageId()) ^ hash()(pageId.subPage()) << 1); } diff --git a/ProjectPages.cpp b/ProjectPages.cpp index e42fdb75e..13777bb28 100644 --- a/ProjectPages.cpp +++ b/ProjectPages.cpp @@ -344,7 +344,7 @@ ProjectPages::toImageFileInfo() const { } void ProjectPages::updateMetadataFrom(const std::vector& files) { - typedef std::map MetadataMap; + typedef std::unordered_map MetadataMap; MetadataMap metadata_map; for (const ImageFileInfo& file : files) { diff --git a/ProjectReader.h b/ProjectReader.h index ee6d9573b..c27e82364 100644 --- a/ProjectReader.h +++ b/ProjectReader.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include class QDomElement; class ProjectPages; @@ -89,10 +89,10 @@ class ProjectReader { } }; - typedef std::map DirMap; - typedef std::map FileMap; - typedef std::map ImageMap; - typedef std::map PageMap; + typedef std::unordered_map DirMap; + typedef std::unordered_map FileMap; + typedef std::unordered_map ImageMap; + typedef std::unordered_map PageMap; void processDirectories(const QDomElement& dirs_el); diff --git a/ProjectWriter.h b/ProjectWriter.h index 14ee0fcd1..eb765bb96 100644 --- a/ProjectWriter.h +++ b/ProjectWriter.h @@ -33,7 +33,7 @@ #include #include #include -#include +#include class AbstractFilter; class ProjectPages; @@ -110,7 +110,7 @@ DECLARE_NON_COPYABLE(ProjectWriter) class Sequenced; - typedef std::map MetadataByImage; + typedef std::unordered_map MetadataByImage; typedef boost::multi_index::multi_index_container< Directory, diff --git a/filters/deskew/Settings.cpp b/filters/deskew/Settings.cpp index 940e20379..e042be3ca 100644 --- a/filters/deskew/Settings.cpp +++ b/filters/deskew/Settings.cpp @@ -99,7 +99,7 @@ namespace deskew { bool Settings::isParamsNull(const PageId& page_id) const { QMutexLocker locker(&m_mutex); - return m_perPageParams.find(page_id) == m_perPageParams.end(); + return (m_perPageParams.find(page_id) == m_perPageParams.end()); } const DeviationProvider& Settings::deviationProvider() const { diff --git a/filters/deskew/Settings.h b/filters/deskew/Settings.h index af796edab..5cad77f4c 100644 --- a/filters/deskew/Settings.h +++ b/filters/deskew/Settings.h @@ -25,7 +25,7 @@ #include "Params.h" #include #include -#include +#include #include #include @@ -57,7 +57,7 @@ namespace deskew { const DeviationProvider& deviationProvider() const; private: - typedef std::map PerPageParams; + typedef std::unordered_map PerPageParams; mutable QMutex m_mutex; PerPageParams m_perPageParams; diff --git a/filters/fix_orientation/Settings.cpp b/filters/fix_orientation/Settings.cpp index 24bdf66c3..269075203 100644 --- a/filters/fix_orientation/Settings.cpp +++ b/filters/fix_orientation/Settings.cpp @@ -76,6 +76,6 @@ namespace fix_orientation { bool Settings::isRotationNull(const ImageId& image_id) const { QMutexLocker locker(&m_mutex); - return m_perImageRotation.find(image_id) == m_perImageRotation.end(); + return (m_perImageRotation.find(image_id) == m_perImageRotation.end()); } } // namespace fix_orientation \ No newline at end of file diff --git a/filters/fix_orientation/Settings.h b/filters/fix_orientation/Settings.h index 877d8f6e4..9fff17de3 100644 --- a/filters/fix_orientation/Settings.h +++ b/filters/fix_orientation/Settings.h @@ -25,7 +25,7 @@ #include "ImageId.h" #include "PageId.h" #include -#include +#include #include class AbstractRelinker; @@ -52,7 +52,7 @@ namespace fix_orientation { bool isRotationNull(const ImageId& image_id) const; private: - typedef std::map PerImageRotation; + typedef std::unordered_map PerImageRotation; void setImageRotationLocked(const ImageId& image_id, const OrthogonalRotation& rotation); diff --git a/filters/output/Settings.cpp b/filters/output/Settings.cpp index e0bf0c0e8..d5fe27618 100644 --- a/filters/output/Settings.cpp +++ b/filters/output/Settings.cpp @@ -112,8 +112,8 @@ namespace output { void Settings::setColorParams(const PageId& page_id, const ColorParams& prms) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setColorParams(prms); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -125,8 +125,8 @@ namespace output { void Settings::setPictureShapeOptions(const PageId& page_id, PictureShapeOptions picture_shape_options) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setPictureShapeOptions(picture_shape_options); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -138,8 +138,8 @@ namespace output { void Settings::setDpi(const PageId& page_id, const Dpi& dpi) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setOutputDpi(dpi); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -151,8 +151,8 @@ namespace output { void Settings::setDewarpingOptions(const PageId& page_id, const DewarpingOptions& opt) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setDewarpingOptions(opt); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -164,8 +164,8 @@ namespace output { void Settings::setSplittingOptions(const PageId& page_id, const SplittingOptions& opt) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setSplittingOptions(opt); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -177,8 +177,8 @@ namespace output { void Settings::setDistortionModel(const PageId& page_id, const dewarping::DistortionModel& model) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setDistortionModel(model); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -190,8 +190,8 @@ namespace output { void Settings::setDepthPerception(const PageId& page_id, const DepthPerception& depth_perception) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setDepthPerception(depth_perception); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); @@ -203,8 +203,8 @@ namespace output { void Settings::setDespeckleLevel(const PageId& page_id, DespeckleLevel level) { const QMutexLocker locker(&m_mutex); - const auto it(m_perPageParams.lower_bound(page_id)); - if ((it == m_perPageParams.end()) || m_perPageParams.key_comp()(page_id, it->first)) { + const auto it(m_perPageParams.find(page_id)); + if (it == m_perPageParams.end()) { Params params; params.setDespeckleLevel(level); m_perPageParams.insert(it, PerPageParams::value_type(page_id, params)); diff --git a/filters/output/Settings.h b/filters/output/Settings.h index ee84b7f88..fef14b3ef 100644 --- a/filters/output/Settings.h +++ b/filters/output/Settings.h @@ -33,7 +33,7 @@ #include "PropertySet.h" #include "OutputProcessingParams.h" #include -#include +#include #include class AbstractRelinker; @@ -106,10 +106,10 @@ namespace output { void setOutputProcessingParams(const PageId& page_id, const OutputProcessingParams& output_processing_params); private: - typedef std::map PerPageParams; - typedef std::map PerPageOutputParams; - typedef std::map PerPageZones; - typedef std::map PerPageOutputProcessingParams; + typedef std::unordered_map PerPageParams; + typedef std::unordered_map PerPageOutputParams; + typedef std::unordered_map PerPageZones; + typedef std::unordered_map PerPageOutputProcessingParams; static PropertySet initialPictureZoneProps(); diff --git a/filters/page_layout/OptionsWidget.h b/filters/page_layout/OptionsWidget.h index a3c0444fa..48c24e20a 100644 --- a/filters/page_layout/OptionsWidget.h +++ b/filters/page_layout/OptionsWidget.h @@ -28,7 +28,7 @@ #include "PageId.h" #include #include -#include +#include #include #include @@ -102,7 +102,7 @@ namespace page_layout { void applyAlignment(const std::set& pages); private: - typedef std::map AlignmentByButton; + typedef std::unordered_map AlignmentByButton; void updateMarginsDisplay(); diff --git a/filters/page_split/Settings.cpp b/filters/page_split/Settings.cpp index 7fae8505e..df36a105a 100644 --- a/filters/page_split/Settings.cpp +++ b/filters/page_split/Settings.cpp @@ -103,9 +103,8 @@ namespace page_split { } void Settings::updatePageLocked(const ImageId& image_id, const UpdateAction& action) { - auto it(m_perPageRecords.lower_bound(image_id)); - if ((it == m_perPageRecords.end()) - || m_perPageRecords.key_comp()(image_id, it->first)) { + auto it(m_perPageRecords.find(image_id)); + if (it == m_perPageRecords.end()) { // No record exists for this page. Record record(m_defaultLayoutType); @@ -144,9 +143,8 @@ namespace page_split { Settings::Record Settings::conditionalUpdate(const ImageId& image_id, const UpdateAction& action, bool* conflict) { QMutexLocker locker(&m_mutex); - auto it(m_perPageRecords.lower_bound(image_id)); - if ((it == m_perPageRecords.end()) - || m_perPageRecords.key_comp()(image_id, it->first)) { + auto it(m_perPageRecords.find(image_id)); + if (it == m_perPageRecords.end()) { // No record exists for this page. Record record(m_defaultLayoutType); diff --git a/filters/page_split/Settings.h b/filters/page_split/Settings.h index 343d91e09..a2d26f2e2 100644 --- a/filters/page_split/Settings.h +++ b/filters/page_split/Settings.h @@ -28,7 +28,7 @@ #include "PageId.h" #include #include -#include +#include #include class AbstractRelinker; @@ -174,7 +174,7 @@ namespace page_split { Record conditionalUpdate(const ImageId& image_id, const UpdateAction& action, bool* conflict = nullptr); private: - typedef std::map PerPageRecords; + typedef std::unordered_map PerPageRecords; Record getPageRecordLocked(const ImageId& image_id) const; diff --git a/filters/select_content/Settings.cpp b/filters/select_content/Settings.cpp index be1bb800f..7c862019f 100644 --- a/filters/select_content/Settings.cpp +++ b/filters/select_content/Settings.cpp @@ -95,7 +95,7 @@ namespace select_content { bool Settings::isParamsNull(const PageId& page_id) const { QMutexLocker locker(&m_mutex); - return m_pageParams.find(page_id) == m_pageParams.end(); + return (m_pageParams.find(page_id) == m_pageParams.end()); } QSizeF Settings::pageDetectionBox() const { diff --git a/filters/select_content/Settings.h b/filters/select_content/Settings.h index bb0995b03..a8dc8b47a 100644 --- a/filters/select_content/Settings.h +++ b/filters/select_content/Settings.h @@ -25,7 +25,7 @@ #include "Params.h" #include #include -#include +#include #include class AbstractRelinker; @@ -62,7 +62,7 @@ namespace select_content { const DeviationProvider& deviationProvider() const; private: - typedef std::map PageParams; + typedef std::unordered_map PageParams; mutable QMutex m_mutex; PageParams m_pageParams; From af2074b794f52f19e66eed3f3e895ca8ba894bc7 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 13:59:08 +0300 Subject: [PATCH 24/28] ~ ImageViewBase changes: * The paintEvent() changes from ver. Experimental; * The downscaled image version is now 200 dpi (was 300). --- ImageViewBase.cpp | 60 +++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/ImageViewBase.cpp b/ImageViewBase.cpp index 7d0a3581b..dc69bdaf6 100644 --- a/ImageViewBase.cpp +++ b/ImageViewBase.cpp @@ -254,7 +254,7 @@ QImage ImageViewBase::createDownscaledImage(const QImage& image) { // Original and downscaled DPM. const Dpm o_dpm(image); - const Dpm d_dpm(Dpi(300, 300)); + const Dpm d_dpm(Dpi(200, 200)); const int o_w = image.width(); const int o_h = image.height(); @@ -439,61 +439,33 @@ void ImageViewBase::paintEvent(QPaintEvent* event) { // Width of a source pixel in mm, as it's displayed on screen. const double pixel_width = widthMM() * xscale / width(); + // Make clipping smooth. + painter.setRenderHint(QPainter::Antialiasing, true); + // Disable antialiasing for large zoom levels. painter.setRenderHint(QPainter::SmoothPixmapTransform, pixel_width < 0.5); if (validateHqPixmap()) { // HQ pixmap maps one to one to screen pixels, so antialiasing is not necessary. painter.setRenderHint(QPainter::SmoothPixmapTransform, false); + + QPainterPath clip_path; + clip_path.addPolygon(m_virtualToWidget.map(m_virtualImageCropArea)); + painter.setClipPath(clip_path); + painter.drawPixmap(m_hqPixmapPos, m_hqPixmap); } else { scheduleHqVersionRebuild(); - painter.setWorldTransform( - m_pixmapToImage * m_imageToVirtual * m_virtualToWidget - ); - PixmapRenderer::drawPixmap(painter, m_pixmap); - } - - painter.setRenderHints(QPainter::Antialiasing, true); - painter.setWorldMatrixEnabled(false); + QTransform const pixmap_to_virtual(m_pixmapToImage * m_imageToVirtual); + painter.setWorldTransform(pixmap_to_virtual * m_virtualToWidget); - // Cover parts of the image that should not be visible with background. - // Note that because of Qt::WA_OpaquePaintEvent attribute, we need - // to paint the whole widget, which we do here. + QPainterPath clip_path; + clip_path.addPolygon(pixmap_to_virtual.inverted().map(m_virtualImageCropArea)); + painter.setClipPath(clip_path); - const QPolygonF image_area( - PolygonUtils::round( - m_virtualToWidget.map( - m_imageToVirtual.map(QRectF(m_image.rect())) - ) - ) - ); - const QPolygonF crop_area( - PolygonUtils::round(m_virtualToWidget.map(m_virtualImageCropArea)) - ); - - const QPolygonF intersected_area( - PolygonUtils::round(image_area.intersected(crop_area)) - ); - - QPainterPath intersected_path; - intersected_path.addPolygon(intersected_area); - - QPainterPath containing_path; - containing_path.addRect(viewport()->rect()); - - const QBrush brush(palette().color(QPalette::Window)); - QPen pen(brush, 1.0); - pen.setCosmetic(true); - - // By using a pen with the same color as the brush, we essentially - // expanding the area we are going to draw. It's necessary because - // XRender doesn't provide subpixel accuracy. - - painter.setPen(pen); - painter.setBrush(brush); - painter.drawPath(containing_path.subtracted(intersected_path)); + PixmapRenderer::drawPixmap(painter, m_pixmap); + } painter.restore(); From dc3eb4a70f36c7f8bf5ed56b81a45e5fd645fb9c Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 14:20:44 +0300 Subject: [PATCH 25/28] ~ Update README & VERSION. --- README.md | 29 ++++++++++++++++++----------- version.h | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8a22aaf9a..36034915b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ brings new ones and fixes. * [**Scan Tailor Enhanced** features](#scan-tailor-enhanced-features) * [Auto margins \[improved\]](#auto-margins-improved) * [Page detect \[reworked\]](#page-detect-reworked) - * [Deviation](#deviation) + * [Deviation \[reworked\]](#deviation-reworked) * [Picture shape \[reworked\]](#picture-shape-reworked) * [Multi column thumbnails view \[reworked\]](#multi-column-thumbnails-view-reworked) * [**Scan Tailor Featured** features](#scan-tailor-featured-features) @@ -76,11 +76,13 @@ Features *This feature has been reworked and is now a part of the [page area](#page-area) feature.* -* ##### Deviation +* ##### Deviation \[reworked\] Deviation feature enables highlighting of different pages. Highlighted in red are pages from Deskew filter with too high skew, from Select Content filter pages with different size of content and in Margins filter are highlighted pages which does not match others. + *This feature has been reworked. See [Scan Tailor Advanced fixes & improvements](#scan-tailor-advanced-fixes--improvements) for more information.* + * ##### Picture shape \[reworked\] Picture shape feature adds option for mixed pages to choose from free shape and rectangular shape images. This patch does not improve the original algoritm but creates from the @@ -149,16 +151,16 @@ Features #### **Scan Tailor Advanced** features * ##### Scan Tailor Advanced fixes & improvements -1. Portability. +* Portability. The setting is stored in the folder with a program. -2. Page splitting had an influence on output only in b&w mode with dewarping disabled. +* Page splitting had an influence on output only in b&w mode with dewarping disabled. Now it works in all the modes. -3. Page layout and all the other views now consider splitting settings. +* Page layout and all the other views now consider splitting settings. Corresponding improvements are done to thumbnails. -4. Changed Scan Tailor behavior on page split stage. +* Changed Scan Tailor behavior on page split stage. 1. Reworked apply cut feature. Now on applying cut to the pages with different dimensions than the page the cut applied to, Scan Tailor tries to adapt cutters instead of fully rejecting the cut setting and switching to auto mode for those pages as it was before. @@ -167,18 +169,23 @@ Features 3. UI: Added cutters interaction between each other. They can't more intersect each other, which created a wrong page layout configuration before. -5. Optimized memory usage on the output stage. +* Optimized memory usage on the output stage. -6. Reworking on [multi column thumbnails view](#multi-column-thumbnails-view-reworked) feature from ver. Enhanced. +* Reworking on [multi column thumbnails view](#multi-column-thumbnails-view-reworked) feature from ver. Enhanced. Now thumbnails are shown evenly. -7. Added option to control highlighting (with red asterisks) the thumbnails of pages with high deviation. +* Added option to control highlighting (with red asterisks) the thumbnails of pages with high deviation. The option refreshes the thumbnails instantly. -8. Support for processing of images with light content on dark background. +* Support for processing of images with light content on dark background. Now that kind of images can correctly be handled on all the stages. Many book covers are examples of such images. -9. Fixed other bugs of official, Enhanced and Featured versions and made lots of other improvements. +* Deviation feature reworked. + 1. A deviation provider implemented. + It supports caching and recalculates the values on demand. There isn't more any necessity to store deviation in page parameters and so in the project file, that approach caused some problems as the deviation is not actually a page parameter and depends on all the pages in the project. + 2. Added sorting by decreasing deviation. + +* Fixed other bugs of official, Enhanced and Featured versions and made lots of other improvements. * ##### Light and Dark color schemes You can choose a desired color scheme in settings. diff --git a/version.h b/version.h index 5659629f0..e0ac13349 100644 --- a/version.h +++ b/version.h @@ -19,8 +19,8 @@ #ifndef SCANTAILOR_VERSION_H_ #define SCANTAILOR_VERSION_H_ -#define VERSION "1.0.9" +#define VERSION "1.0.10" #define VERSION_QUAD "" // Must be "x.x.x.x" or an empty string. -#define PROJECT_VERSION 1 +#define PROJECT_VERSION 2 #endif From 28a1c7490b7985823e77814c2d74468c1956f73d Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 15:10:23 +0300 Subject: [PATCH 26/28] ~ Changed default parameters profiles saving scheme and added version check. --- DefaultParamsProfileManager.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/DefaultParamsProfileManager.cpp b/DefaultParamsProfileManager.cpp index fa980e43d..159b60161 100644 --- a/DefaultParamsProfileManager.cpp +++ b/DefaultParamsProfileManager.cpp @@ -3,6 +3,7 @@ #include #include "DefaultParamsProfileManager.h" #include "DefaultParams.h" +#include "version.h" using namespace page_split; using namespace output; @@ -55,12 +56,22 @@ std::unique_ptr DefaultParamsProfileManager::readProfile(const QS profileFile.close(); - return std::make_unique(doc.documentElement()); + const QDomElement profileElement(doc.documentElement()); + const QString version = profileElement.attribute("version"); + if (version.isNull() || (version.toInt() != PROJECT_VERSION)) { + return nullptr; + } + const QDomElement defaultParamsElement(profileElement.namedItem("default-params").toElement()); + + return std::make_unique(defaultParamsElement); } bool DefaultParamsProfileManager::writeProfile(const DefaultParams& params, const QString& name) const { QDomDocument doc; - doc.appendChild(params.toXml(doc, "profile")); + QDomElement rootElement(doc.createElement("profile")); + doc.appendChild(rootElement); + rootElement.setAttribute("version", PROJECT_VERSION); + rootElement.appendChild(params.toXml(doc, "default-params")); QDir dir(path); if (!dir.exists()) { From 9b0c7fdafe4d77da0816c0f31c488911ac18bdfc Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 15:24:39 +0300 Subject: [PATCH 27/28] ~ Update translations. --- filters/output/ui/OutputOptionsWidget.ui | 8 +- translations/scantailor_ru.ts | 178 ++++++++++++++++++----- translations/scantailor_untranslated.ts | 176 +++++++++++++++++----- ui/DefaultParamsDialog.ui | 6 +- 4 files changed, 284 insertions(+), 84 deletions(-) diff --git a/filters/output/ui/OutputOptionsWidget.ui b/filters/output/ui/OutputOptionsWidget.ui index 18af1c2b4..f8f6c209d 100644 --- a/filters/output/ui/OutputOptionsWidget.ui +++ b/filters/output/ui/OutputOptionsWidget.ui @@ -86,7 +86,7 @@ 0 - -231 + 0 228 1215 @@ -593,7 +593,7 @@ - Red component adjustment. Negative value means the segmenter will be more sensitive to red and vice versa for positive values. + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. false @@ -628,7 +628,7 @@ - Green component adjustment. Negative value means the segmenter will be more sensitive to green and vice versa for positive values. + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. false @@ -663,7 +663,7 @@ - Blue component adjustment. Negative value means the segmenter will be more sensitive to blue and vice versa for positive values. + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. false diff --git a/translations/scantailor_ru.ts b/translations/scantailor_ru.ts index d5992e68d..6af2cdb9d 100644 --- a/translations/scantailor_ru.ts +++ b/translations/scantailor_ru.ts @@ -151,14 +151,14 @@ - + ... - + Reset Сбросить @@ -373,12 +373,42 @@ Цветовая сегментация - + + R + + + + + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. + Настройка красного компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к красному и наоборот для положительных. + + + + G + + + + + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. + Настройка зеленого компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к зеленому и наоборот для положительных. + + + + B + + + + + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. + Настройка синего компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к синему и наоборот для положительных. + + + Reduce noise: Уменьшить шум: - + Reduce the number of colors of the output image by grouping similar colors. Уменьшить количество цветов выходного изображения, группируя похожие цвета. @@ -398,7 +428,12 @@ Меньшее значение обозначает меньшее кол-во цветов в выходном изображении, значения 2 и 6 включительно гарантируют индексированое изображение. - + + Normalize + Нормализовать + + + Make dark and light gray gradients black and white respectively. Сделать темные и светлые оттенки серого черными и белыми соответственно. @@ -495,7 +530,12 @@ Чувствительность (%): - + + Higher search sensivity + Большая чувств. поиска + + + Output Resolution (DPI) Разрешение на выходе (DPI) @@ -628,12 +668,12 @@ - + Custom Выборочный - + Marginal По краям @@ -648,15 +688,15 @@ Источник - + - + Error Ошибка - + Error loading the profile. Ошибка при загрузке профиля. @@ -666,7 +706,7 @@ Это имя конфликтует со стандартным именем профиля. Введите другое имя. - + Error saving the profile. Ошибка при сохранении профиля. @@ -849,7 +889,7 @@ ImageViewBase - + Use the mouse wheel or +/- to zoom. When zoomed, dragging is possible. Используйте колесо мыши для увеличения. В увеличенном виде доступно перетаскивание. @@ -963,7 +1003,7 @@ - + Save Project Сохранить проект @@ -1205,7 +1245,7 @@ Отмена - + Insert before ... Вставить перед ... @@ -1225,7 +1265,7 @@ Вставить сюда ... - + Scan Tailor Projects Проекты Scan Tailor @@ -1238,13 +1278,13 @@ - + Error Ошибка - + Unable to open the project file. Не удалось открыть файл проекта. @@ -1259,7 +1299,7 @@ версия - + Output is not yet possible, as the final size of pages is not yet known. To determine it, run batch processing at "Select Content" or "Margins". Вывод невозможен, поскольку еще не известны итоговые размеры страниц. @@ -1276,7 +1316,7 @@ To determine it, run batch processing at "Select Content" or "Mar - + Error saving the project file! Ошибка при сохранении файла! @@ -1722,12 +1762,12 @@ To determine it, run batch processing at "Select Content" or "Mar - + Change ... Применить... - + Mode Режим @@ -1807,12 +1847,42 @@ To determine it, run batch processing at "Select Content" or "Mar Цветовая сегментация - + + R + + + + + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. + Настройка красного компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к красному и наоборот для положительных. + + + + G + + + + + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. + Настройка зеленого компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к зеленому и наоборот для положительных. + + + + B + + + + + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. + Настройка синего компонента. Отрицательные значения обозначают, что сегментер будет более чувствителен к синему и наоборот для положительных. + + + Reduce noise: Уменьшить шум: - + Reduce the number of colors of the output image by grouping similar colors. Уменьшить количество цветов выходного изображения, группируя похожие цвета. @@ -1832,7 +1902,12 @@ To determine it, run batch processing at "Select Content" or "Mar Меньшее значение обозначает меньшее кол-во цветов в выходном изображении, значения 2 и 6 включительно гарантируют индексированое изображение. - + + Normalize + Нормализовать + + + Make dark and light gray gradients black and white respectively. Сделать темные и светлые оттенки серого черными и белыми соответственно. @@ -1853,6 +1928,11 @@ To determine it, run batch processing at "Select Content" or "Mar + Higher search sensitivity + Большая чувств. поиска + + + @@ -2781,7 +2861,7 @@ You should remove them from the project. Позиция выбранной страницы в текущем порядке. - + Page information. Информация о странице. @@ -2791,9 +2871,9 @@ You should remove them from the project. Имя страницы и тип. - - p. %1 - с. %1 + + p. %1 / %2 + с. %1 / %2 @@ -2964,7 +3044,17 @@ You should remove them from the project. deskew::Filter - + + Natural order + Естественный порядок + + + + Order by decreasing deviation + Сортировка по убывающему отклонению + + + Deskew Компенсация наклона @@ -3097,7 +3187,7 @@ You should remove them from the project. Прямоугольная - + Apply Splitting Settings Применить настройки разделения @@ -3112,8 +3202,8 @@ You should remove them from the project. Применить восприятие глубины - - + + Off Отключено @@ -3141,7 +3231,7 @@ You should remove them from the project. output::TabbedImageView - + Use Ctrl+1..5 to switch the tabs. Используйте Ctrl+1..5 для переключения табов. @@ -3149,12 +3239,12 @@ You should remove them from the project. output::Task::UiUpdater - + Picture zones are only available in Mixed mode. Зоны картинок доступны только в режиме "Смешанный". - + Despeckling can't be done in Color / Grayscale mode. Удаление пятен не делается в режиме "Цветной / Серый". @@ -3187,7 +3277,7 @@ You should remove them from the project. page_layout::Filter - + Natural order Естественный порядок @@ -3201,6 +3291,11 @@ You should remove them from the project. Order by increasing height Сортировка по возрастающей высоте + + + Order by decreasing deviation + Сортировка по убывающему отклонению + Margins @@ -3218,7 +3313,7 @@ You should remove them from the project. page_layout::OptionsWidget - + Apply Margins Применить поля @@ -3281,7 +3376,7 @@ You should remove them from the project. select_content::Filter - + Natural order Естественный порядок @@ -3295,6 +3390,11 @@ You should remove them from the project. Order by increasing height Сортировка по возрастающей высоте + + + Order by decreasing deviation + Сортировка по убывающему отклонению + Select Content diff --git a/translations/scantailor_untranslated.ts b/translations/scantailor_untranslated.ts index b3d369c9d..45714aea6 100644 --- a/translations/scantailor_untranslated.ts +++ b/translations/scantailor_untranslated.ts @@ -151,14 +151,14 @@ - + ... - + Reset @@ -373,12 +373,42 @@ - + + R + + + + + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. + + + + + G + + + + + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. + + + + + B + + + + + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. + + + + Reduce noise: - + Reduce the number of colors of the output image by grouping similar colors. @@ -398,7 +428,12 @@ - + + Normalize + + + + Make dark and light gray gradients black and white respectively. @@ -495,7 +530,12 @@ - + + Higher search sensivity + + + + Output Resolution (DPI) @@ -628,12 +668,12 @@ - + Custom - + Marginal @@ -648,15 +688,15 @@ - + - + Error - + Error loading the profile. @@ -666,7 +706,7 @@ - + Error saving the profile. @@ -847,7 +887,7 @@ ImageViewBase - + Use the mouse wheel or +/- to zoom. When zoomed, dragging is possible. @@ -961,7 +1001,7 @@ - + Save Project @@ -1203,7 +1243,7 @@ - + Insert before ... @@ -1223,7 +1263,7 @@ - + Scan Tailor Projects @@ -1236,13 +1276,13 @@ - + Error - + Unable to open the project file. @@ -1257,7 +1297,7 @@ - + Output is not yet possible, as the final size of pages is not yet known. To determine it, run batch processing at "Select Content" or "Margins". @@ -1273,7 +1313,7 @@ To determine it, run batch processing at "Select Content" or "Mar - + Error saving the project file! @@ -1716,12 +1756,12 @@ To determine it, run batch processing at "Select Content" or "Mar - + Change ... - + Mode @@ -1801,12 +1841,42 @@ To determine it, run batch processing at "Select Content" or "Mar - + + R + + + + + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. + + + + + G + + + + + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. + + + + + B + + + + + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. + + + + Reduce noise: - + Reduce the number of colors of the output image by grouping similar colors. @@ -1826,7 +1896,12 @@ To determine it, run batch processing at "Select Content" or "Mar - + + Normalize + + + + Make dark and light gray gradients black and white respectively. @@ -1847,6 +1922,11 @@ To determine it, run batch processing at "Select Content" or "Mar + Higher search sensitivity + + + + @@ -2761,7 +2841,7 @@ You should remove them from the project. - + Page information. @@ -2771,8 +2851,8 @@ You should remove them from the project. - - p. %1 + + p. %1 / %2 @@ -2944,7 +3024,17 @@ You should remove them from the project. deskew::Filter - + + Natural order + + + + + Order by decreasing deviation + + + + Deskew @@ -3077,7 +3167,7 @@ You should remove them from the project. - + Apply Splitting Settings @@ -3092,8 +3182,8 @@ You should remove them from the project. - - + + Off @@ -3121,7 +3211,7 @@ You should remove them from the project. output::TabbedImageView - + Use Ctrl+1..5 to switch the tabs. @@ -3129,12 +3219,12 @@ You should remove them from the project. output::Task::UiUpdater - + Picture zones are only available in Mixed mode. - + Despeckling can't be done in Color / Grayscale mode. @@ -3167,7 +3257,7 @@ You should remove them from the project. page_layout::Filter - + Natural order @@ -3181,6 +3271,11 @@ You should remove them from the project. Order by increasing height + + + Order by decreasing deviation + + Margins @@ -3198,7 +3293,7 @@ You should remove them from the project. page_layout::OptionsWidget - + Apply Margins @@ -3261,7 +3356,7 @@ You should remove them from the project. select_content::Filter - + Natural order @@ -3275,6 +3370,11 @@ You should remove them from the project. Order by increasing height + + + Order by decreasing deviation + + Select Content diff --git a/ui/DefaultParamsDialog.ui b/ui/DefaultParamsDialog.ui index 32b883fa2..dc4f8ef96 100644 --- a/ui/DefaultParamsDialog.ui +++ b/ui/DefaultParamsDialog.ui @@ -2074,7 +2074,7 @@ - Red component adjustment. Negative value means the segmenter will be more sensitive to red and vice versa for positive values. + Red component adjustment. A negative value means the segmenter will be more sensitive to red and vice versa for a positive one. false @@ -2109,7 +2109,7 @@ - Green component adjustment. Negative value means the segmenter will be more sensitive to green and vice versa for positive values. + Green component adjustment. A negative value means the segmenter will be more sensitive to green and vice versa for a positive one. false @@ -2144,7 +2144,7 @@ - Blue component adjustment. Negative value means the segmenter will be more sensitive to blue and vice versa for positive values. + Blue component adjustment. A negative value means the segmenter will be more sensitive to blue and vice versa for a positive one. false From a5ee3507d76ada77501a2045026580222714d436 Mon Sep 17 00:00:00 2001 From: Alex <4lex49@zoho.com> Date: Sun, 18 Feb 2018 17:05:26 +0300 Subject: [PATCH 28/28] ~ Fixed dependency mistake. (Relates to f4a32c4cf71b1472b616a3524a67f5dfcf9cd13b) --- ImageViewBase.cpp | 9 ++++----- ImageViewInfoObserver.h | 4 ++++ StatusBarPanel.h | 2 +- Utils.h | 12 +++++++++++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ImageViewBase.cpp b/ImageViewBase.cpp index dc69bdaf6..2918e3612 100644 --- a/ImageViewBase.cpp +++ b/ImageViewBase.cpp @@ -21,20 +21,19 @@ #include "PixmapRenderer.h" #include "BackgroundExecutor.h" #include "Dpm.h" -#include "Dpi.h" #include "ScopedIncDec.h" #include "imageproc/PolygonUtils.h" #include "imageproc/Transform.h" #include "OpenGLSupport.h" #include "ColorSchemeManager.h" #include "UnitsProvider.h" -#include "StatusBarPanel.h" +#include "Utils.h" +#include #include #include #include #include #include -#include #include #include #include @@ -595,8 +594,8 @@ void ImageViewBase::showEvent(QShowEvent* event) { QWidget::showEvent(event); if (auto* mainWindow = dynamic_cast(window())) { - if (auto* statusBarPanel = mainWindow->statusBar()->findChild()) { - statusBarPanel->setInfoProvider(&infoProvider()); + if (auto* infoObserver = Utils::castOrFindChild(mainWindow->statusBar())) { + infoObserver->setInfoProvider(&infoProvider()); } } } diff --git a/ImageViewInfoObserver.h b/ImageViewInfoObserver.h index 48f1d5614..67cebb1c5 100644 --- a/ImageViewInfoObserver.h +++ b/ImageViewInfoObserver.h @@ -6,6 +6,7 @@ #include #include +class ImageViewInfoProvider; class Dpi; class ImageViewInfoObserver { @@ -19,6 +20,9 @@ class ImageViewInfoObserver { virtual void updateDpi(const Dpi& dpi) = 0; virtual void clearImageViewInfo() = 0; + + virtual void setInfoProvider(ImageViewInfoProvider* infoProvider) { + } }; diff --git a/StatusBarPanel.h b/StatusBarPanel.h index deffa021e..f64d9866b 100644 --- a/StatusBarPanel.h +++ b/StatusBarPanel.h @@ -42,7 +42,7 @@ Q_OBJECT void updateUnits(Units) override; - void setInfoProvider(ImageViewInfoProvider* infoProvider); + void setInfoProvider(ImageViewInfoProvider* infoProvider) override; private: void mousePosChanged(); diff --git a/Utils.h b/Utils.h index 1d3475faf..b7f4b9b9e 100644 --- a/Utils.h +++ b/Utils.h @@ -104,11 +104,21 @@ Utils::mapSetValue(std::unordered_map& map, const K& ke template T Utils::castOrFindChild(QObject* object) { + if (object == nullptr) { + return nullptr; + } + if (auto result = dynamic_cast(object)) { return result; } else { - return object->findChild(); + for (QObject* child : object->children()) { + if (result = castOrFindChild(child)) { + return result; + } + } } + + return nullptr; } #endif // ifndef UTILS_H_