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 (&params, 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                      &params);
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