summaryrefslogtreecommitdiff
path: root/operations
diff options
context:
space:
mode:
authorThomas Manni <thomas.manni@free.fr>2015-06-01 19:01:00 +0200
committerThomas Manni <thomas.manni@free.fr>2015-06-03 19:48:41 +0200
commit087d54eed44e02acd9fc11e720f375ce60bb9d6c (patch)
treec1ff1cedadf723bc69f95958cc002abe3edd140f /operations
parentd9ceb783488544fbb514c155c0dfc28107292fe9 (diff)
operations: bump-map: complete rewrite
Diffstat (limited to 'operations')
-rw-r--r--operations/common/bump-map.c452
1 files changed, 216 insertions, 236 deletions
diff --git a/operations/common/bump-map.c b/operations/common/bump-map.c
index a4773119..2f060537 100644
--- a/operations/common/bump-map.c
+++ b/operations/common/bump-map.c
@@ -22,16 +22,9 @@
*
* Ported to gegl by Dov Grobgeld <dov.grobgeld@gmail.com>
*
- * ToDo:
- *
- * - Make lut table size configurable?
- * - Support and y-offset tiling of the bump map.
- * - Make an opencl version.
- * - Don't "upgrade" Y,YA and RGB images to RGBA as it is a waste of time.
*/
#include "config.h"
-
#include <glib/gi18n-lib.h>
#ifdef GEGL_PROPERTIES
@@ -60,20 +53,20 @@ property_double (azimuth, _("Azimuth"), 135.0)
ui_meta ("unit", "degree")
property_double (elevation, _("Elevation"), 45.0)
- value_range (0.0, 360.0)
+ value_range (0.5, 90.0)
-property_double (depth, _("Depth"), 0.005)
- value_range (0.0005, 100)
+property_int (depth, _("Depth"), 3)
+ value_range (1, 65)
-property_int (offset_x, _("Offset X"), -1000.0)
+property_int (offset_x, _("Offset X"), 0)
value_range (-20000, 20000)
- ui_range (-1000.0, 1000.0)
+ ui_range (-1000, 1000)
ui_meta ("axis", "x")
ui_meta ("unit", "pixel-coordinate")
-property_int (offset_y, _("Offset Y"), -1000.0)
+property_int (offset_y, _("Offset Y"), 0)
value_range (-20000, 20000)
- ui_range (-1000.0, 1000.0)
+ ui_range (-1000, 1000)
ui_meta ("axis", "y")
ui_meta ("unit", "pixel-coordinate")
@@ -92,43 +85,44 @@ property_double (ambient, _("Ambient lighting factor"), 0.0)
#include "gegl-op.h"
#include <math.h>
-/***** Macros *****/
-
-// Should the LUT table be a configurable property? Should it
-// be interpolated instead of by a plain lookup?
-#define LUT_TABLE_SIZE 8192
-
-#define MOD(x, y) \
- ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
+#define LUT_SIZE 2048
typedef struct
{
- gdouble lx, ly; /* X and Y components of light vector */
- gdouble nz2, nzlz; /* nz^2, nz*lz */
- gdouble background; /* Shade for vertical normals */
- gdouble compensation; /* Background compensation */
- gfloat lut[LUT_TABLE_SIZE]; /* Look-up table for modes - should be made interpolated*/
+ gdouble lx, ly; /* X and Y components of light vector */
+ gdouble nz2, nzlz; /* nz^2, nz*lz */
+ gdouble background; /* Shade for vertical normals */
+ gdouble compensation; /* Background compensation */
+ gdouble lut[LUT_SIZE]; /* Look-up table for modes - should be made interpolated*/
+
+ gboolean in_has_alpha; /* babl formats info */
+ gboolean bm_has_alpha;
+ gint in_components;
+ gint bm_components;
} bumpmap_params_t;
static void
-bumpmap_setup_calc (GeglProperties *o,
- bumpmap_params_t *params)
+bumpmap_init_params (GeglProperties *o,
+ const Babl *in_format,
+ const Babl *bm_format)
{
+ bumpmap_params_t *params = (bumpmap_params_t *) o->user_data;
+ gdouble lz, nz;
+ gint i;
+
+ gint scale = LUT_SIZE - 1;
+
/* Convert to radians */
const gdouble azimuth = G_PI * o->azimuth / 180.0;
const gdouble elevation = G_PI * o->elevation / 180.0;
- gdouble lz, nz;
- gint i;
-
/* Calculate the light vector */
params->lx = cos (azimuth) * cos (elevation);
params->ly = sin (azimuth) * cos (elevation);
lz = sin (elevation);
/* Calculate constant Z component of surface normal */
- /* (depth may be 0 if non-interactive) */
- nz = 6.0 / MAX (o->depth, 0.001);
+ nz = 6.0 / o->depth;
params->nz2 = nz * nz;
params->nzlz = nz * lz;
@@ -139,152 +133,194 @@ bumpmap_setup_calc (GeglProperties *o,
params->compensation = sin (elevation);
/* Create look-up table for map type */
- for (i = 0; i < LUT_TABLE_SIZE; i++)
+ for (i = 0; i < LUT_SIZE; i++)
{
gdouble n;
switch (o->type)
{
case GEGL_BUMP_MAP_TYPE_SPHERICAL:
- n = 1.0 - 1.0*i / LUT_TABLE_SIZE;
- params->lut[i] = sqrt(1.0 - n * n) + 0.5;
+ n = (gdouble) i / scale - 1.0;
+ params->lut[i] = sqrt (1.0 - n * n) + 0.5;
break;
case GEGL_BUMP_MAP_TYPE_SINUSOIDAL:
- n = 1.0 * i / (LUT_TABLE_SIZE-1);
- params->lut[i] = (sin((-G_PI / 2.0) + G_PI * n) + 1.0) / 2.0 + 0.5;
+ n = (gdouble) i / scale;
+ params->lut[i] = (sin ((-G_PI / 2.0) + G_PI * n) + 1.0) / 2.0 + 0.5;
break;
case GEGL_BUMP_MAP_TYPE_LINEAR:
default:
- params->lut[i] = 1.0*i/(LUT_TABLE_SIZE-1);
+ params->lut[i] = (gdouble) i / scale;
}
if (o->invert)
params->lut[i] = 1.0 - params->lut[i];
}
+
+ /* babl format stuff */
+ params->in_has_alpha = babl_format_has_alpha (in_format);
+ params->bm_has_alpha = babl_format_has_alpha (bm_format);
+ params->in_components = babl_format_get_n_components (in_format);
+ params->bm_components = babl_format_get_n_components (bm_format);
}
static void
-bumpmap_row (const gfloat *src,
- gfloat *dest,
- gint width,
- gint nchannels,
- gint has_alpha,
- const gfloat *bm_row1,
- const gfloat *bm_row2,
- const gfloat *bm_row3,
- gint bm_width,
- gint bm_xofs,
- gboolean row_in_bumpmap,
- GeglProperties *o,
- bumpmap_params_t *params)
+bumpmap_row (gfloat *row,
+ gint width,
+ const gfloat *bm_row1,
+ const gfloat *bm_row2,
+ const gfloat *bm_row3,
+ gint bm_width,
+ gboolean row_in_bumpmap,
+ gint roix,
+ GeglProperties *o)
{
+ bumpmap_params_t *params = (bumpmap_params_t *) o->user_data;
gint xofs1, xofs2, xofs3;
- gint x, k;
- gdouble result;
-
- xofs2 = MOD (bm_xofs,bm_width);
+ gint x, b;
+ gfloat *buf = row;
for (x = 0; x < width; x++)
{
- double shade;
- double nx, ny;
+ gdouble shade;
+ gdouble nx, ny;
/* Calculate surface normal from bump map */
if (o->tiled || (row_in_bumpmap &&
- x >= - bm_xofs && x < - bm_xofs + bm_width))
+ roix + x >= - o->offset_x && roix + x < - o->offset_x + bm_width))
{
- if (o->tiled)
- {
- xofs1 = MOD (xofs2 - 1, bm_width);
- xofs3 = MOD (xofs2 + 1, bm_width);
- }
- else
- {
- xofs1 = CLAMP (xofs2 - 1, 0, bm_width - 1);
- xofs3 = CLAMP (xofs2 + 1, 0, bm_width - 1);
- }
+ xofs2 = (x + 1) * params->bm_components;
+ xofs1 = CLAMP (x * params->bm_components, 0, (width + 1) * params->bm_components);
+ xofs3 = CLAMP ((x + 2) * params->bm_components, 0, (width + 1) * params->bm_components);
nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
+
ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
}
else
{
- nx = ny = 0;
+ nx = ny = 0.0;
}
- if ((nx == 0) && (ny == 0))
+ if ((nx == 0.0) && (ny == 0.0))
{
shade = params->background;
}
else
{
- double ndotl = nx * params->lx + ny * params->ly + params->nzlz;
+ gdouble ndotl = nx * params->lx + ny * params->ly + params->nzlz;
- if (ndotl < 0)
+ if (ndotl < 0.0)
{
shade = params->compensation * o->ambient;
}
else
{
- double pre_shade = ndotl / sqrt (nx * nx + ny * ny + params->nz2);
-
- shade = pre_shade + MAX(0, (params->compensation - pre_shade)) *
- o->ambient;
+ shade = ndotl / sqrt (nx * nx + ny * ny + params->nz2);
+ shade += MAX(0, (params->compensation - shade)) * o->ambient;
}
}
/* Paint */
if (o->compensate)
{
- for (k = nchannels-has_alpha; k; k--)
- {
- result = (*src++ * shade) / params->compensation;
- *dest++ = result;
- }
+ for (b = 0 ; b < 3; b++)
+ buf[b] = (buf[b] * shade) / params->compensation;
}
else
{
- for (k = nchannels-has_alpha; k; k--)
- *dest++ = *src++ * shade;
+ for (b = 0 ; b < 3; b++)
+ buf[b] = buf[b] * shade;
}
- if (has_alpha)
- *dest++ = *src++;
+ if (params->in_has_alpha)
+ buf[3] = buf[3];
- /* Next pixel */
- if (++xofs2 == bm_width)
- xofs2 = 0;
+ buf += params->in_components;
}
}
-// Map the FIFO bumpmap row according to the lookup table.
static void
-bumpmap_convert_row (gfloat *row,
- gint width,
- const gfloat *lut,
- double waterlevel)
+bumpmap_convert (gfloat *buffer,
+ glong n_pixels,
+ GeglProperties *o)
{
- gfloat *p = row;
+ bumpmap_params_t *params = (bumpmap_params_t *) o->user_data;
+ gint idx;
+ gint scale = LUT_SIZE - 1;
+ gfloat *p = buffer;
- for (; width; width--)
+ while (n_pixels--)
{
- int lut_idx = CLAMP((int)(waterlevel * (LUT_TABLE_SIZE-1)
- + *p *(LUT_TABLE_SIZE-1)*(1.0-waterlevel)),0,(LUT_TABLE_SIZE-1));
- *p++ = lut[lut_idx];
+ gfloat value = CLAMP (p[0], 0.0, 1.0);
+
+ if (params->bm_has_alpha)
+ {
+ gfloat alpha = CLAMP (p[1], 0.0, 1.0);
+ idx = (gint) ((o->waterlevel + (value - o->waterlevel) * alpha) * scale);
+ }
+ else
+ idx = (gint) (value * scale);
+
+ p[0] = params->lut[idx];
+ p += params->bm_components;
}
}
static void
prepare (GeglOperation *operation)
{
- gegl_operation_set_format (operation, "input", babl_format ("RGBA float"));
- gegl_operation_set_format (operation, "aux", babl_format ("Y float"));
- gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const Babl *in_format = gegl_operation_get_source_format (operation, "input");
+ const Babl *bm_format = gegl_operation_get_source_format (operation, "aux");
+
+ if (!o->user_data)
+ o->user_data = g_slice_new0 (bumpmap_params_t);
+
+ if (in_format)
+ {
+ if (babl_format_has_alpha (in_format))
+ in_format = babl_format ("R'G'B'A float");
+ else
+ in_format = babl_format ("R'G'B' float");
+ }
+ else
+ in_format = babl_format ("R'G'B' float");
+
+ if (bm_format)
+ {
+ if (babl_format_has_alpha (bm_format))
+ bm_format = babl_format ("Y'A float");
+ else
+ bm_format = babl_format ("Y' float");
+ }
+ else
+ bm_format = babl_format ("Y' float");
+
+ bumpmap_init_params (o, in_format, bm_format);
+
+ gegl_operation_set_format (operation, "input", in_format);
+ gegl_operation_set_format (operation, "aux", bm_format);
+ gegl_operation_set_format (operation, "output", in_format);
+}
+
+static void
+finalize (GObject *object)
+{
+ GeglOperation *op = (void*) object;
+ GeglProperties *o = GEGL_PROPERTIES (op);
+
+ if (o->user_data)
+ {
+ g_slice_free (bumpmap_params_t, o->user_data);
+ o->user_data = NULL;
+ }
+
+ G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
}
static gboolean
@@ -292,162 +328,81 @@ process (GeglOperation *operation,
GeglBuffer *input,
GeglBuffer *aux,
GeglBuffer *output,
- const GeglRectangle *rect,
+ const GeglRectangle *roi,
gint level)
{
- GeglProperties *o = GEGL_PROPERTIES (operation);
- gfloat *src_buf, *dst_buf, *bm_row1, *bm_row2, *bm_row3, *src_row, *dest_row, *bm_tmprow;
- const Babl *format = gegl_operation_get_format (operation, "output");
- gint channels = babl_format_get_n_components (format);
- bumpmap_params_t params;
- gint yofs1, yofs2, yofs3;
- gint row_stride;
- gint row, y;
- gint bm_width, bm_height;
- gint slice_thickness = 32;
- gboolean first_time = TRUE;
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ bumpmap_params_t *params = (bumpmap_params_t *) o->user_data;
- // This should be made more sophisticated
- int has_alpha = (channels == 4) || (channels == 2);
+ const Babl *in_format = gegl_operation_get_format (operation, "input");
+ const Babl *bm_format = gegl_operation_get_format (operation, "aux");
- if (! aux)
- return FALSE;
+ GeglAbyssPolicy repeat_mode = o->tiled ? GEGL_ABYSS_LOOP : GEGL_ABYSS_CLAMP;
- bm_width = gegl_buffer_get_width (aux);
- bm_height = gegl_buffer_get_height (aux);
+ GeglRectangle bm_rect, bm_extent;
- src_buf = g_new0 (gfloat, rect->width * slice_thickness * channels);
- dst_buf = g_new0 (gfloat, rect->width * slice_thickness * channels);
+ gfloat *src_row, *src_buffer, *bm_buffer;
+ gfloat *bm_row1, *bm_row2, *bm_row3;
+ gint y;
+ gboolean row_in_bumpmap;
- bumpmap_setup_calc (o, &params);
+ src_buffer = g_new (gfloat, roi->width * roi->height * params->in_components);
- /* Initialize offsets */
- if (o->tiled)
- {
- yofs2 = MOD (o->offset_y, bm_height);
- yofs1 = MOD (yofs2 - 1, bm_height);
- yofs3 = MOD (yofs2 + 1, bm_height);
- }
- else
+ gegl_buffer_get (input, roi, 1.0, in_format, src_buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (aux)
{
- yofs2 = o->offset_y;
- yofs1 = yofs2 - 1;
- yofs3 = yofs2 + 1;
- }
+ bm_extent = *gegl_buffer_get_extent (aux);
- /* Initialize three line fifo buffers */
- bm_row1 = g_new (gfloat, bm_width);
- bm_row2 = g_new (gfloat, bm_width);
- bm_row3 = g_new (gfloat, bm_width);
+ bm_rect = *roi;
+ bm_rect.x += o->offset_x - 1;
+ bm_rect.y += o->offset_y - 1;
+ bm_rect.width += 2;
+ bm_rect.height += 2;
- // The source and destination row stride in floats
- row_stride = rect->width*channels;
+ bm_buffer = g_new (gfloat, bm_rect.width * bm_rect.height * params->bm_components);
- // Process the input buffer one slice at a time, but the bumpmap one row at a time.
- for (row=rect->y; row < rect->y+rect->height; row+= slice_thickness)
- {
- GeglRectangle rect_slice, bm_rect;
- rect_slice.x = rect->x;
- rect_slice.width = rect->width;
- rect_slice.y = rect->y+row;
- rect_slice.height = MIN (slice_thickness, rect->height-row);
- gegl_buffer_get (input, &rect_slice, 1.0,
- babl_format ("RGBA float"), src_buf,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
- // Get the bumpmap one row at a time. The following values are constant
- // in the bumpmap buffer access.
- bm_rect.x = 0;
- bm_rect.width = bm_width;
- bm_rect.height = 1;
-
- for (y = 0; y < rect_slice.height; y++)
- {
- gboolean row_in_bumpmap = (yofs2 > 0 && yofs2 < bm_height);
+ gegl_buffer_get (aux, &bm_rect, 1.0, bm_format,
+ bm_buffer, GEGL_AUTO_ROWSTRIDE, repeat_mode);
- // Fill in the three rows FIFO the first time we are inside the bumpmap.
- if (row_in_bumpmap && first_time)
- {
- first_time = FALSE;
-
- bm_rect.y = yofs1;
- gegl_buffer_get (aux, &bm_rect, 1.0,
- babl_format ("Y float"), bm_row1,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
- bm_rect.y = yofs2;
- gegl_buffer_get (aux, &bm_rect, 1.0,
- babl_format ("Y float"), bm_row2,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
- bm_rect.y = yofs3;
- gegl_buffer_get (aux, &bm_rect, 1.0,
- babl_format ("Y float"), bm_row3,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
- bumpmap_convert_row (bm_row1, bm_width, params.lut, o->waterlevel);
- bumpmap_convert_row (bm_row2, bm_width, params.lut, o->waterlevel);
- bumpmap_convert_row (bm_row3, bm_width, params.lut, o->waterlevel);
- }
+ bumpmap_convert (bm_buffer, bm_rect.width * bm_rect.height, o);
- src_row = src_buf + y * row_stride;
- dest_row = dst_buf + y * row_stride;
-
- bumpmap_row (src_row, dest_row,
- rect->width,
- channels,
- has_alpha,
- bm_row1, bm_row2, bm_row3,
- bm_width,
- o->offset_x,
- row_in_bumpmap,
- o,
- &params);
-
- if (!first_time)
- {
- /* Next line */
- bm_tmprow = bm_row1;
- bm_row1 = bm_row2;
- bm_row2 = bm_row3;
- bm_row3 = bm_tmprow;
- }
+ bm_row1 = bm_buffer;
+ bm_row2 = bm_buffer + (bm_rect.width * params->bm_components);
+ bm_row3 = bm_buffer + (2 * bm_rect.width * params->bm_components);
- if (++yofs2 == bm_height && o->tiled)
- yofs2 = 0;
+ for (y = roi->y; y < roi->y + roi->height; y++)
+ {
+ row_in_bumpmap = (y >= - o->offset_y && y < - o->offset_y + bm_extent.height);
- if (o->tiled)
- yofs3 = MOD (yofs2 + 1, bm_height);
- else
- yofs3 = CLAMP (yofs2 + 1, 0, bm_height);
+ src_row = src_buffer + ((y - roi->y) * roi->width * params->in_components);
- if (!first_time)
- {
- bm_rect.y = yofs3;
- gegl_buffer_get (aux, &bm_rect, 1.0, babl_format ("Y float"), bm_row3,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
- bumpmap_convert_row (bm_row3, bm_width, params.lut, o->waterlevel);
- }
- }
+ bumpmap_row (src_row, roi->width,
+ bm_row1, bm_row2, bm_row3, bm_extent.width,
+ row_in_bumpmap, roi->x, o);
- gegl_buffer_set (output, &rect_slice, 0,
- babl_format ("RGBA float"), dst_buf,
- GEGL_AUTO_ROWSTRIDE);
+ bm_row1 = bm_row2;
+ bm_row2 = bm_row3;
+ bm_row3 = bm_row2 + (bm_rect.width * params->bm_components);
+ }
+
+ g_free (bm_buffer);
}
- g_free (bm_row1);
- g_free (bm_row2);
- g_free (bm_row3);
+ gegl_buffer_set (output, roi, level, in_format,
+ src_buffer, GEGL_AUTO_ROWSTRIDE);
- g_free (dst_buf);
- g_free (src_buf);
+ g_free (src_buffer);
return TRUE;
}
static GeglRectangle
-get_bounding_box (GeglOperation *self)
+get_bounding_box (GeglOperation *operation)
{
GeglRectangle result = { 0, 0, 0, 0 };
- GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (self, "input");
+ GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input");
if (! in_rect)
return result;
@@ -455,18 +410,43 @@ get_bounding_box (GeglOperation *self)
return *in_rect;
}
+static GeglRectangle
+get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ if (!strcmp (input_pad, "aux"))
+ {
+ GeglRectangle bm_rect = *gegl_operation_source_get_bounding_box (operation,
+ "aux");
+ if (gegl_rectangle_is_empty (&bm_rect))
+ return *roi;
+
+ return bm_rect;
+ }
+
+ return *roi;
+}
+
static void
gegl_op_class_init (GeglOpClass *klass)
{
+ GObjectClass *object_class;
GeglOperationClass *operation_class;
GeglOperationComposerClass *composer_class;
+ object_class = G_OBJECT_CLASS (klass);
operation_class = GEGL_OPERATION_CLASS (klass);
composer_class = GEGL_OPERATION_COMPOSER_CLASS (klass);
- operation_class->prepare = prepare;
- operation_class->get_bounding_box = get_bounding_box;
- composer_class->process = process;
+ object_class->finalize = finalize;
+
+ operation_class->prepare = prepare;
+ operation_class->get_bounding_box = get_bounding_box;
+ operation_class->get_required_for_output = get_required_for_output;
+ operation_class->opencl_support = FALSE;
+
+ composer_class->process = process;
gegl_operation_class_set_keys (operation_class,
"name", "gegl:bump-map",
@@ -476,8 +456,8 @@ gegl_op_class_init (GeglOpClass *klass)
"description", _("This plug-in uses the algorithm described by John "
"Schlag, \"Fast Embossing Effects on Raster Image "
"Data\" in Graphics GEMS IV (ISBN 0-12-336155-9). "
- "It takes a drawable to be applied as a bump "
- "map to another image and produces a nice embossing "
+ "It takes a buffer to be applied as a bump "
+ "map to another buffer and produces a nice embossing "
"effect."),
NULL);
}