emboss.c 8.75 KB
Newer Older
1 2
/* This file is an image processing operation for GEGL
 *
3 4 5 6
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
7
 *
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
15 16 17 18 19 20 21 22
 *
 * Copyright 1997 Eric L. Hernes (erich@rrnet.com)
 * Copyright 2011 Robert Sasu (sasu.robert@gmail.com)
 */

#include "config.h"
#include <glib/gi18n-lib.h>

23 24
#ifdef GEGL_PROPERTIES

25
enum_start (gegl_emboss_type)
26 27
  enum_value (GEGL_EMBOSS_TYPE_EMBOSS,  "emboss",  N_("Emboss"))
  enum_value (GEGL_EMBOSS_TYPE_BUMPMAP, "bumpmap", N_("Bumpmap (preserve original colors)"))
28
enum_end (GeglEmbossType)
29

30 31 32 33 34 35 36 37
property_enum (type, _("Emboss Type"),
               GeglEmbossType, gegl_emboss_type, GEGL_EMBOSS_TYPE_EMBOSS)
    description(_("Rendering type"))

property_double (azimuth, _("Azimuth"), 30.0)
    description (_("Light angle (degrees)"))
    value_range (0, 360)
    ui_meta ("unit", "degree")
38
    ui_meta ("direction", "ccw")
39 40 41 42 43 44 45 46 47

property_double (elevation, _("Elevation"), 45.0)
    description (_("Elevation angle (degrees)"))
    value_range (0, 180)
    ui_meta ("unit", "degree")

property_int (depth, _("Depth"), 20)
    description (_("Filter width"))
    value_range (1, 100)
48 49 50

#else

51
#define GEGL_OP_AREA_FILTER
52
#define GEGL_OP_NAME     emboss
53
#define GEGL_OP_C_SOURCE emboss.c
54

55
#include "gegl-op.h"
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
#include <math.h>
#include <stdio.h>

#define DEG_TO_RAD(d) (((d) * G_PI) / 180.0)

/*
 * ANSI C code from the article
 * "Fast Embossing Effects on Raster Image Data"
 * by John Schlag, jfs@kerner.com
 * in "Graphics Gems IV", Academic Press, 1994
 *
 * Emboss - shade 24-bit pixels using a single distant light source.
 * Normals are obtained by differentiating a monochrome 'bump' image.
 * The unary case ('texture' == NULL) uses the shading result as output.
 * The binary case multiples the optional 'texture' image by the shade.
 * Images are in row major order with interleaved color components
 * (rgbrgb...).  E.g., component c of pixel x,y of 'dst' is
 * dst[3*(y*width + x) + c].
 */

static void
emboss (gfloat              *src_buf,
        const GeglRectangle *src_rect,
        gfloat              *dst_buf,
        const GeglRectangle *dst_rect,
81 82
        GeglEmbossType       type,
        gint                 y,
83 84 85 86 87
        gint                 floats_per_pixel,
        gdouble              azimuth,
        gdouble              elevation,
        gint                 width45)
{
88
  gint x;
89 90 91 92 93 94 95 96 97 98 99 100 101
  gint offset, verify;
  gint bytes;

  gdouble Lx, Ly, Lz;
  gdouble Nz, Nz2, NzLz;

  Lx = cos (azimuth) * cos (elevation);
  Ly = sin (azimuth) * cos (elevation);
  Lz = sin (elevation);
  Nz = 1.0 / width45;
  Nz2  = Nz * Nz;
  NzLz = Nz * Lz;

102
  bytes = floats_per_pixel - 1;
103 104

  verify = src_rect->width * src_rect->height * floats_per_pixel;
105
  offset = y * dst_rect->width * floats_per_pixel;
106

107
  for (x = 0; x < dst_rect->width; x++)
108 109 110 111 112 113 114 115 116 117 118 119
    {
      gint   i, j, b, count;
      gfloat Nx, Ny, NdotL;
      gfloat shade;
      gfloat M[3][3];
      gfloat a;

      for (i = 0; i < 3; i++)
        for (j = 0; j < 3; j++)
          M[i][j] = 0.0;

      for (b = 0; b < bytes; b++)
120 121
        {
          for (i = 0; i < 3; i++)
122
            {
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
              for (j = 0; j < 3; j++)
                {
                  count = ((y + i - 1) * src_rect->width + (x + j - 1)) * floats_per_pixel + bytes;

                  /* verify each time that we are in the source image */
                  if (count >= 0 && count < verify)
                    a = src_buf[count];
                  else
                    a = 1.0;

                  /* calculate recalculate the sorrounding pixels by
                   * multiplication after we have that we can
                   * calculate new value of the pixel
                   */
                  if ((count - bytes + b) >= 0 && (count - bytes + b) < verify)
                    M[i][j] += a * src_buf[count - bytes + b];
                }
140
            }
141
        }
142 143 144 145

      Nx = M[0][0] + M[1][0] + M[2][0] - M[0][2] - M[1][2] - M[2][2];
      Ny = M[2][0] + M[2][1] + M[2][2] - M[0][0] - M[0][1] - M[0][2];

146 147
      /* calculating the shading result (same as in gimp) */
      if (Nx == 0 && Ny == 0)
148
        shade = Lz;
149
      else if ((NdotL = Nx * Lx + Ny * Ly + NzLz) < 0)
150 151
        shade = 0;
      else
152
        shade = NdotL / sqrt (Nx * Nx + Ny * Ny + Nz2);
153

154
      count = (y * src_rect->width + x) * floats_per_pixel;
155

156 157 158 159 160
      /* setting the value of the destination buffer */
      if (type == GEGL_EMBOSS_TYPE_EMBOSS)
        {
          dst_buf[offset++] = shade;
        }
161 162
      else
        {
163 164 165
          /* recalculating every byte of a pixel by multiplying with
           * the shading result
           */
166 167

          for (b = 0; b < bytes; b++)
168 169 170 171 172 173
            {
              if ((count + b) >= 0 && (count + b) < verify)
                dst_buf[offset++] = (src_buf[count + b] * shade);
              else
                dst_buf[offset++] = 1.0;
            }
174
        }
175 176 177 178 179 180

      /* preserving alpha */
      if ((count + bytes) >= 0 && (count + bytes) < verify)
        dst_buf[offset++] = src_buf[count + bytes];
      else
        dst_buf[offset++] = 1.0;
181 182 183 184 185 186
    }
}

static void
prepare (GeglOperation *operation)
{
187
  GeglProperties          *o       = GEGL_PROPERTIES (operation);
188
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
189
  const Babl              *space   = gegl_operation_get_source_space (operation, "input");
190 191 192

  op_area->left = op_area->right = op_area->top = op_area->bottom = 3;

193
  if (o->type == GEGL_EMBOSS_TYPE_BUMPMAP)
194
    gegl_operation_set_format (operation, "output",
195
                               babl_format_with_space ("RGBA float", space));
196 197
  else
    gegl_operation_set_format (operation, "output",
198
                               babl_format_with_space ("YA float", space));
199 200 201 202 203 204
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
205 206
         const GeglRectangle *result,
         gint                 level)
207
{
208
  GeglProperties          *o       = GEGL_PROPERTIES (operation);
209
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
210
  const Babl              *space   = gegl_operation_get_source_space (operation, "input");
211 212 213 214

  GeglRectangle  rect;
  gfloat        *src_buf;
  gfloat        *dst_buf;
215
  const Babl    *format;
216
  gint           y;
217
  gint           floats_per_pixel;
218
  float          factor = 1.0f / (1<<level);
219 220

  /*blur-map or emboss*/
221
  if (o->type == GEGL_EMBOSS_TYPE_BUMPMAP)
222
    {
223
      format = babl_format_with_space ("RGBA float", space);
224 225 226 227
      floats_per_pixel = 4;
    }
  else
    {
228
      format = babl_format_with_space ("YA float", space);
229
      floats_per_pixel = 2;
230 231 232 233 234 235 236
    }

  rect.x      = result->x - op_area->left;
  rect.width  = result->width + op_area->left + op_area->right;
  rect.y      = result->y - op_area->top;
  rect.height = result->height + op_area->top + op_area->bottom;

237 238 239 240 241 242 243 244
  if (level)
  {
    rect.x      *= factor;
    rect.y      *= factor;
    rect.width  *= factor;
    rect.height *= factor;
  }

245 246 247
  src_buf = g_new0 (gfloat, rect.width * rect.height * floats_per_pixel);
  dst_buf = g_new0 (gfloat, rect.width * rect.height * floats_per_pixel);

248
  gegl_buffer_get (input, &rect, factor, format, src_buf,
249
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
250 251

  /*do for every row*/
252 253
  for (y = 0; y < rect.height; y++)
    emboss (src_buf, &rect, dst_buf, &rect, o->type, y, floats_per_pixel,
254
            DEG_TO_RAD (o->azimuth), DEG_TO_RAD (o->elevation), o->depth * factor);
255

256
  gegl_buffer_set (output, &rect, level, format,
257 258 259 260 261 262 263 264 265
                   dst_buf, GEGL_AUTO_ROWSTRIDE);

  g_free (src_buf);
  g_free (dst_buf);

  return TRUE;
}

static void
266
gegl_op_class_init (GeglOpClass *klass)
267 268 269 270 271 272 273 274 275
{
  GeglOperationClass       *operation_class;
  GeglOperationFilterClass *filter_class;

  operation_class = GEGL_OPERATION_CLASS (klass);
  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);

  filter_class->process    = process;
  operation_class->prepare = prepare;
276
  operation_class->threaded = FALSE; // XXX: docs operation test yields horizontal stripe on division
277

278
  gegl_operation_class_set_keys (operation_class,
279
    "name",        "gegl:emboss",
280
    "title",       _("Emboss"),
281
    "reference-hash", "af0c6c39428853e1010fa4c51ee67c7d",
282
    "categories",  "light",
283
    "license",     "GPL3+",
284
    "description", _("Simulates an image created by embossing"),
285
    NULL);
286 287 288
}

#endif