Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_oilpaint_filter.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Krita
3 *
4 * SPDX-FileCopyrightText: 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
5 * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
6 *
7 * ported from digikam, Copyrighted by Gilles Caulier,
8 * Original Oilpaint algorithm copyrighted 2004 by
9 * Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14#include "kis_oilpaint_filter.h"
15
16#include <stdlib.h>
17#include <vector>
18
19#include <QPoint>
20#include <QSpinBox>
21#include <QDateTime>
22
23#include <klocalizedstring.h>
24#include <kis_debug.h>
25#include <kpluginfactory.h>
26
27#include <KoUpdater.h>
28
29#include <KisDocument.h>
30#include <kis_image.h>
32#include <kis_layer.h>
34#include <kis_global.h>
35#include <kis_types.h>
39#include <kis_paint_device.h>
42
43
50
52 const QRect& applyRect,
53 const KisFilterConfigurationSP config,
54 KoUpdater* progressUpdater
55 ) const
56{
57 Q_ASSERT(!device.isNull());
58
59 //read the filter configuration values from the KisFilterConfiguration object
60 const quint32 brushSize = config ? config->getInt("brushSize", 1) : 1;
61 const quint32 smooth = config ? config->getInt("smooth", 30) : 30;
62
63 OilPaint(device, device, applyRect, brushSize, smooth, progressUpdater);
64}
65
66// This method have been ported from Pieter Z. Voloshyn algorithm code.
67
68/* Function to apply the OilPaint effect.
69 *
70 * data => The image data in RGBA mode.
71 * w => Width of image.
72 * h => Height of image.
73 * BrushSize => Brush size.
74 * Smoothness => Smooth value.
75 *
76 * Theory => Using MostFrequentColor function we take the main color in
77 * a matrix and simply write at the original position.
78 */
79
80void KisOilPaintFilter::OilPaint(const KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &applyRect,
81 int BrushSize, int Smoothness, KoUpdater* progressUpdater) const
82{
83 KisSequentialConstIteratorProgress it(src, applyRect, progressUpdater);
84 KisSequentialIterator dstIt(dst, applyRect);
85
86 while (it.nextPixel() && dstIt.nextPixel()) {
87 MostFrequentColor(src, dstIt.rawData(), applyRect, it.x(), it.y(), BrushSize, Smoothness);
88 }
89}
90
91// This method has been ported from Pieter Z. Voloshyn's algorithm code in Digikam.
92
93/* Function to determine the most frequent color in a matrix
94 *
95 * Bits => Bits array
96 * Width => Image width
97 * Height => Image height
98 * X => Position horizontal
99 * Y => Position vertical
100 * Radius => Is the radius of the matrix to be analyzed
101 * Intensity => Intensity to calculate
102 *
103 * Theory => This function creates a matrix with the analyzed pixel in
104 * the center of this matrix and find the most frequent color
105 */
106
107void KisOilPaintFilter::MostFrequentColor(KisPaintDeviceSP src, quint8* dst, const QRect& /*bounds*/, int X, int Y, int Radius, int Intensity) const
108{
109 uint I;
110
111 double Scale = Intensity / 255.0;
112
113 // Alloc some arrays to be used
114 uchar *IntensityCount = new uchar[(Intensity + 1)];
115
116 const KoColorSpace* cs = src->colorSpace();
117
118 QVector<float> channel(cs->channelCount());
119 QVector<float>* AverageChannels = new QVector<float>[(Intensity + 1)];
120
121 // Erase the array
122 memset(IntensityCount, 0, (Intensity + 1) * sizeof(uchar));
123
124 int startx = X - Radius;
125 int starty = Y - Radius;
126 int width = (2 * Radius) + 1;
127 int height = (2 * Radius) + 1;
128
129 qreal middlePointAlpha = 1;
130 {
131 // if the current pixel is transparent, the result must be transparent, too.
132 KisSequentialConstIterator middlePointIt(src, QRect(X, Y, 1, 1));
133 middlePointIt.nextPixel();
134 middlePointAlpha = cs->opacityF(middlePointIt.oldRawData());
135 }
136
137
138 KisSequentialConstIterator srcIt(src, QRect(startx, starty, width, height));
139 while (middlePointAlpha > 0 && srcIt.nextPixel()) {
140
141 cs->normalisedChannelsValue(srcIt.oldRawData(), channel);
142
143 if (cs->opacityU8(srcIt.oldRawData()) == 0) {
144 // if the pixel is transparent, it's not going to provide any useful information
145 continue;
146 }
147
148 I = (uint)(cs->intensity8(srcIt.oldRawData()) * Scale);
149
150 IntensityCount[I]++;
151
152 if (IntensityCount[I] == 1) {
153 AverageChannels[I] = channel;
154 } else {
155 for (int i = 0; i < channel.size(); i++) {
156 AverageChannels[I][i] += channel[i];
157 }
158 }
159 }
160
161 I = 0;
162 int MaxInstance = 0;
163
164 for (int i = 0 ; i <= Intensity ; ++i) {
165 if (IntensityCount[i] > MaxInstance) {
166 I = i;
167 MaxInstance = IntensityCount[i];
168 }
169 }
170
171 if (MaxInstance != 0) {
172 channel = AverageChannels[I];
173 for (int i = 0; i < channel.size(); i++) {
174 channel[i] /= MaxInstance;
175 }
176 cs->fromNormalisedChannelsValue(dst, channel);
177 cs->setOpacity(dst, OPACITY_OPAQUE_U8, middlePointAlpha);
178 } else {
179 memset(dst, 0, cs->pixelSize());
180 cs->setOpacity(dst, OPACITY_OPAQUE_U8, middlePointAlpha);
181 }
182
183
184 delete [] IntensityCount; // free all the arrays
185 delete [] AverageChannels;
186}
187
188QRect KisOilPaintFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int /*lod*/) const
189{
190 const quint32 brushSize = _config ? _config->getInt("brushSize", 1) : 1;
191 return rect.adjusted(-brushSize * 2, -brushSize * 2, brushSize * 2, brushSize * 2);
192}
193
194QRect KisOilPaintFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int /*lod*/) const
195{
196 const quint32 brushSize = _config ? _config->getInt("brushSize", 1) : 1;
197
198 return rect.adjusted( -brushSize*2, -brushSize*2, brushSize*2, brushSize*2);
199}
200
201
203{
205 param.push_back(KisIntegerWidgetParam(1, 5, 1, i18n("Brush size"), "brushSize"));
206 param.push_back(KisIntegerWidgetParam(10, 255, 30, i18nc("smooth out the painting strokes the filter creates", "Smooth"), "smooth"));
207 KisMultiIntegerFilterWidget * w = new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
209 return w;
210}
211
213{
214 KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
215 config->setProperty("brushSize", 1);
216 config->setProperty("smooth", 30);
217
218 return config;
219}
const quint8 OPACITY_OPAQUE_U8
unsigned int uint
static KisResourcesInterfaceSP instance()
QRect neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override
void MostFrequentColor(KisPaintDeviceSP src, quint8 *dst, const QRect &bounds, int X, int Y, int Radius, int Intensity) const
void OilPaint(const KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &applyRect, int BrushSize, int Smoothness, KoUpdater *progressUpdater) const
QRect changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override
KisConfigWidget * createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override
KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override
void processImpl(KisPaintDeviceSP device, const QRect &applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const override
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE int x() const
ALWAYS_INLINE const quint8 * oldRawData() const
ALWAYS_INLINE int y() const
bool isNull() const
const KoID FiltersCategoryArtisticId("artistic_filters", ki18nc("The category of artistic filters, like raindrops. Adjective.", "Artistic"))
std::vector< KisIntegerWidgetParam > vKisIntegerWidgetParam
void setSupportsThreading(bool v)
virtual KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
void setSupportsAdjustmentLayers(bool v)
void setSupportsPainting(bool v)