1 /*
2 Scan Tailor - Interactive post-processing tool for scanned pages.
3 Copyright (C) Joseph Artsimovich <joseph.artsimovich@gmail.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "Task.h"
20 #include <UnitsProvider.h>
21 #include <utility>
22 #include "Dpm.h"
23 #include "Filter.h"
24 #include "FilterData.h"
25 #include "FilterUiInterface.h"
26 #include "ImageView.h"
27 #include "OptionsWidget.h"
28 #include "Params.h"
29 #include "Settings.h"
30 #include "TaskStatus.h"
31 #include "Utils.h"
32 #include "filters/output/Task.h"
33
34 using namespace imageproc;
35
36 namespace page_layout {
37 class Task::UiUpdater : public FilterResult {
38 public:
39 UiUpdater(intrusive_ptr<Filter> filter,
40 intrusive_ptr<Settings> settings,
41 const PageId& page_id,
42 const QImage& image,
43 const ImageTransformation& xform,
44 const GrayImage& gray_image,
45 const QRectF& adapted_content_rect,
46 bool agg_size_changed,
47 bool batch);
48
49 void updateUI(FilterUiInterface* ui) override;
50
filter()51 intrusive_ptr<AbstractFilter> filter() override { return m_filter; }
52
53 private:
54 intrusive_ptr<Filter> m_filter;
55 intrusive_ptr<Settings> m_settings;
56 PageId m_pageId;
57 QImage m_image;
58 QImage m_downscaledImage;
59 GrayImage m_grayImage;
60 ImageTransformation m_xform;
61 QRectF m_adaptedContentRect;
62 bool m_aggSizeChanged;
63 bool m_batchProcessing;
64 };
65
66
Task(intrusive_ptr<Filter> filter,intrusive_ptr<output::Task> next_task,intrusive_ptr<Settings> settings,const PageId & page_id,bool batch,bool debug)67 Task::Task(intrusive_ptr<Filter> filter,
68 intrusive_ptr<output::Task> next_task,
69 intrusive_ptr<Settings> settings,
70 const PageId& page_id,
71 bool batch,
72 bool debug)
73 : m_filter(std::move(filter)),
74 m_nextTask(std::move(next_task)),
75 m_settings(std::move(settings)),
76 m_pageId(page_id),
77 m_batchProcessing(batch) {}
78
79 Task::~Task() = default;
80
process(const TaskStatus & status,const FilterData & data,const QRectF & page_rect,const QRectF & content_rect)81 FilterResultPtr Task::process(const TaskStatus& status,
82 const FilterData& data,
83 const QRectF& page_rect,
84 const QRectF& content_rect) {
85 status.throwIfCancelled();
86
87 const QSizeF content_size_mm(Utils::calcRectSizeMM(data.xform(), content_rect));
88
89 if (m_settings->isPageAutoMarginsEnabled(m_pageId)) {
90 const Margins& margins_mm = Utils::calcMarginsMM(data.xform(), page_rect, content_rect);
91 m_settings->setHardMarginsMM(m_pageId, margins_mm);
92 }
93
94 QSizeF agg_hard_size_before;
95 QSizeF agg_hard_size_after;
96 const Params params(m_settings->updateContentSizeAndGetParams(m_pageId, page_rect, content_rect, content_size_mm,
97 &agg_hard_size_before, &agg_hard_size_after));
98
99 const QRectF adapted_content_rect(Utils::adaptContentRect(data.xform(), content_rect));
100
101 if (m_nextTask) {
102 const QPolygonF content_rect_phys(data.xform().transformBack().map(adapted_content_rect));
103 const QPolygonF page_rect_phys(
104 Utils::calcPageRectPhys(data.xform(), content_rect_phys, params, agg_hard_size_after));
105
106 ImageTransformation new_xform(data.xform());
107 new_xform.setPostCropArea(Utils::shiftToRoundedOrigin(new_xform.transform().map(page_rect_phys)));
108
109 return m_nextTask->process(status, FilterData(data, new_xform), content_rect_phys);
110 } else {
111 return make_intrusive<UiUpdater>(m_filter, m_settings, m_pageId, data.origImage(), data.xform(),
112 data.isBlackOnWhite() ? data.grayImage() : data.grayImage().inverted(),
113 adapted_content_rect, agg_hard_size_before != agg_hard_size_after,
114 m_batchProcessing);
115 }
116 }
117
118 /*============================ Task::UiUpdater ==========================*/
119
UiUpdater(intrusive_ptr<Filter> filter,intrusive_ptr<Settings> settings,const PageId & page_id,const QImage & image,const ImageTransformation & xform,const GrayImage & gray_image,const QRectF & adapted_content_rect,const bool agg_size_changed,const bool batch)120 Task::UiUpdater::UiUpdater(intrusive_ptr<Filter> filter,
121 intrusive_ptr<Settings> settings,
122 const PageId& page_id,
123 const QImage& image,
124 const ImageTransformation& xform,
125 const GrayImage& gray_image,
126 const QRectF& adapted_content_rect,
127 const bool agg_size_changed,
128 const bool batch)
129 : m_filter(std::move(filter)),
130 m_settings(std::move(settings)),
131 m_pageId(page_id),
132 m_image(image),
133 m_downscaledImage(ImageView::createDownscaledImage(image)),
134 m_xform(xform),
135 m_grayImage(gray_image),
136 m_adaptedContentRect(adapted_content_rect),
137 m_aggSizeChanged(agg_size_changed),
138 m_batchProcessing(batch) {}
139
updateUI(FilterUiInterface * ui)140 void Task::UiUpdater::updateUI(FilterUiInterface* ui) {
141 // This function is executed from the GUI thread.
142 OptionsWidget* const opt_widget = m_filter->optionsWidget();
143 opt_widget->postUpdateUI();
144 ui->setOptionsWidget(opt_widget, ui->KEEP_OWNERSHIP);
145
146 if (m_aggSizeChanged) {
147 ui->invalidateAllThumbnails();
148 } else {
149 ui->invalidateThumbnail(m_pageId);
150 }
151
152 if (m_batchProcessing) {
153 return;
154 }
155
156 auto* view = new ImageView(m_settings, m_pageId, m_image, m_downscaledImage, m_grayImage, m_xform,
157 m_adaptedContentRect, *opt_widget);
158 ui->setImageWidget(view, ui->TRANSFER_OWNERSHIP);
159
160 QObject::connect(view, SIGNAL(invalidateThumbnail(const PageId&)), opt_widget,
161 SIGNAL(invalidateThumbnail(const PageId&)));
162 QObject::connect(view, SIGNAL(invalidateAllThumbnails()), opt_widget, SIGNAL(invalidateAllThumbnails()));
163 QObject::connect(view, SIGNAL(marginsSetLocally(const Margins&)), opt_widget,
164 SLOT(marginsSetExternally(const Margins&)));
165 QObject::connect(opt_widget, SIGNAL(marginsSetLocally(const Margins&)), view,
166 SLOT(marginsSetExternally(const Margins&)));
167 QObject::connect(opt_widget, SIGNAL(topBottomLinkToggled(bool)), view, SLOT(topBottomLinkToggled(bool)));
168 QObject::connect(opt_widget, SIGNAL(leftRightLinkToggled(bool)), view, SLOT(leftRightLinkToggled(bool)));
169 QObject::connect(opt_widget, SIGNAL(alignmentChanged(const Alignment&)), view,
170 SLOT(alignmentChanged(const Alignment&)));
171 QObject::connect(opt_widget, SIGNAL(aggregateHardSizeChanged()), view, SLOT(aggregateHardSizeChanged()));
172 } // Task::UiUpdater::updateUI
173 } // namespace page_layout