Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_ls_bevel_emboss_filter.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * See LayerFX plugin for Gimp as a reference implementation of this style:
5 * http://registry.gimp.org/node/186
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
11
12#include <cstdlib>
13
14#include <QBitArray>
15
16#include <KoUpdater.h>
17#include <resources/KoPattern.h>
18
20
21#include "psd.h"
22
25#include "kis_gaussian_kernel.h"
26
27#include "kis_pixel_selection.h"
28#include "kis_fill_painter.h"
30#include "kis_iterator_ng.h"
32
33#include "kis_psd_layer_style.h"
35
36#include "kis_ls_utils.h"
37
38#include "gimp_bump_map.h"
39#include "kis_transaction.h"
42
43
45 : KisLayerStyleFilter(KoID("lsstroke", i18n("Stroke (style)")))
46{
47}
48
53
58
60 KisPixelSelectionSP dstSelection,
61 const QRect &applyRect,
62 int size,
63 int initialSize,
64 bool invert,
66{
68 KisSelectionSP tmpBaseSelection = s1.selection();
69 KisPixelSelectionSP tmpSelection = tmpBaseSelection->pixelSelection();
70
71 // NOTE: we are not using createCompositionSourceDevice() intentionally,
72 // because the source device doesn't have alpha channel
74 KisPixelSelectionSP fillDevice = s2.selection()->pixelSelection();
75
76 KisPainter gc(dstSelection);
78
79 for (int i = 0; i < size; i++) {
80 const int growSize = initialSize - i - 1;
81
82 quint8 selectedness = invert ?
83 qRound(qreal(size - i - 1) / size * 255.0) :
84 qRound(qreal(i + 1) / size * 255.0);
85 fillDevice->setDefaultPixel(KoColor(&selectedness, fillDevice->colorSpace()));
86
87 tmpSelection->makeCloneFromRough(srcSelection, srcSelection->selectedRect());
88
89 QRect changeRect = KisLsUtils::growSelectionUniform(tmpSelection, growSize, applyRect);
90
91 gc.setSelection(tmpBaseSelection);
92 gc.bitBlt(changeRect.topLeft(), fillDevice, changeRect);
93 }
94}
95
96struct ContrastOp {
97 static const bool supportsCaching = false;
98
99 ContrastOp(qreal contrast)
100 : m_contrast(contrast)
101 {
102 }
103
104 int operator() (int iValue) {
105 qreal value = qreal(iValue - 127) / 127.0;
106
107 qreal slant = std::tan ((m_contrast + 1) * M_PI_4);
108 value = (value - 0.5) * slant + 0.5;
109
110 return qRound(value * 255.0);
111 }
112
113private:
115};
116
118 static const bool supportsCaching = true;
119
121 return qRound(qMax(0, value - 127) * (255.0 / (255 - 127)));
122 }
123};
124
126 static const bool supportsCaching = true;
127
129 return 255 - qRound(qMin(value, 127) * (255.0 / 127.0));
130 }
131};
132
133template <class MapOp>
135 KisPixelSelectionSP dstSelection,
136 MapOp mapOp,
137 const QRect &applyRect)
138{
139 static quint8 mapTable[256];
140 static bool mapInitialized = false;
141
142 if (!MapOp::supportsCaching || !mapInitialized) {
143 mapInitialized = true;
144
145 for (int i = 0; i < 256; i++) {
146 mapTable[i] = mapOp(i);
147 }
148 }
149
150 KisSequentialConstIterator srcIt(srcSelection, applyRect);
151 KisSequentialIterator dstIt(dstSelection, applyRect);
152
153 while (srcIt.nextPixel() && dstIt.nextPixel()) {
154 const quint8 *srcPtr = srcIt.rawDataConst();
155 quint8 *dstPtr = dstIt.rawData();
156 *dstPtr = mapTable[*srcPtr];
157 }
158}
159
160template <class MapOp>
162 MapOp mapOp,
163 const QRect &applyRect)
164{
165 static quint8 mapTable[256];
166 static bool mapInitialized = false;
167
168 if (!MapOp::supportsCaching || !mapInitialized) {
169 mapInitialized = true;
170
171 for (int i = 0; i < 256; i++) {
172 mapTable[i] = mapOp(i);
173 }
174 }
175
176 KisSequentialIterator dstIt(dstSelection, applyRect);
177
178 while (dstIt.nextPixel()) {
179 quint8 *dstPtr = dstIt.rawData();
180 *dstPtr = mapTable[*dstPtr];
181 }
182}
183
185{
198
199 QRect totalChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
200 QRect changeRect = calcBevelChangeRect(applyRect, config);
201 changeRect = kisGrowRect(changeRect, 1); // bumpmap method
202 changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften());
203 return changeRect;
204 }
205
206 QRect totalNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
207 QRect changeRect = applyRect;
208 changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften());
209 changeRect = kisGrowRect(changeRect, 1); // bumpmap method
210 changeRect = calcBevelNeedRect(applyRect, config);
211 return changeRect;
212 }
213
222
223private:
224 QRect calcBevelChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
225 const int size = config->size();
226 int limitingGrowSize = 0;
227
228 switch (config->style()) {
230 limitingGrowSize = size;
231 break;
233 limitingGrowSize = 0;
234 break;
235 case psd_bevel_emboss: {
236 const int initialSize = std::ceil(qreal(size) / 2.0);
237 limitingGrowSize = initialSize;
238 break;
239 }
241 const int halfSizeC = std::ceil(qreal(size) / 2.0);
242 limitingGrowSize = halfSizeC;
243 break;
244 }
246 warnKrita << "WARNING: Stroke Emboss style is not implemented yet!";
247 return applyRect;
248 }
249
250 return kisGrowRect(applyRect, limitingGrowSize);
251 }
252
253 QRect calcBevelNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
254 const int size = config->size();
255 int limitingGrowSize = size;
256
257 return kisGrowRect(applyRect, limitingGrowSize);
258 }
259};
260
263 const QRect &applyRect,
264 const psd_layer_effects_bevel_emboss *config,
265 KisResourcesInterfaceSP resourcesInterface,
267{
268 if (applyRect.isEmpty()) return;
269
270 BevelEmbossRectCalculator d(applyRect, config);
271
273 KisSelectionSP baseSelection = s1.selection();
274 KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, d.initialFetchRect);
275
276 KisPixelSelectionSP selection = baseSelection->pixelSelection();
277
278 const int size = config->size();
279
280 int limitingGrowSize = 0;
282 KisPixelSelectionSP bumpmapSelection = s2.selection()->pixelSelection();
283
284 switch (config->style()) {
286 paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, size, false, env);
287 limitingGrowSize = size;
288 break;
290 paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, 0, false, env);
291 limitingGrowSize = 0;
292 break;
293 case psd_bevel_emboss: {
294 const int initialSize = std::ceil(qreal(size) / 2.0);
295 paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, initialSize, false, env);
296 limitingGrowSize = initialSize;
297 break;
298 }
300 const int halfSizeF = std::floor(qreal(size) / 2.0);
301 const int halfSizeC = std::ceil(qreal(size) / 2.0);
302 // TODO: probably not correct!
303 paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeC, halfSizeC, false, env);
304 paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeF, 0, true, env);
305 limitingGrowSize = halfSizeC;
306 break;
307 }
309 warnKrita << "WARNING: Stroke Emboss style is not implemented yet!";
310 return;
311 }
312
314 KisPixelSelectionSP limitingSelection = s3.selection()->pixelSelection();
315 limitingSelection->makeCloneFromRough(selection, selection->selectedRect());
316 {
317 QRect changeRectUnused =
318 KisLsUtils::growSelectionUniform(limitingSelection,
319 limitingGrowSize,
320 d.applyBevelRect);
321 Q_UNUSED(changeRectUnused);
322 }
323
324 //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("1_selection_xconv.png");
325
326 if (config->textureEnabled()) {
328 KisPixelSelectionSP textureSelection = s4.selection()->pixelSelection();
329
330 KisLsUtils::fillPattern(textureSelection, d.applyTextureRect, env,
331 config->textureScale(),
332 config->texturePattern(resourcesInterface),
333 config->textureHorizontalPhase(),
334 config->textureVerticalPhase(),
335 config->textureAlignWithLayer());
336
337 int contrastadj = 0;
338
339 {
340 using namespace std;
341
342 int tex_depth = config->textureDepth();
343
344 if (tex_depth >= 0.0) {
345 if (tex_depth <= 100.0) {
346 contrastadj = int(qRound((1-(tex_depth/100.0)) * -127));
347 } else {
348 contrastadj = int(qRound(((tex_depth-100.0)/900.0) * 127));
349 }
350 } else {
351 textureSelection->invert();
352 if (tex_depth >= -100.0) {
353 contrastadj = int(qRound((1-(abs(tex_depth)/100.0)) * -127));
354 } else {
355 contrastadj = int(qRound(((abs(tex_depth)-100.0)/900.0) * 127));
356 }
357 }
358 }
359
360 qreal contrast = qBound(-1.0, qreal(contrastadj) / 127.0, 1.0);
361 mapPixelValues(textureSelection, ContrastOp(contrast), d.applyTextureRect);
362
363 {
364 KisPainter gc(bumpmapSelection);
366 gc.bitBlt(d.applyTextureRect.topLeft(), textureSelection, d.applyTextureRect);
367 gc.end();
368 }
369 }
370
371 //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("15_selection_texture.png");
372
373 if (config->contourEnabled()) {
374 if (config->range() != KisLsUtils::FULL_PERCENT_RANGE) {
375 KisLsUtils::adjustRange(bumpmapSelection, d.applyContourRect, config->range());
376 }
377
378 KisLsUtils::applyContourCorrection(bumpmapSelection,
379 d.applyContourRect,
380 config->contourLookupTable(),
381 config->antiAliased(),
382 true);
383 }
384
385 bumpmap_vals_t bmvals;
386
387 bmvals.azimuth = config->angle();
388 bmvals.elevation = config->altitude();
389 bmvals.depth = config->depth();
390 bmvals.ambient = 0;
391 bmvals.compensate = true;
392 bmvals.invert = config->direction() == psd_direction_down;
393 bmvals.type = LINEAR;
394
395 bumpmap(bumpmapSelection, d.applyBumpmapRect, bmvals);
396
397 //bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("3_selection_bumpmap.png");
398
399 { // TODO: optimize!
400
401 KisLsUtils::applyContourCorrection(bumpmapSelection,
402 d.applyGlossContourRect,
403 config->glossContourLookupTable(),
404 config->glossAntiAliased(),
405 true);
406
407 }
408
409 if (config->soften()) {
410 KisLsUtils::applyGaussianWithTransaction(bumpmapSelection, d.applyGaussianRect, config->soften());
411 }
412
413
414 if (config->textureEnabled() && config->textureInvert()) {
415 bumpmapSelection->invert();
416 }
417
418 selection->clear();
419 mapPixelValues(bumpmapSelection, selection,
420 ShadowsFetchOp(), d.shadowHighlightsFinalRect);
421 selection->applySelection(limitingSelection, SELECTION_INTERSECT);
422
423 //dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("4_dst_before_apply.png");
424 //selection->convertToQImage(0, QRect(0,0,300,300)).save("4_shadows_sel.png");
425
426 {
427 KisPaintDeviceSP dstDevice = dst->getProjection("00_bevel_shadow",
428 config->shadowBlendMode(),
429 config->shadowOpacity(),
430 QBitArray(),
431 srcDevice);
432
433 const KoColor fillColor(config->shadowColor(), dstDevice->colorSpace());
434 const QRect &fillRect = d.shadowHighlightsFinalRect;
435
436 KisCachedPaintDevice::Guard d1(dstDevice, *env->cachedPaintDevice());
437 KisPaintDeviceSP fillDevice = d1.device();
438 fillDevice->setDefaultPixel(fillColor);
439
440 KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection);
441 }
442
443 selection->clear();
444 mapPixelValues(bumpmapSelection, selection,
445 HighlightsFetchOp(), d.shadowHighlightsFinalRect);
446 selection->applySelection(limitingSelection, SELECTION_INTERSECT);
447
448 //selection->convertToQImage(0, QRect(0,0,300,300)).save("5_highlights_sel.png");
449
450 {
451 KisPaintDeviceSP dstDevice = dst->getProjection("01_bevel_highlight",
452 config->highlightBlendMode(),
453 config->highlightOpacity(),
454 QBitArray(),
455 srcDevice);
456
457 const KoColor fillColor(config->highlightColor(), dstDevice->colorSpace());
458 const QRect &fillRect = d.shadowHighlightsFinalRect;
459
460 KisCachedPaintDevice::Guard d1(dstDevice, *env->cachedPaintDevice());
461 KisPaintDeviceSP fillDevice = d1.device();
462 fillDevice->setDefaultPixel(fillColor);
463
464 KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection);
465 }
466}
467
471 const QRect &applyRect,
472 KisPSDLayerStyleSP style,
474{
475 Q_UNUSED(env);
476 Q_UNUSED(blower);
478
479 const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss();
480 if (!KisLsUtils::checkEffectEnabled(config, dst)) return;
481
483 applyBevelEmboss(src, dst, applyRect, w.config, style->resourcesInterface(), env);
484}
485
487{
488 const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss();
489 if (!config->effectEnabled()) return rect;
490
492
493 BevelEmbossRectCalculator d(rect, w.config);
494 return d.totalNeedRect(rect, w.config);
495}
496
498{
499 const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss();
500 if (!config->effectEnabled()) return rect;
501
503
504 BevelEmbossRectCalculator d(rect, w.config);
505 return d.totalChangeRect(rect, w.config);
506}
float value(const T *src, size_t ch)
QPointF s3
QPointF s1
QPointF s2
@ SELECTION_INTERSECT
const QString COMPOSITE_COPY
const QString COMPOSITE_MULT
void processDirectly(KisPaintDeviceSP src, KisMultipleProjection *dst, KisLayerStyleKnockoutBlower *blower, const QRect &applyRect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override
KisLayerStyleFilter * clone() const override
QRect neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override
void applyBevelEmboss(KisPaintDeviceSP srcDevice, KisMultipleProjection *dst, const QRect &applyRect, const psd_layer_effects_bevel_emboss *config, KisResourcesInterfaceSP resourcesInterface, KisLayerStyleFilterEnvironment *env) const
QRect changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const override
KisPaintDeviceSP getProjection(const QString &id, const QString &compositeOpId, quint8 opacity, const QBitArray &channelFlags, KisPaintDeviceSP prototype)
void setDefaultPixel(const KoColor &defPixel)
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
const KoColorSpace * colorSpace() const
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void setSelection(KisSelectionSP selection)
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setCompositeOpId(const KoCompositeOp *op)
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE const quint8 * rawDataConst() const
Definition KoID.h:30
qint32 range() const
Definition psd.h:351
bool effectEnabled() const
Definition psd.h:261
qint32 angle() const
Definition psd.h:286
qint32 size() const
Definition psd.h:306
const quint8 * contourLookupTable() const
Definition psd.h:311
bool antiAliased() const
Definition psd.h:316
void bumpmap(KisPixelSelectionSP device, const QRect &selectionRect, const bumpmap_vals_t &bmvals)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define warnKrita
Definition kis_debug.h:87
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
void mapPixelValues(KisPixelSelectionSP srcSelection, KisPixelSelectionSP dstSelection, MapOp mapOp, const QRect &applyRect)
void paintBevelSelection(KisPixelSelectionSP srcSelection, KisPixelSelectionSP dstSelection, const QRect &applyRect, int size, int initialSize, bool invert, KisLayerStyleFilterEnvironment *env)
void applyContourCorrection(KisPixelSelectionSP selection, const QRect &applyRect, const quint8 *lookup_table, bool antiAliased, bool edgeHidden)
QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect)
QRect growRectFromRadius(const QRect &rc, int radius)
void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range)
void fillPattern(KisPaintDeviceSP fillDevice, const QRect &applyRect, KisLayerStyleFilterEnvironment *env, int scale, KoPatternSP pattern, int horizontalPhase, int verticalPhase, bool alignWithLayer)
bool checkEffectEnabled(const psd_layer_effects_shadow_base *config, KisMultipleProjection *dst)
void selectionFromAlphaChannel(KisPaintDeviceSP srcDevice, KisSelectionSP dstSelection, const QRect &srcRect)
static const int FULL_PERCENT_RANGE
void applyGaussianWithTransaction(KisPixelSelectionSP selection, const QRect &applyRect, qreal radius)
@ LINEAR
Definition nugrid.h:26
@ psd_direction_down
Definition psd.h:142
@ psd_bevel_pillow_emboss
Definition psd.h:138
@ psd_bevel_inner_bevel
Definition psd.h:136
@ psd_bevel_stroke_emboss
Definition psd.h:139
@ psd_bevel_outer_bevel
Definition psd.h:135
@ psd_bevel_emboss
Definition psd.h:137
QRect calcBevelNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config)
QRect totalChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config)
BevelEmbossRectCalculator(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config)
QRect calcBevelChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config)
QRect totalNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config)
static const bool supportsCaching
ContrastOp(qreal contrast)
KisPaintDeviceSP device() const
void clear(const QRect &r)
void applySelection(KisPixelSelectionSP selection, SelectionAction action)
KisPixelSelectionSP pixelSelection
static const bool supportsCaching
QString shadowBlendMode() const
Definition psd.h:821
const quint8 * glossContourLookupTable() const
Definition psd.h:775
qint32 highlightOpacity() const
Definition psd.h:812
psd_bevel_style style() const
Definition psd.h:721
bool contourEnabled() const
Definition psd.h:848
psd_direction direction() const
Definition psd.h:748
bool glossAntiAliased() const
Definition psd.h:785
KoPatternSP texturePattern(KisResourcesInterfaceSP resourcesInterface) const
Definition psd.h:879
int textureVerticalPhase() const
Definition psd.h:950
bool textureInvert() const
Definition psd.h:912
qint32 shadowOpacity() const
Definition psd.h:839
bool textureEnabled() const
Definition psd.h:866
bool textureAlignWithLayer() const
Definition psd.h:921
int textureHorizontalPhase() const
Definition psd.h:941
QString highlightBlendMode() const
Definition psd.h:794
KoColor shadowColor() const
Definition psd.h:830
KoColor highlightColor() const
Definition psd.h:803