Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_unsharp_filter.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Krita
3 *
4 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
10#include <QBitArray>
11
12#include <kis_mask_generator.h>
15#include <kis_gaussian_kernel.h>
19#include <KoProgressUpdater.h>
20#include <KoUpdater.h>
21#include <KoConvolutionOp.h>
22#include <kis_paint_device.h>
23#include "kis_lod_transform.h"
24
25#include "kis_wdg_unsharp.h"
26#include "ui_wdgunsharp.h"
27#include "KoColorSpaceTraits.h"
29
30
47
49{
50 return new KisWdgUnsharp(parent);
51}
52
54{
55 KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
56 config->setProperty("halfSize", 1);
57 config->setProperty("amount", 0.5);
58 config->setProperty("threshold", 0);
59 config->setProperty("lightnessOnly", true);
60 return config;
61}
62
64 const QRect& applyRect,
65 const KisFilterConfigurationSP config,
66 KoUpdater* progressUpdater
67 ) const
68{
69
70 QPointer<KoUpdater> filterUpdater = 0;
71 QPointer<KoUpdater> convolutionUpdater = 0;
72 QScopedPointer<KoProgressUpdater> updater;
73
74 if (progressUpdater) {
75 updater.reset(new KoProgressUpdater(progressUpdater));
76 updater->start(100, i18n("Unsharp Mask"));
77 // Two sub-sub tasks that each go from 0 to 100.
78 convolutionUpdater = updater->startSubtask();
79 filterUpdater = updater->startSubtask();
80 }
81
83
84 QVariant value;
85
86 KisLodTransformScalar t(device);
87
88 const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
89 const qreal amount = (config->getProperty("amount", value)) ? value.toDouble() : 0.5;
90 const uint threshold = (config->getProperty("threshold", value)) ? value.toUInt() : 0;
91 const uint lightnessOnly = (config->getProperty("lightnessOnly", value)) ? value.toBool() : true;
92
93 QBitArray channelFlags = config->channelFlags();
94 KisGaussianKernel::applyGaussian(device, applyRect,
95 halfSize, halfSize,
96 channelFlags,
97 convolutionUpdater);
98
99 qreal weights[2];
100 qreal factor = 128;
101
102 weights[0] = factor * (1. + amount);
103 weights[1] = -factor * amount;
104
105 if (lightnessOnly) {
106 processLightnessOnly(device, applyRect, threshold, weights, factor, channelFlags, filterUpdater);
107 } else {
108 processRaw(device, applyRect, threshold, weights, factor, channelFlags, filterUpdater);
109 }
110}
111
113 const QRect &rect,
114 quint8 threshold,
115 qreal weights[2],
116 qreal factor,
117 const QBitArray &channelFlags,
118 KoUpdater *progressUpdater) const
119{
120 const KoColorSpace *cs = device->colorSpace();
121 const int pixelSize = cs->pixelSize();
122 KoConvolutionOp * convolutionOp = cs->convolutionOp();
123
124 quint8 *colors[2];
125 colors[0] = new quint8[pixelSize];
126 colors[1] = new quint8[pixelSize];
127
128 KisSequentialIteratorProgress dstIt(device, rect, progressUpdater);
129
130 while (dstIt.nextPixel()) {
131 quint8 diff = 0;
132 if (threshold == 1) {
133 if (memcmp(dstIt.oldRawData(), dstIt.rawDataConst(), cs->pixelSize()) == 0) {
134 diff = 1;
135 }
136 }
137 else {
138 diff = cs->difference(dstIt.oldRawData(), dstIt.rawDataConst());
139 }
140
141 if (diff >= threshold) {
142 memcpy(colors[0], dstIt.oldRawData(), pixelSize);
143 memcpy(colors[1], dstIt.rawDataConst(), pixelSize);
144 convolutionOp->convolveColors(colors, weights, dstIt.rawData(), factor, 0, 2, channelFlags);
145 } else {
146 memcpy(dstIt.rawData(), dstIt.oldRawData(), pixelSize);
147 }
148 }
149
150 delete[] colors[0];
151 delete[] colors[1];
152}
153
155 const QRect &rect,
156 quint8 threshold,
157 qreal weights[2],
158 qreal factor,
159 const QBitArray & /*channelFlags*/,
160 KoUpdater *progressUpdater) const
161{
162 const KoColorSpace *cs = device->colorSpace();
163 const int pixelSize = cs->pixelSize();
164
165 quint16 labColorSrc[4];
166 quint16 labColorDst[4];
167
168 const int posL = 0;
169 const int posAlpha = 3;
170
171 const qreal factorInv = 1.0 / factor;
172
173 KisSequentialIteratorProgress dstIt(device, rect, progressUpdater);
174
175 while (dstIt.nextPixel()) {
176 quint8 diff = cs->differenceA(dstIt.oldRawData(), dstIt.rawDataConst());
177 if (diff >= threshold) {
178 cs->toLabA16(dstIt.oldRawData(), (quint8*)labColorSrc, 1);
179 cs->toLabA16(dstIt.rawDataConst(), (quint8*)labColorDst, 1);
180
181 qint32 valueL = (labColorSrc[posL] * weights[0] + labColorDst[posL] * weights[1]) * factorInv;
182 labColorSrc[posL] = CLAMP(valueL,
185
186 qint32 valueAlpha = (labColorSrc[posAlpha] * weights[0] + labColorDst[posAlpha] * weights[1]) * factorInv;
187 labColorSrc[posAlpha] = CLAMP(valueAlpha,
190
191 cs->fromLabA16((quint8*)labColorSrc, dstIt.rawData(), 1);
192 } else {
193 memcpy(dstIt.rawData(), dstIt.oldRawData(), pixelSize);
194 }
195 }
196}
197
198QRect KisUnsharpFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
199{
201
202 QVariant value;
203 const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
204
205 return rect.adjusted(-halfSize * 2, -halfSize * 2, halfSize * 2, halfSize * 2);
206}
207
208QRect KisUnsharpFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
209{
211
212 QVariant value;
213 const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
214
215 return rect.adjusted( -halfSize, -halfSize, halfSize, halfSize);
216}
float value(const T *src, size_t ch)
@ FULLY_INDEPENDENT
unsigned int uint
void setSupportsLevelOfDetail(bool value)
static void applyGaussian(KisPaintDeviceSP device, const QRect &rect, qreal xRadius, qreal yRadius, const QBitArray &channelFlags, KoUpdater *updater, bool createTransaction=false, KisConvolutionBorderOp borderOp=BORDER_REPEAT)
qreal scale(qreal value) const
const KoColorSpace * colorSpace() const
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE const quint8 * oldRawData() const
ALWAYS_INLINE const quint8 * rawDataConst() const
KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override
KisConfigWidget * createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override
void processRaw(KisPaintDeviceSP device, const QRect &rect, quint8 threshold, qreal weights[2], qreal factor, const QBitArray &channelFlags, KoUpdater *progressUpdater) const
void processImpl(KisPaintDeviceSP device, const QRect &applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const override
QRect neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override
QRect changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override
void processLightnessOnly(KisPaintDeviceSP device, const QRect &rect, quint8 threshold, qreal weights[2], qreal factor, const QBitArray &channelFlags, KoUpdater *progressUpdater) const
virtual quint32 pixelSize() const =0
virtual quint8 difference(const quint8 *src1, const quint8 *src2) const =0
KoConvolutionOp * convolutionOp
virtual void toLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const
virtual void fromLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const
virtual quint8 differenceA(const quint8 *src1, const quint8 *src2) const =0
virtual void convolveColors(const quint8 *const *colors, const qreal *kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nColors, const QBitArray &channelFlags) const =0
#define CLAMP(x, l, h)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
const KoID FiltersCategoryEnhanceId("enhance_filters", ki18nc("The category of enhancement filters, like sharpen. Verb.", "Enhance"))
void setSupportsThreading(bool v)
virtual KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
void setSupportsAdjustmentLayers(bool v)
void setSupportsPainting(bool v)
void setColorSpaceIndependence(ColorSpaceIndependence v)