/* This file is an image processing operation for GEGL * * GEGL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * GEGL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright (C) 1997 Lauri Alanko * Copyright 2011 Robert Sasu (sasu.robert@gmail.com) */ #include "config.h" #include #ifdef GEGL_CHANT_PROPERTIES gegl_chant_double (a1, _("(1,1) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (1,1)")) gegl_chant_double (a2, _("(1,2) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (1,2)")) gegl_chant_double (a3, _("(1,3) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (1,3)")) gegl_chant_double (a4, _("(1,4) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (1,4)")) gegl_chant_double (a5, _("(1,5) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (1,5)")) gegl_chant_double (b1, _("(2,1) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (2,1)")) gegl_chant_double (b2, _("(2,2) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (2,2)")) gegl_chant_double (b3, _("(2,3) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (2,3)")) gegl_chant_double (b4, _("(2,4) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (2,4)")) gegl_chant_double (b5, _("(2,5) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (2,5)")) gegl_chant_double (c1, _("(3,1) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (3,1)")) gegl_chant_double (c2, _("(3,2) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (3,2)")) gegl_chant_double (c3, _("(3,3) "), -G_MAXINT, G_MAXINT, 1.0, _("Value of the element in position (3,3)")) gegl_chant_double (c4, _("(3,4) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (3,4)")) gegl_chant_double (c5, _("(3,5) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (3,5)")) gegl_chant_double (d1, _("(4,1) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (4,1)")) gegl_chant_double (d2, _("(4,2) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (4,2)")) gegl_chant_double (d3, _("(4,3) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (4,3)")) gegl_chant_double (d4, _("(4,4) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (4,4)")) gegl_chant_double (d5, _("(4,5) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (4,5)")) gegl_chant_double (e1, _("(5,1) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (5,1)")) gegl_chant_double (e2, _("(5,2) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (5,2)")) gegl_chant_double (e3, _("(5,3) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (5,3)")) gegl_chant_double (e4, _("(5,4) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (5,4)")) gegl_chant_double (e5, _("(5,5) "), -G_MAXINT, G_MAXINT, 0.0, _("Value of the element in position (5,5)")) gegl_chant_double (div, _("Divisor: "), -G_MAXINT, G_MAXINT, 1.0, _("The value of the divisor")) gegl_chant_double (off, _("Offset: "), -1.0, 1.0, 0.0, _("The value of the offset")) gegl_chant_boolean (norm, _("Normalize"), TRUE, _("Normalize or not")) gegl_chant_boolean (red, _("Red"), TRUE, _("Red channel")) gegl_chant_boolean (green, _("Green"), TRUE, _("Green channel")) gegl_chant_boolean (blue, _("Blue"), TRUE, _("Blue channel")) gegl_chant_boolean (alpha, _("Alpha"), TRUE, _("Alpha channel")) gegl_chant_boolean (weight, _("Alpha-weighting"), TRUE, _("Alpha weighting")) gegl_chant_string (border, _("Border"), "extend", _("Type of border to choose." "Choices are extend, wrap, crop." "Default is extend")) #else #define GEGL_CHANT_TYPE_AREA_FILTER #define GEGL_CHANT_C_FILE "convolution-matrix.c" #include "gegl-chant.h" #include #include #define RESPONSE_RESET 1 #define BIG_MATRIX /* toggle for 11x11 matrix code experimental*/ #undef BIG_MATRIX #ifndef BIG_MATRIX #define MATRIX_SIZE (5) #else #define MATRIX_SIZE (11) #endif #define HALF_WINDOW (MATRIX_SIZE/2) #define MATRIX_CELLS (MATRIX_SIZE*MATRIX_SIZE) #define DEST_ROWS (MATRIX_SIZE/2 + 1) #define CHANNELS (5) #define BORDER_MODES (3) static void prepare (GeglOperation *operation) { GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation); op_area->left = op_area->right = op_area->top = op_area->bottom = HALF_WINDOW; gegl_operation_set_format (operation, "output", babl_format ("RGBA float")); } static void make_matrix (GeglChantO *o, gdouble **matrix) { matrix[0][0] = o->a1; matrix[0][1] = o->a2; matrix[0][2] = o->a3; matrix[0][3] = o->a4; matrix[0][4] = o->a5; matrix[1][0] = o->b1; matrix[1][1] = o->b2; matrix[1][2] = o->b3; matrix[1][3] = o->b4; matrix[1][4] = o->b5; matrix[2][0] = o->c1; matrix[2][1] = o->c2; matrix[2][2] = o->c3; matrix[2][3] = o->c4; matrix[2][4] = o->c5; matrix[3][0] = o->d1; matrix[3][1] = o->d2; matrix[3][2] = o->d3; matrix[3][3] = o->d4; matrix[3][4] = o->d5; matrix[4][0] = o->e1; matrix[4][1] = o->e2; matrix[4][2] = o->e3; matrix[4][3] = o->e4; matrix[4][4] = o->e5; } static void normalize_o (GeglChantO *o, gdouble **matrix) { gint x, y; gboolean valid = FALSE; gfloat sum = 0.0; for (y = 0; y < MATRIX_SIZE; y++) for (x = 0; x < MATRIX_SIZE; x++) { sum += matrix[x][y]; if (matrix[x][y] != 0.0) valid = TRUE; } if (sum > 0) { o->off = 0.0; o->div = sum; } else if (sum < 0) { o->off = 1.0; o->div = -sum; } else { o->off = 0.5; o->div = 1; } } static void convolve_pixel(gfloat *src_buf, gfloat *dst_buf, const GeglRectangle *result, const GeglRectangle *extended, const GeglRectangle *boundary, gdouble **matrix, GeglChantO *o, GeglBuffer *input, gint xx, gint yy, gdouble matrixsum) { gint i, x, y, temp, s_x, s_y; gdouble sum; gfloat color[4]; gint d_offset, s_offset; gint half; gdouble alphasum = 0.0; s_x = 0; s_y = 0; half = (MATRIX_SIZE / 2) + (MATRIX_SIZE % 2); d_offset = ((yy - result->y)*result->width * 4) + (xx - result->x) * 4; s_offset = (yy - result->y + HALF_WINDOW) * extended->width * 4 + (xx - result->x + HALF_WINDOW) * 4; for (i=0; i < 4; i++) { sum = 0.0; if ((i==0 && o->red) || (i==1 && o->blue) || (i==2 && o->green) || (i==3 && o->alpha)) { for (x=0;x < MATRIX_SIZE; x++) for (y=0; y < MATRIX_SIZE; y++) { if (!strcmp(o->border,"wrap")) { s_x = fmod (x+xx, boundary->width); while (s_x < 0) s_x +=boundary->width; s_y = fmod (y+yy, boundary->height); while (s_y < 0) s_y +=boundary->width; } else if (!strcmp(o->border,"extend")) { s_x = CLAMP (x+xx, 0, boundary->width); s_y = CLAMP (y+yy, 0, boundary->height); } temp = (s_y - extended->y) * extended->width * 4 + (s_x - extended->x) * 4; if ((s_x >= extended->x && (s_x < extended->x + extended->width)) && (s_y >=extended->y && (s_y < extended->y + extended->height))) { if (i!=3 && o->weight) sum += matrix[x][y] * src_buf[temp + i] * src_buf[temp + 3]; else sum += matrix[x][y] * src_buf[temp + i]; if (i==3) alphasum += fabs (matrix[x][y] * src_buf[temp+i]); } else { gfloat temp_color[4]; gegl_buffer_sample (input, s_x, s_y, NULL, temp_color, babl_format ("RGBA float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (i!=3 && o->weight) sum += matrix[x][y] * temp_color[i] * temp_color[3]; else sum += matrix[x][y] * temp_color[i]; if (i==3) alphasum += fabs (matrix[x][y] * temp_color[i]); } } sum = sum / o->div; if (i==3 && o->weight) { if (alphasum != 0) sum = sum * matrixsum / alphasum; else sum = 0.0; } sum += o->off; color[i] = sum; } else color[i] = src_buf[s_offset + i]; } for (i=0; i < 4; i++) dst_buf[d_offset + i] = color[i]; } static GeglRectangle get_effective_area (GeglOperation *operation) { GeglRectangle result = {0,0,0,0}; GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input"); gegl_rectangle_copy(&result, in_rect); return result; } static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation); GeglRectangle rect; GeglRectangle boundary = get_effective_area (operation); gfloat *src_buf; gfloat *dst_buf; gdouble **matrix; gchar *type; gint x, y; gdouble matrixsum = 0.0; type = "RGBA float"; matrix = g_new0 (gdouble*, MATRIX_SIZE); for (x=0; x < MATRIX_SIZE ;x++) matrix[x] = g_new0 (gdouble, MATRIX_SIZE); make_matrix (o, matrix); if (o->norm) normalize_o (o, matrix); for (x=0; x < MATRIX_SIZE; x++) for (y=0; y < MATRIX_SIZE; y++) matrixsum += fabs (matrix[x][y]); 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; src_buf = g_new0 (gfloat, rect.width * rect.height * 4); dst_buf = g_new0 (gfloat, result->width * result->height * 4); gegl_buffer_get (input, &rect, 1.0, babl_format (type), src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); /*fill src_buf with wrap pixels if it is the case*/ if (o->div != 0) { for (y=result->y; y < result->height + result->y; y++) for (x=result->x; x < result->width + result->x; x++) convolve_pixel (src_buf, dst_buf, result, &rect, &boundary, matrix, o, input, x, y, matrixsum); gegl_buffer_set (output, result, 0, babl_format (type), dst_buf, GEGL_AUTO_ROWSTRIDE); } else gegl_buffer_set (output, &rect, 0, babl_format (type), src_buf, GEGL_AUTO_ROWSTRIDE); g_free (src_buf); g_free (dst_buf); return TRUE; } static GeglRectangle get_bounding_box (GeglOperation *operation) { GeglRectangle result = {0,0,0,0}; GeglRectangle *in_rect; in_rect = gegl_operation_source_get_bounding_box (operation, "input"); if (!in_rect) return result; return *in_rect; } static GeglRectangle get_required_for_output (GeglOperation *operation, const gchar *input_pad, const GeglRectangle *roi) { return get_bounding_box (operation); } static void gegl_chant_class_init (GeglChantClass *klass) { 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; operation_class->get_bounding_box = get_bounding_box; operation_class->get_required_for_output = get_required_for_output; gegl_operation_class_set_keys (operation_class, "categories" , "generic", "name" , "gegl:convolution-matrix", "description" , _("Creates image by manually set convolution matrix"), NULL); } #endif