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