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