1 /*
2 * Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_filter_stroke_strategy.h"
20
21 #include <filter/kis_filter.h>
22 #include <filter/kis_filter_configuration.h>
23 #include <kis_transaction.h>
24 #include <KoCompositeOpRegistry.h>
25
26
27 struct KisFilterStrokeStrategy::Private {
PrivateKisFilterStrokeStrategy::Private28 Private()
29 : updatesFacade(0),
30 cancelSilently(false),
31 secondaryTransaction(0),
32 levelOfDetail(0)
33 {
34 }
35
PrivateKisFilterStrokeStrategy::Private36 Private(const Private &rhs)
37 : filter(rhs.filter),
38 filterConfig(rhs.filterConfig),
39 node(rhs.node),
40 updatesFacade(rhs.updatesFacade),
41 cancelSilently(rhs.cancelSilently),
42 filterDevice(),
43 filterDeviceBounds(),
44 secondaryTransaction(0),
45 progressHelper(),
46 levelOfDetail(0)
47 {
48 KIS_ASSERT_RECOVER_RETURN(!rhs.filterDevice);
49 KIS_ASSERT_RECOVER_RETURN(rhs.filterDeviceBounds.isEmpty());
50 KIS_ASSERT_RECOVER_RETURN(!rhs.secondaryTransaction);
51 KIS_ASSERT_RECOVER_RETURN(!rhs.progressHelper);
52 KIS_ASSERT_RECOVER_RETURN(!rhs.levelOfDetail);
53 }
54
55 KisFilterSP filter;
56 KisFilterConfigurationSP filterConfig;
57 KisNodeSP node;
58 KisUpdatesFacade *updatesFacade;
59
60 bool cancelSilently;
61 KisPaintDeviceSP filterDevice;
62 QRect filterDeviceBounds;
63 KisTransaction *secondaryTransaction;
64 QScopedPointer<KisProcessingVisitor::ProgressHelper> progressHelper;
65
66 int levelOfDetail;
67 };
68
69
KisFilterStrokeStrategy(KisFilterSP filter,KisFilterConfigurationSP filterConfig,KisResourcesSnapshotSP resources)70 KisFilterStrokeStrategy::KisFilterStrokeStrategy(KisFilterSP filter,
71 KisFilterConfigurationSP filterConfig,
72 KisResourcesSnapshotSP resources)
73 : KisPainterBasedStrokeStrategy(QLatin1String("FILTER_STROKE"),
74 kundo2_i18n("Filter \"%1\"", filter->name()),
75 resources,
76 QVector<KisFreehandStrokeInfo*>(),false),
77 m_d(new Private())
78 {
79 m_d->filter = filter;
80 m_d->filterConfig = filterConfig;
81 m_d->node = resources->currentNode();
82 m_d->updatesFacade = resources->image().data();
83 m_d->cancelSilently = false;
84 m_d->secondaryTransaction = 0;
85 m_d->levelOfDetail = 0;
86
87 setSupportsWrapAroundMode(true);
88 enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
89 }
90
KisFilterStrokeStrategy(const KisFilterStrokeStrategy & rhs,int levelOfDetail)91 KisFilterStrokeStrategy::KisFilterStrokeStrategy(const KisFilterStrokeStrategy &rhs, int levelOfDetail)
92 : KisPainterBasedStrokeStrategy(rhs, levelOfDetail),
93 m_d(new Private(*rhs.m_d))
94 {
95 // only non-started transaction are allowed
96 KIS_ASSERT_RECOVER_NOOP(!m_d->secondaryTransaction);
97 m_d->levelOfDetail = levelOfDetail;
98 }
99
~KisFilterStrokeStrategy()100 KisFilterStrokeStrategy::~KisFilterStrokeStrategy()
101 {
102 delete m_d;
103 }
104
initStrokeCallback()105 void KisFilterStrokeStrategy::initStrokeCallback()
106 {
107 KisPainterBasedStrokeStrategy::initStrokeCallback();
108
109 KisPaintDeviceSP dev = targetDevice();
110 m_d->filterDeviceBounds = dev->extent();
111
112 if (m_d->filter->needsTransparentPixels(m_d->filterConfig.data(), dev->colorSpace())) {
113 m_d->filterDeviceBounds |= dev->defaultBounds()->bounds();
114 }
115
116 if (activeSelection() ||
117 (dev->colorSpace() != dev->compositionSourceColorSpace() &&
118 *dev->colorSpace() != *dev->compositionSourceColorSpace())) {
119
120 m_d->filterDevice = dev->createCompositionSourceDevice(dev);
121 m_d->secondaryTransaction = new KisTransaction(m_d->filterDevice);
122
123 if (activeSelection()) {
124 m_d->filterDeviceBounds &= activeSelection()->selectedRect();
125 }
126 } else {
127 m_d->filterDevice = dev;
128 }
129
130 m_d->progressHelper.reset(new KisProcessingVisitor::ProgressHelper(m_d->node));
131 }
132
doStrokeCallback(KisStrokeJobData * data)133 void KisFilterStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
134 {
135 Data *d = dynamic_cast<Data*>(data);
136 CancelSilentlyMarker *cancelJob =
137 dynamic_cast<CancelSilentlyMarker*>(data);
138
139 if (d) {
140 const QRect rc = d->processRect;
141
142 if (!m_d->filterDeviceBounds.intersects(
143 m_d->filter->neededRect(rc, m_d->filterConfig.data(), m_d->levelOfDetail))) {
144
145 return;
146 }
147
148 m_d->filter->processImpl(m_d->filterDevice, rc,
149 m_d->filterConfig.data(),
150 m_d->progressHelper->updater());
151
152 if (m_d->secondaryTransaction) {
153 KisPainter::copyAreaOptimized(rc.topLeft(), m_d->filterDevice, targetDevice(), rc, activeSelection());
154
155 // Free memory
156 m_d->filterDevice->clear(rc);
157 }
158
159 m_d->node->setDirty(rc);
160 } else if (cancelJob) {
161 m_d->cancelSilently = true;
162 } else {
163 qFatal("KisFilterStrokeStrategy: job type is not known");
164 }
165 }
166
cancelStrokeCallback()167 void KisFilterStrokeStrategy::cancelStrokeCallback()
168 {
169 delete m_d->secondaryTransaction;
170 m_d->filterDevice = 0;
171
172 if (m_d->cancelSilently) {
173 m_d->updatesFacade->disableDirtyRequests();
174 }
175
176 KisPainterBasedStrokeStrategy::cancelStrokeCallback();
177
178 if (m_d->cancelSilently) {
179 m_d->updatesFacade->enableDirtyRequests();
180 }
181 }
182
finishStrokeCallback()183 void KisFilterStrokeStrategy::finishStrokeCallback()
184 {
185 delete m_d->secondaryTransaction;
186 m_d->filterDevice = 0;
187
188 KisPainterBasedStrokeStrategy::finishStrokeCallback();
189 }
190
createLodClone(int levelOfDetail)191 KisStrokeStrategy* KisFilterStrokeStrategy::createLodClone(int levelOfDetail)
192 {
193 if (!m_d->filter->supportsLevelOfDetail(m_d->filterConfig.data(), levelOfDetail)) return 0;
194 if (!m_d->node->supportsLodPainting()) return 0;
195
196 KisFilterStrokeStrategy *clone = new KisFilterStrokeStrategy(*this, levelOfDetail);
197 return clone;
198 }
199