Krita Source Code Documentation
Loading...
Searching...
No Matches
gimp_bump_map.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * gimp_bump_map contains code taken from gimp-bumpmap.c, original copyright:
5 *
6 * SPDX-FileCopyrightText: 1997 Federico Mena Quintero <federico@nuclecu.unam.mx>
7 * SPDX-FileCopyrightText: 1997-2000 Jens Lautenbacher <jtl@gimp.org>
8 * SPDX-FileCopyrightText: 2000 Sven Neumann <sven@gimp.org>
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
13#include "gimp_bump_map.h"
14
15#include <QRect>
16#include "kis_pixel_selection.h"
17#include "kis_image.h"
18
19#include <memory>
20
21
22typedef int gint;
23typedef qint32 gint32;
24typedef bool gboolean;
25typedef quint8 guchar;
26typedef double gdouble;
27
28#define G_PI M_PI
29#define MOD(x, y) ((x) % (y))
30#define CLAMP(x, l, h) qBound(l, x, h)
31#define MAX(x,y) qMax(x,y)
32#define MIN(x,y) qMin(x,y)
33
34typedef struct
35{
36 gint lx, ly; /* X and Y components of light vector */
37 gint nz2, nzlz; /* nz^2, nz*lz */
38 gint background; /* Shade for vertical normals */
39 gdouble compensation; /* Background compensation */
40 guchar lut[256]; /* Look-up table for modes */
42
43void bumpmap_init_params (bumpmap_params_t *params, const bumpmap_vals_t &bmvals);
44
45void
46bumpmap_row (const bumpmap_vals_t &bmvals,
47 guchar *dest,
48 gint width,
49 const guchar *bm_row1,
50 const guchar *bm_row2,
51 const guchar *bm_row3,
52 bumpmap_params_t *params);
53
55
56void convertRow(quint8 *data, int width, const quint8 *lut)
57{
58 for (int i = 0; i < width; i++) {
59 *data = lut[*data];
60 data++;
61 }
62}
63
65 const QRect &selectionRect,
66 const bumpmap_vals_t &bmvals)
67{
70
71 bumpmap_params_t params;
72 bumpmap_init_params (&params, bmvals);
73
74 const QRect dataRect = kisGrowRect(selectionRect, 1);
75
76 const int dataRowSize = dataRect.width() * sizeof(quint8);
77 const int selectionRowSize = selectionRect.width() * sizeof(quint8);
78 QScopedArrayPointer<quint8> dstRow(new quint8[selectionRowSize]);
79
80 std::unique_ptr<quint8[]> bmRow1(new quint8[dataRowSize]);
81 std::unique_ptr<quint8[]> bmRow2(new quint8[dataRowSize]);
82 std::unique_ptr<quint8[]> bmRow3(new quint8[dataRowSize]);
83
84 device->readBytes(bmRow1.get(), dataRect.left(), dataRect.top(), dataRect.width(), 1);
85 device->readBytes(bmRow2.get(), dataRect.left(), dataRect.top() + 1, dataRect.width(), 1);
86 device->readBytes(bmRow3.get(), dataRect.left(), dataRect.top() + 2, dataRect.width(), 1);
87
88 convertRow(bmRow1.get(), dataRect.width(), params.lut);
89 convertRow(bmRow2.get(), dataRect.width(), params.lut);
90 convertRow(bmRow3.get(), dataRect.width(), params.lut);
91
92 for (int row = selectionRect.top();
93 row < selectionRect.top() + selectionRect.height(); row++) {
94
95 bumpmap_row (bmvals, dstRow.data(), selectionRect.width(),
96 bmRow1.get() + 1, bmRow2.get() + 1, bmRow3.get() + 1,
97 &params);
98
99 device->writeBytes(dstRow.data(), selectionRect.left(), row, selectionRect.width(), 1);
100
101 bmRow1.swap(bmRow2);
102 bmRow2.swap(bmRow3);
103
104 device->readBytes(bmRow3.get(), dataRect.left(), row + 1, dataRect.width(), 1);
105 convertRow(bmRow3.get(), dataRect.width(), params.lut);
106 }
107}
108
110{
111 /* Convert to radians */
112 const gdouble azimuth = G_PI * bmvals.azimuth / 180.0;
113 const gdouble elevation = G_PI * bmvals.elevation / 180.0;
114
115 gint lz, nz;
116 gint i;
117
118 /* Calculate the light vector */
119 params->lx = cos (azimuth) * cos (elevation) * 255.0;
120 params->ly = sin (azimuth) * cos (elevation) * 255.0;
121 lz = sin (elevation) * 255.0;
122
123 /* Calculate constant Z component of surface normal */
124 /* (depth may be 0 if non-interactive) */
125 nz = (6 * 255) / qMax (bmvals.depth, 1);
126 params->nz2 = nz * nz;
127 params->nzlz = nz * lz;
128
129 /* Optimize for vertical normals */
130 params->background = lz;
131
132 /* Calculate darkness compensation factor */
133 params->compensation = sin(elevation);
134
135 /* Create look-up table for map type */
136 for (i = 0; i < 256; i++)
137 {
138 gdouble n;
139
140 switch (bmvals.type)
141 {
142 case SPHERICAL:
143 n = i / 255.0 - 1.0;
144 params->lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
145 break;
146
147 case SINUSOIDAL:
148 n = i / 255.0;
149 params->lut[i] = (int) (255.0 *
150 (sin((-G_PI / 2.0) + G_PI * n) + 1.0) /
151 2.0 + 0.5);
152 break;
153
154 case LINEAR:
155 default:
156 params->lut[i] = i;
157 }
158
159 if (bmvals.invert)
160 params->lut[i] = 255 - params->lut[i];
161 }
162}
163
164void
166 guchar *dest,
167 gint width,
168 const guchar *bm_row1,
169 const guchar *bm_row2,
170 const guchar *bm_row3,
171 bumpmap_params_t *params)
172{
173 gint xofs1, xofs2;
174 gint x;
175
176 for (x = 0; x < width; x++) {
177 gint xofs3;
178 gint shade;
179 gint nx, ny;
180
181 /* Calculate surface normal from bump map */
182
183 xofs2 = x;
184 xofs1 = xofs2 - 1;
185 xofs3 = xofs2 + 1;
186
187 nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
188 bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
189 ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
190 bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
191
192 /* Shade */
193
194 if ((nx == 0) && (ny == 0)) {
195 shade = params->background;
196 } else {
197 gint ndotl = nx * params->lx + ny * params->ly + params->nzlz;
198
199 if (ndotl < 0) {
200 shade = params->compensation * bmvals.ambient;
201 } else {
202 shade = ndotl / sqrt (nx * nx + ny * ny + params->nz2);
203
204 shade = shade + MAX(0.0, (255 * params->compensation - shade)) *
205 bmvals.ambient / 255;
206 }
207 }
208
209 /* Paint */
210
211 if (bmvals.compensate) {
212 int result = shade / params->compensation;
213 *dest++ = MIN(255, result);
214 } else {
215 *dest++ = shade;
216 }
217 }
218}
219
void readBytes(quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) const
void writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
void bumpmap_init_params(bumpmap_params_t *params, const bumpmap_vals_t &bmvals)
#define G_PI
bool gboolean
void bumpmap(KisPixelSelectionSP device, const QRect &selectionRect, const bumpmap_vals_t &bmvals)
void bumpmap_row(const bumpmap_vals_t &bmvals, guchar *dest, gint width, const guchar *bm_row1, const guchar *bm_row2, const guchar *bm_row3, bumpmap_params_t *params)
#define MIN(x, y)
int gint
#define MAX(x, y)
quint8 guchar
double gdouble
void convertRow(quint8 *data, int width, const quint8 *lut)
qint32 gint32
@ SINUSOIDAL
@ SPHERICAL
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
@ LINEAR
Definition nugrid.h:26