Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_colorize_stroke_strategy.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QBitArray>
10
11#include "krita_utils.h"
12#include "kis_paint_device.h"
13#include "kis_lazy_fill_tools.h"
14#include "kis_gaussian_kernel.h"
15#include "kis_painter.h"
17#include "kis_lod_transform.h"
18#include "kis_node.h"
19#include "kis_image_config.h"
20#include "KisWatershedWorker.h"
22
23#include "kis_transaction.h"
24
28
29using namespace KisLazyFillTools;
30
66
69 KisPaintDeviceSP filteredSource,
70 bool filteredSourceValid,
71 const QRect &boundingRect,
72 KisNodeSP progressNode,
73 bool prefilterOnly)
74 : KisRunnableBasedStrokeStrategy(QLatin1String("colorize-stroke"), prefilterOnly ? kundo2_i18n("Prefilter Colorize Mask") : kundo2_i18n("Colorize")),
75 m_d(new Private)
76{
77 m_d->progressNode = progressNode;
78 m_d->src = src;
79 m_d->dst = dst;
80 m_d->filteredSource = filteredSource;
81 m_d->boundingRect = boundingRect;
82 m_d->filteredSourceValid = filteredSourceValid;
83 m_d->prefilterOnly = prefilterOnly;
84
88
92}
93
96 m_d(new Private(*rhs.m_d, levelOfDetail))
97{
98 KisLodTransform t(levelOfDetail);
99 m_d->boundingRect = t.map(rhs.m_d->boundingRect);
100}
101
105
107{
108 m_d->filteringOptions = value;
109}
110
112{
113 return m_d->filteringOptions;
114}
115
117{
118 KoColor convertedColor(color);
119 convertedColor.convertTo(m_d->dst->colorSpace());
120
121 m_d->keyStrokes << KeyStroke(dev, convertedColor);
122}
123
125{
126 using namespace KritaUtils;
127
129
130 const QVector<QRect> patchRects =
131 splitRectIntoPatches(m_d->boundingRect, optimalPatchSize());
132
133 if (!m_d->filteredSourceValid) {
134 // TODO: make this conversion concurrent!!!
136 filteredMainDev->setDefaultBounds(m_d->src->defaultBounds());
137
138 struct PrefilterSharedState {
139 QRect boundingRect;
140 KisPaintDeviceSP filteredMainDev;
141 KisPaintDeviceSP filteredMainDevSavedCopy;
142 QScopedPointer<KisTransaction> activeTransaction;
143 FilteringOptions filteringOptions;
144 };
145
146 QSharedPointer<PrefilterSharedState> state(new PrefilterSharedState());
147 state->boundingRect = m_d->boundingRect;
148 state->filteredMainDev = filteredMainDev;
149 state->filteringOptions = m_d->filteringOptions;
150
151 if (m_d->filteringOptions.useEdgeDetection &&
152 m_d->filteringOptions.edgeDetectionSize > 0.0) {
153
154 addJobSequential(jobs, [state] () {
155 state->activeTransaction.reset(new KisTransaction(state->filteredMainDev));
156 });
157
158 Q_FOREACH (const QRect &rc, patchRects) {
159 addJobConcurrent(jobs, [state, rc] () {
160 KisLodTransformScalar t(state->filteredMainDev);
161 KisGaussianKernel::applyLoG(state->filteredMainDev,
162 rc,
163 t.scale(0.5 * state->filteringOptions.edgeDetectionSize),
164 -1.0,
165 QBitArray(), 0);
166 });
167 }
168
169 addJobSequential(jobs, [state] () {
170 state->activeTransaction.reset();
171 normalizeAlpha8Device(state->filteredMainDev, state->boundingRect);
172 state->activeTransaction.reset(new KisTransaction(state->filteredMainDev));
173 });
174
175 Q_FOREACH (const QRect &rc, patchRects) {
176 addJobConcurrent(jobs, [state, rc] () {
177 KisLodTransformScalar t(state->filteredMainDev);
178 KisGaussianKernel::applyGaussian(state->filteredMainDev,
179 rc,
180 t.scale(state->filteringOptions.edgeDetectionSize),
181 t.scale(state->filteringOptions.edgeDetectionSize),
182 QBitArray(), 0);
183 });
184 }
185
186 addJobSequential(jobs, [state] () {
187 state->activeTransaction.reset();
188 });
189 }
190
191 if (m_d->filteringOptions.fuzzyRadius > 0) {
192
193 addJobSequential(jobs, [state] () {
194 state->filteredMainDevSavedCopy = new KisPaintDevice(*state->filteredMainDev);
195 state->activeTransaction.reset(new KisTransaction(state->filteredMainDev));
196 });
197
198 Q_FOREACH (const QRect &rc, patchRects) {
199 addJobConcurrent(jobs, [state, rc] () {
200 KisLodTransformScalar t(state->filteredMainDev);
201 KisGaussianKernel::applyGaussian(state->filteredMainDev,
202 rc,
203 t.scale(state->filteringOptions.fuzzyRadius),
204 t.scale(state->filteringOptions.fuzzyRadius),
205 QBitArray(), 0);
206 KisPainter gc(state->filteredMainDev);
207 gc.bitBlt(rc.topLeft(), state->filteredMainDevSavedCopy, rc);
208 });
209 }
210
211 addJobSequential(jobs, [state] () {
212 state->activeTransaction.reset();
213 });
214 }
215
216 addJobSequential(jobs, [this, state] () {
217 normalizeAndInvertAlpha8Device(state->filteredMainDev, state->boundingRect);
218
219 KisDefaultBoundsBaseSP oldBounds = m_d->filteredSource->defaultBounds();
220 m_d->filteredSource->makeCloneFrom(state->filteredMainDev, m_d->boundingRect);
221 m_d->filteredSource->setDefaultBounds(oldBounds);
222 m_d->filteredSourceValid = true;
223 });
224 }
225
226 if (!m_d->prefilterOnly) {
227 addJobSequential(jobs, [this] () {
228 m_d->heightMap = new KisPaintDevice(*m_d->filteredSource);
229 });
230
231 Q_FOREACH (const QRect &rc, patchRects) {
232 addJobConcurrent(jobs, [this, rc] () {
233 KritaUtils::filterAlpha8Device(m_d->heightMap, rc,
234 [](quint8 pixel) {
235 return quint8(255 - pixel);
236 });
237 });
238 }
239
240 addJobSequential(jobs, [this] () {
241 m_d->progressHelper.reset(new KisProcessingVisitor::ProgressHelper(m_d->progressNode));
242
243 KisWatershedWorker worker(m_d->heightMap, m_d->dst, m_d->boundingRect, m_d->progressHelper->updater());
244 Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) {
245 KoColor color =
246 !stroke.isTransparent ?
247 stroke.color : KoColor::createTransparent(m_d->dst->colorSpace());
248
249 worker.addKeyStroke(stroke.dev, color);
250 }
251 worker.run(m_d->filteringOptions.cleanUpAmount);
252 m_d->progressHelper.reset();
253 });
254 }
255
256 addJobSequential(jobs, [this] () {
257 Q_EMIT sigFinished(m_d->prefilterOnly);
258 });
259
261}
262
267
269{
270 // NOTE: this method may be called by the GUI thread asynchronously!
272 if (helper) {
273 helper->cancel();
274 }
275}
276
278{
279 KisImageConfig cfg(true);
280 if (!cfg.useLodForColorizeMask()) return 0;
281
282 KisColorizeStrokeStrategy *clone = new KisColorizeStrokeStrategy(*this, levelOfDetail);
283 return clone;
284}
float value(const T *src, size_t ch)
void setFilteringOptions(const KisLazyFillTools::FilteringOptions &value)
const QScopedPointer< Private > m_d
void tryCancelCurrentStrokeJobAsync() override
tryCancelCurrentStrokeJobAsync is called by the strokes queue when the stroke is being cancelled....
KisColorizeStrokeStrategy(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisPaintDeviceSP filteredSource, bool filteredSourceValid, const QRect &boundingRect, KisNodeSP progressNode, bool prefilterOnly=false)
KisStrokeStrategy * createLodClone(int levelOfDetail) override
KisLazyFillTools::FilteringOptions filteringOptions() const
void addKeyStroke(KisPaintDeviceSP dev, const KoColor &color)
void sigFinished(bool prefilterOnly)
static void applyGaussian(KisPaintDeviceSP device, const QRect &rect, qreal xRadius, qreal yRadius, const QBitArray &channelFlags, KoUpdater *updater, bool createTransaction=false, KisConvolutionBorderOp borderOp=BORDER_REPEAT)
static void applyLoG(KisPaintDeviceSP device, const QRect &rect, qreal radius, qreal coeff, const QBitArray &channelFlags, KoUpdater *progressUpdater)
bool useLodForColorizeMask(bool requestDefault=false) const
qreal scale(qreal value) const
KisPaintInformation map(KisPaintInformation pi) const
void setDefaultBounds(KisDefaultBoundsBaseSP bounds)
static KisPaintDeviceSP convertToAlphaAsAlpha(KisPaintDeviceSP src)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
KisRunnableStrokeJobsInterface * runnableJobsInterface() const
virtual void addRunnableJobs(const QVector< KisRunnableStrokeJobDataBase * > &list)=0
void enableJob(JobType type, bool enable=true, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void setClearsRedoOnStart(bool value)
void setRequestsOtherStrokesToEnd(bool value)
void setNeedsExplicitCancel(bool value)
void run(qreal cleanUpAmount=0.0)
run the filling process using the passes height map, strokes, and write the result coloring into the ...
void addKeyStroke(KisPaintDeviceSP dev, const KoColor &color)
Adds a key stroke to the worker.
void convertTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
Definition KoColor.cpp:136
static KoColor createTransparent(const KoColorSpace *cs)
Definition KoColor.cpp:681
KUndo2MagicString kundo2_i18n(const char *text)
void normalizeAndInvertAlpha8Device(KisPaintDeviceSP dev, const QRect &rect)
void normalizeAlpha8Device(KisPaintDeviceSP dev, const QRect &rect)
void filterAlpha8Device(KisPaintDeviceSP dev, const QRect &rc, std::function< quint8(quint8)> func)
QSharedPointer< KisProcessingVisitor::ProgressHelper > progressHelper
Private(const Private &rhs, int _levelOfDetail)