Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_texture_option.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2012 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2014 Mohit Goyal <mohit.bits2011@gmail.com>
4 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
9
10#include <QString>
11#include <QCheckBox>
12#include <QBuffer>
13#include <QFormLayout>
14#include <QPainter>
15#include <QBoxLayout>
16
17#include <klocalizedstring.h>
18
19#include <KoPattern.h>
20#include <KoAbstractGradient.h>
21#include <KoResource.h>
23#include <kis_paint_device.h>
24#include <kis_fill_painter.h>
25#include <kis_painter.h>
26#include <kis_iterator_ng.h>
28#include "KoMixColorsOp.h"
33
37
38/**********************************************************************/
39/* KisTextureOption */
40/**********************************************************************/
41
42
44 KisResourcesInterfaceSP resourcesInterface,
45 KoCanvasResourcesInterfaceSP canvasResourcesInterface,
46 int levelOfDetail,
47 KisBrushTextureFlags flags)
48 : m_gradient(0)
49 , m_levelOfDetail(levelOfDetail)
50 , m_strengthOption(setting)
51 , m_flags(flags)
52{
53 fillProperties(setting, resourcesInterface, canvasResourcesInterface);
54}
55
57{
59 data.read(setting);
60
61 if (data.textureData.isNull()) {
62 m_enabled = false;
63 return;
64 }
65
68
71 }
72
75 }
76
78
79 QString effectiveCompositeOp = COMPOSITE_OVER;
80 bool additionalInvert = false;
81
82 if (canvasResourcesInterface && data.autoInvertOnErase) {
83 effectiveCompositeOp = canvasResourcesInterface->resource(KoCanvasResource::CurrentEffectiveCompositeOp).toString();
84 KIS_SAFE_ASSERT_RECOVER (!effectiveCompositeOp.isEmpty()) {
85 effectiveCompositeOp = COMPOSITE_OVER;
86 }
87 additionalInvert = effectiveCompositeOp == COMPOSITE_ERASE;
88 }
89
91 if (!m_maskInfo->fillProperties(setting, resourcesInterface, additionalInvert)) {
92 warnKrita << "WARNING: Couldn't load the pattern for a stroke (KisTextureProperties)";
93 m_enabled = false;
94 return;
95 }
96
98
99 m_enabled = data.isEnabled;
100 m_offsetX = data.offsetX;
101 m_offsetY = data.offsetY;
102
103 if (m_texturingMode == KisTextureOptionData::GRADIENT && canvasResourcesInterface) {
104 KoAbstractGradientSP gradient = canvasResourcesInterface->resource(KoCanvasResource::CurrentGradient).value<KoAbstractGradientSP>()->cloneAndBakeVariableColors(canvasResourcesInterface);
105 if (gradient) {
106 m_gradient = gradient;
107 m_cachedGradient.setGradient(gradient, 256);
108 }
109 }
110}
111
113{
120
122 data.read(setting.data());
123
124 if (data.isEnabled && !data.textureData.patternBase64.isEmpty()) {
125 patterns << data.textureData.loadLinkedPattern(resourcesInterface);
126 }
127
128 return patterns;
129}
130
132{
139
141 data.read(setting.data());
142
143 if (data.isEnabled && data.textureData.patternBase64.isEmpty()) {
144 patterns << data.textureData.loadLinkedPattern(resourcesInterface);
145 }
146
147 return patterns;
148}
149
154
156{
158 data.read(settings);
159
161}
162
164{
166 data.read(settings);
167
168 return data.isEnabled && data.autoInvertOnErase;
169}
170
171
173 if (!m_enabled) return;
174 if (!m_maskInfo->isValid()) return;
175
176 KisPaintDeviceSP mask = m_maskInfo->mask();
177
178 const QRect rect = dab->bounds();
179 const QRect maskBounds = m_maskInfo->maskBounds();
180
182 KisPaintDeviceSP fillMaskDevice = g.device();
183
184 int x = offset.x() % maskBounds.width() - m_offsetX;
185 int y = offset.y() % maskBounds.height() - m_offsetY;
186
187 const QRect maskPatchRect = QRect(x, y, rect.width(), rect.height());
188
189 KisFillPainter fillMaskPainter(fillMaskDevice);
190 fillMaskPainter.setCompositeOpId(COMPOSITE_COPY);
191 fillMaskPainter.fillRect(kisGrowRect(maskPatchRect, 1), mask, maskBounds);
192 fillMaskPainter.end();
193
194 qreal pressure = m_strengthOption.apply(info);
195 quint8* dabData = dab->data();
196
197 KisSequentialConstIterator it(fillMaskDevice, QRect(x, y, rect.width(), rect.height()));
198 while (it.nextPixel()) {
199 const QRgb *maskQRgb = reinterpret_cast<const QRgb*>(it.oldRawData());
200 dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, maskQRgb, dabData, pressure, 1);
201 dabData += dab->pixelSize();
202 }
203}
204
206 if (!m_enabled) return;
207 if (!m_maskInfo->isValid()) return;
208
210
211 KisPaintDeviceSP mask = m_maskInfo->mask();
212 const QRect maskBounds = m_maskInfo->maskBounds();
213 QRect rect = dab->bounds();
214
216 KisPaintDeviceSP fillDevice = g.device();
217
218 int x = offset.x() % maskBounds.width() - m_offsetX;
219 int y = offset.y() % maskBounds.height() - m_offsetY;
220
221 const QRect maskPatchRect = QRect(x, y, rect.width(), rect.height());
222
223 KisFillPainter fillPainter(fillDevice);
224 fillPainter.setCompositeOpId(COMPOSITE_COPY);
225 fillPainter.fillRect(kisGrowRect(maskPatchRect, 1), mask, maskBounds);
226 fillPainter.end();
227
228 qreal pressure = m_strengthOption.apply(info);
229 quint8* dabData = dab->data();
230
231 //for gradient textures...
232 KoMixColorsOp* colorMix = dab->colorSpace()->mixColorsOp();
233 qint16 colorWeights[2];
234 colorWeights[0] = qRound(pressure * 255);
235 colorWeights[1] = 255 - colorWeights[0];
236 quint8* colors[2];
237 m_cachedGradient.setColorSpace(dab->colorSpace()); //Change colorspace here so we don't have to convert each pixel drawn
238
239 KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
240 for (int row = 0; row < rect.height(); ++row) {
241 for (int col = 0; col < rect.width(); ++col) {
242
243 const QRgb* maskQRgb = reinterpret_cast<const QRgb*>(iter->oldRawData());
244 qreal gradientvalue = qreal(qGray(*maskQRgb))/255.0;//qreal(*iter->oldRawData()) / 255.0;
245 KoColor paintcolor;
246 paintcolor.setColor(m_cachedGradient.cachedAt(gradientvalue), dab->colorSpace());
247 qreal paintOpacity = paintcolor.opacityF() * (qreal(qAlpha(*maskQRgb)) / 255.0);
248 paintcolor.setOpacity(qMin(paintOpacity, dab->colorSpace()->opacityF(dabData)));
249 colors[0] = paintcolor.data();
250 KoColor dabColor(dabData, dab->colorSpace());
251 colors[1] = dabColor.data();
252 colorMix->mixColors(colors, colorWeights, 2, dabData);
253
254 iter->nextPixel();
255 dabData += dab->pixelSize();
256 }
257 iter->nextRow();
258 }
259}
260
261void KisTextureOption::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info)
262{
263 if (!m_enabled) return;
264 if (!m_maskInfo->isValid()) return;
265
267 applyLightness(dab, offset, info);
268 return;
269 }
271 applyGradient(dab, offset, info);
272 return;
273 }
274
275 QRect rect = dab->bounds();
276 KisPaintDeviceSP mask = m_maskInfo->mask();
277 const QRect maskBounds = m_maskInfo->maskBounds();
278
280 KisPaintDeviceSP maskPatch = g.device();
281
282 int x = offset.x() % maskBounds.width() - m_offsetX;
283 int y = offset.y() % maskBounds.height() - m_offsetY;
284
285 const QRect maskPatchRect = QRect(x, y, rect.width(), rect.height());
286
287 KisFillPainter fillPainter(maskPatch);
288 fillPainter.setCompositeOpId(COMPOSITE_COPY);
289 fillPainter.fillRect(kisGrowRect(maskPatchRect, 1), mask, maskBounds);
290 fillPainter.end();
291
292 // Compute final strength
293 qreal strength = m_strengthOption.apply(info);
294
295 // Select mask compositing op
297 int alphaChannelOffset = -1;
298
299 const QList<KoChannelInfo *> channels = dab->colorSpace()->channels();
300 for (quint32 i = 0; i < dab->pixelSize(); i++) {
301 if (channels[i]->channelType() == KoChannelInfo::ALPHA) {
302 // TODO: check correctness for 16bits!
303 alphaChannelOffset = channels[i]->pos()/* * channels[i]->size()*/;
304 alphaChannelType = channels[i]->channelValueType();
305 break;
306 }
307 }
308
309 KIS_SAFE_ASSERT_RECOVER (alphaChannelOffset >= 0) {
310 alphaChannelOffset = 0;
311 }
312
313 QScopedPointer<KisMaskingBrushCompositeOpBase> compositeOp;
314 QString compositeOpId;
315
316 switch (m_texturingMode) {
317 case KisTextureOptionData::MULTIPLY: compositeOpId = COMPOSITE_MULT; break;
318 case KisTextureOptionData::SUBTRACT: compositeOpId = COMPOSITE_SUBTRACT; break;
319 case KisTextureOptionData::DARKEN: compositeOpId = COMPOSITE_DARKEN; break;
320 case KisTextureOptionData::OVERLAY: compositeOpId = COMPOSITE_OVERLAY; break;
321 case KisTextureOptionData::COLOR_DODGE: compositeOpId = COMPOSITE_DODGE; break;
322 case KisTextureOptionData::COLOR_BURN: compositeOpId = COMPOSITE_BURN; break;
324 case KisTextureOptionData::LINEAR_BURN: compositeOpId = COMPOSITE_LINEAR_BURN; break;
327 case KisTextureOptionData::HEIGHT: compositeOpId = "height"; break;
328 case KisTextureOptionData::LINEAR_HEIGHT: compositeOpId = "linear_height"; break;
329 case KisTextureOptionData::HEIGHT_PHOTOSHOP: compositeOpId = "height_photoshop"; break;
330 case KisTextureOptionData::LINEAR_HEIGHT_PHOTOSHOP: compositeOpId = "linear_height_photoshop"; break;
331 default: return;
332 }
334 compositeOpId, alphaChannelType, dab->pixelSize(),
335 alphaChannelOffset, strength, m_useSoftTexturing));
336
337 // Apply the mask to the dab
338 {
339 quint8 *dabIt = nullptr;
340 KisRandomConstAccessorSP maskPatchIt = maskPatch->createRandomConstAccessorNG();
341
342 qint32 dabY = dab->bounds().y();
343 qint32 maskPatchY = maskPatchRect.y();
344 qint32 rowsRemaining = dab->bounds().height();
345 const qint32 dabRowStride = dab->bounds().width() * dab->pixelSize();
346
347 while (rowsRemaining > 0) {
348 qint32 dabX = dab->bounds().x();
349 qint32 maskPatchX = maskPatchRect.x();
350 const qint32 numContiguousMaskPatchRows = maskPatchIt->numContiguousRows(maskPatchY);
351 const qint32 rows = std::min(rowsRemaining, numContiguousMaskPatchRows);
352 qint32 columnsRemaining = dab->bounds().width();
353
354 while (columnsRemaining > 0) {
355 const qint32 numContiguousMaskPatchColumns = maskPatchIt->numContiguousColumns(maskPatchX);
356 const qint32 columns = std::min(columnsRemaining, numContiguousMaskPatchColumns);
357
358 const qint32 maskPatchRowStride = maskPatchIt->rowStride(maskPatchX, maskPatchY);
359
360 dabIt = dab->data() + (dabY * dab->bounds().width() + dabX) * dab->pixelSize();
361 maskPatchIt->moveTo(maskPatchX, maskPatchY);
362
363 compositeOp->composite(maskPatchIt->rawDataConst(), maskPatchRowStride,
364 dabIt, dabRowStride,
365 columns, rows);
366
367 dabX += columns;
368 maskPatchX += columns;
369 columnsRemaining -= columns;
370
371 }
372
373 dabY += rows;
374 maskPatchY += rows;
375 rowsRemaining -= rows;
376 }
377 }
378}
@ SupportsGradientMode
@ SupportsLightnessMode
const QString COMPOSITE_OVER
const QString COMPOSITE_COPY
const QString COMPOSITE_DARKEN
const QString COMPOSITE_OVERLAY
const QString COMPOSITE_DODGE
const QString COMPOSITE_LINEAR_BURN
const QString COMPOSITE_MULT
const QString COMPOSITE_ERASE
const QString COMPOSITE_SUBTRACT
const QString COMPOSITE_BURN
const QString COMPOSITE_LINEAR_DODGE
const QString COMPOSITE_HARD_MIX_SOFTER_PHOTOSHOP
const QString COMPOSITE_HARD_MIX_PHOTOSHOP
virtual const quint8 * rawDataConst() const =0
KoResourceLoadResult loadLinkedPattern(KisResourcesInterfaceSP resourcesInterface) const
void fillRect(qint32 x, qint32 y, qint32 w, qint32 h, const KoColor &c, quint8 opacity)
const KoColorSpace * colorSpace() const
static KisMaskingBrushCompositeOpBase * createForAlphaSrc(const QString &id, KoChannelInfo::enumChannelValueType channelType, int pixelSize, int alphaOffset)
KisRandomConstAccessorSP createRandomConstAccessorNG() const
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
void setCompositeOpId(const KoCompositeOp *op)
virtual qint32 rowStride(qint32 x, qint32 y) const =0
virtual qint32 numContiguousRows(qint32 y) const =0
virtual void moveTo(qint32 x, qint32 y)=0
virtual qint32 numContiguousColumns(qint32 x) const =0
ALWAYS_INLINE const quint8 * oldRawData() const
qreal apply(const KisPaintInformation &info) const
static bool requiresEffectiveCompositeOp(const KisPropertiesConfiguration *settings)
void fillProperties(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface, KoCanvasResourcesInterfaceSP canvasResourcesInterface)
KisTextureMaskInfoSP m_maskInfo
static QList< KoResourceLoadResult > prepareEmbeddedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
KisBrushTextureFlags m_flags
KisCachedPaintDevice m_cachedPaintDevice
KisStrengthOption m_strengthOption
KoAbstractGradientSP m_gradient
void applyGradient(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation &info)
bool applyingGradient() const
KoCachedGradient m_cachedGradient
void apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation &info)
apply combine the texture map with the dab
KisTextureOption(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface, KoCanvasResourcesInterfaceSP canvasResourcesInterface, int levelOfDetail, KisBrushTextureFlags flags=None)
void applyLightness(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation &info)
KisTextureOptionData::TexturingMode m_texturingMode
static QList< KoResourceLoadResult > prepareLinkedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
void setGradient(const KoAbstractGradientSP gradient, qint32 steps, const KoColorSpace *cs)
void setColorSpace(const KoColorSpace *colorSpace)
const quint8 * cachedAt(qreal t) const
gets the color data at position 0 <= t <= 1
@ ALPHA
The channel represents the opacity of a pixel.
enumChannelValueType
enum to define the value of the channel
@ UINT8
use this for an unsigned integer 8bits channel
virtual qreal opacityF(const quint8 *pixel) const =0
QList< KoChannelInfo * > channels
virtual void fillGrayBrushWithColorAndLightnessWithStrength(quint8 *dst, const QRgb *brush, quint8 *brushColor, qreal strength, qint32 nPixels) const
KoMixColorsOp * mixColorsOp
qreal opacityF() const
Definition KoColor.cpp:345
void setColor(const quint8 *data, const KoColorSpace *colorSpace=0)
Definition KoColor.cpp:186
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
quint8 * data()
Definition KoColor.h:144
virtual void mixColors(const quint8 *const *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum=255) const =0
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
unsigned int QRgb
#define warnKrita
Definition kis_debug.h:87
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
QSharedPointer< T > toQShared(T *ptr)
KisPaintDeviceSP device() const
KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info)
static KisTextureMaskInfoCache * instance()
bool read(const KisPropertiesConfiguration *setting)
KisEmbeddedTextureData textureData
static KoColorSpaceRegistry * instance()