File indexing completed on 2024-12-22 04:10:06
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * gimp_bump_map contains code taken from gimp-bumpmap.c, original copyright: 0005 * 0006 * SPDX-FileCopyrightText: 1997 Federico Mena Quintero <federico@nuclecu.unam.mx> 0007 * SPDX-FileCopyrightText: 1997-2000 Jens Lautenbacher <jtl@gimp.org> 0008 * SPDX-FileCopyrightText: 2000 Sven Neumann <sven@gimp.org> 0009 * 0010 * SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "gimp_bump_map.h" 0014 0015 #include <QRect> 0016 #include "kis_pixel_selection.h" 0017 #include "kis_image.h" 0018 0019 0020 typedef int gint; 0021 typedef qint32 gint32; 0022 typedef bool gboolean; 0023 typedef quint8 guchar; 0024 typedef double gdouble; 0025 0026 #define G_PI M_PI 0027 #define MOD(x, y) ((x) % (y)) 0028 #define CLAMP(x, l, h) qBound(l, x, h) 0029 #define MAX(x,y) qMax(x,y) 0030 #define MIN(x,y) qMin(x,y) 0031 0032 typedef struct 0033 { 0034 gint lx, ly; /* X and Y components of light vector */ 0035 gint nz2, nzlz; /* nz^2, nz*lz */ 0036 gint background; /* Shade for vertical normals */ 0037 gdouble compensation; /* Background compensation */ 0038 guchar lut[256]; /* Look-up table for modes */ 0039 } bumpmap_params_t; 0040 0041 void bumpmap_init_params (bumpmap_params_t *params, const bumpmap_vals_t &bmvals); 0042 0043 void 0044 bumpmap_row (const bumpmap_vals_t &bmvals, 0045 guchar *dest, 0046 gint width, 0047 const guchar *bm_row1, 0048 const guchar *bm_row2, 0049 const guchar *bm_row3, 0050 bumpmap_params_t *params); 0051 0052 //////////////////////////////////////////////////////////////////// 0053 0054 void convertRow(quint8 *data, int width, const quint8 *lut) 0055 { 0056 for (int i = 0; i < width; i++) { 0057 *data = lut[*data]; 0058 data++; 0059 } 0060 } 0061 0062 void bumpmap (KisPixelSelectionSP device, 0063 const QRect &selectionRect, 0064 const bumpmap_vals_t &bmvals) 0065 { 0066 KIS_ASSERT_RECOVER_RETURN(bmvals.xofs == 0); 0067 KIS_ASSERT_RECOVER_RETURN(bmvals.yofs == 0); 0068 0069 bumpmap_params_t params; 0070 bumpmap_init_params (¶ms, bmvals); 0071 0072 const QRect dataRect = kisGrowRect(selectionRect, 1); 0073 0074 const int dataRowSize = dataRect.width() * sizeof(quint8); 0075 const int selectionRowSize = selectionRect.width() * sizeof(quint8); 0076 QScopedArrayPointer<quint8> dstRow(new quint8[selectionRowSize]); 0077 0078 QScopedArrayPointer<quint8> bmRow1(new quint8[dataRowSize]); 0079 QScopedArrayPointer<quint8> bmRow2(new quint8[dataRowSize]); 0080 QScopedArrayPointer<quint8> bmRow3(new quint8[dataRowSize]); 0081 0082 device->readBytes(bmRow1.data(), dataRect.left(), dataRect.top(), dataRect.width(), 1); 0083 device->readBytes(bmRow2.data(), dataRect.left(), dataRect.top() + 1, dataRect.width(), 1); 0084 device->readBytes(bmRow3.data(), dataRect.left(), dataRect.top() + 2, dataRect.width(), 1); 0085 0086 convertRow(bmRow1.data(), dataRect.width(), params.lut); 0087 convertRow(bmRow2.data(), dataRect.width(), params.lut); 0088 convertRow(bmRow3.data(), dataRect.width(), params.lut); 0089 0090 for (int row = selectionRect.top(); 0091 row < selectionRect.top() + selectionRect.height(); row++) { 0092 0093 bumpmap_row (bmvals, dstRow.data(), selectionRect.width(), 0094 bmRow1.data() + 1, bmRow2.data() + 1, bmRow3.data() + 1, 0095 ¶ms); 0096 0097 device->writeBytes(dstRow.data(), selectionRect.left(), row, selectionRect.width(), 1); 0098 0099 bmRow1.swap(bmRow2); 0100 bmRow2.swap(bmRow3); 0101 0102 device->readBytes(bmRow3.data(), dataRect.left(), row + 1, dataRect.width(), 1); 0103 convertRow(bmRow3.data(), dataRect.width(), params.lut); 0104 } 0105 } 0106 0107 void bumpmap_init_params (bumpmap_params_t *params, const bumpmap_vals_t &bmvals) 0108 { 0109 /* Convert to radians */ 0110 const gdouble azimuth = G_PI * bmvals.azimuth / 180.0; 0111 const gdouble elevation = G_PI * bmvals.elevation / 180.0; 0112 0113 gint lz, nz; 0114 gint i; 0115 0116 /* Calculate the light vector */ 0117 params->lx = cos (azimuth) * cos (elevation) * 255.0; 0118 params->ly = sin (azimuth) * cos (elevation) * 255.0; 0119 lz = sin (elevation) * 255.0; 0120 0121 /* Calculate constant Z component of surface normal */ 0122 /* (depth may be 0 if non-interactive) */ 0123 nz = (6 * 255) / qMax (bmvals.depth, 1); 0124 params->nz2 = nz * nz; 0125 params->nzlz = nz * lz; 0126 0127 /* Optimize for vertical normals */ 0128 params->background = lz; 0129 0130 /* Calculate darkness compensation factor */ 0131 params->compensation = sin(elevation); 0132 0133 /* Create look-up table for map type */ 0134 for (i = 0; i < 256; i++) 0135 { 0136 gdouble n; 0137 0138 switch (bmvals.type) 0139 { 0140 case SPHERICAL: 0141 n = i / 255.0 - 1.0; 0142 params->lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5); 0143 break; 0144 0145 case SINUSOIDAL: 0146 n = i / 255.0; 0147 params->lut[i] = (int) (255.0 * 0148 (sin((-G_PI / 2.0) + G_PI * n) + 1.0) / 0149 2.0 + 0.5); 0150 break; 0151 0152 case LINEAR: 0153 default: 0154 params->lut[i] = i; 0155 } 0156 0157 if (bmvals.invert) 0158 params->lut[i] = 255 - params->lut[i]; 0159 } 0160 } 0161 0162 void 0163 bumpmap_row (const bumpmap_vals_t &bmvals, 0164 guchar *dest, 0165 gint width, 0166 const guchar *bm_row1, 0167 const guchar *bm_row2, 0168 const guchar *bm_row3, 0169 bumpmap_params_t *params) 0170 { 0171 gint xofs1, xofs2; 0172 gint x; 0173 0174 for (x = 0; x < width; x++) { 0175 gint xofs3; 0176 gint shade; 0177 gint nx, ny; 0178 0179 /* Calculate surface normal from bump map */ 0180 0181 xofs2 = x; 0182 xofs1 = xofs2 - 1; 0183 xofs3 = xofs2 + 1; 0184 0185 nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] - 0186 bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]); 0187 ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] - 0188 bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]); 0189 0190 /* Shade */ 0191 0192 if ((nx == 0) && (ny == 0)) { 0193 shade = params->background; 0194 } else { 0195 gint ndotl = nx * params->lx + ny * params->ly + params->nzlz; 0196 0197 if (ndotl < 0) { 0198 shade = params->compensation * bmvals.ambient; 0199 } else { 0200 shade = ndotl / sqrt (nx * nx + ny * ny + params->nz2); 0201 0202 shade = shade + MAX(0.0, (255 * params->compensation - shade)) * 0203 bmvals.ambient / 255; 0204 } 0205 } 0206 0207 /* Paint */ 0208 0209 if (bmvals.compensate) { 0210 int result = shade / params->compensation; 0211 *dest++ = MIN(255, result); 0212 } else { 0213 *dest++ = shade; 0214 } 0215 } 0216 } 0217