1 /* This file is an image processing operation for GEGL
3 * GEGL is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
8 * GEGL is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
16 * Copyright (C) 1997 Lauri Alanko <la@iki.fi>
17 * Copyright 2011 Robert Sasu (sasu.robert@gmail.com)
21 #include <glib/gi18n-lib.h>
23 #ifdef GEGL_PROPERTIES
25 property_double (a1
, _("(1,1)"), 0.0)
26 property_double (a2
, _("(1,2)"), 0.0)
27 property_double (a3
, _("(1,3)"), 0.0)
28 property_double (a4
, _("(1,4)"), 0.0)
29 property_double (a5
, _("(1,5)"), 0.0)
30 property_double (b1
, _("(2,1)"), 0.0)
31 property_double (b2
, _("(2,2)"), 0.0)
32 property_double (b3
, _("(2,3)"), 0.0)
33 property_double (b4
, _("(2,4)"), 0.0)
34 property_double (b5
, _("(2,5)"), 0.0)
35 property_double (c1
, _("(3,1)"), 0.0)
36 property_double (c2
, _("(3,2)"), 0.0)
37 property_double (c3
, _("(3,3)"), 1.0)
38 property_double (c4
, _("(3,4)"), 0.0)
39 property_double (c5
, _("(3,5)"), 0.0)
40 property_double (d1
, _("(4,1)"), 0.0)
41 property_double (d2
, _("(4,2)"), 0.0)
42 property_double (d3
, _("(4,3)"), 0.0)
43 property_double (d4
, _("(4,4)"), 0.0)
44 property_double (d5
, _("(4,5)"), 0.0)
45 property_double (e1
, _("(5,1)"), 0.0)
46 property_double (e2
, _("(5,2)"), 0.0)
47 property_double (e3
, _("(5,3)"), 0.0)
48 property_double (e4
, _("(5,4)"), 0.0)
49 property_double (e5
, _("(5,5)"), 0.0)
51 property_double (divisor
, _("Divisor"), 1.0)
52 ui_range (-1000.0, 1000.0)
53 ui_meta ("sensitive", "! normalize")
55 property_double (offset
, _("Offset"), 0.0)
56 value_range (-1.0, 1.0)
57 ui_meta ("sensitive", "! normalize")
59 property_boolean (red
, _("Red channel"), TRUE
)
60 property_boolean (green
, _("Green channel"), TRUE
)
61 property_boolean (blue
, _("Blue channel"), TRUE
)
62 property_boolean (alpha
, _("Alpha channel"), TRUE
)
64 property_boolean (normalize
, _("Normalize"), TRUE
)
65 property_boolean (alpha_weight
, _("Alpha-weighting"), TRUE
)
67 property_enum (border
, _("Border"),
68 GeglAbyssPolicy
, gegl_abyss_policy
,
73 #define GEGL_OP_AREA_FILTER
74 #define GEGL_OP_NAME convolution_matrix
75 #define GEGL_OP_C_SOURCE convolution-matrix.c
80 #define MAX_MATRIX_SIZE 5
83 enough_with_3x3 (GeglProperties
*o
)
108 prepare (GeglOperation
*operation
)
110 const Babl
*space
= gegl_operation_get_source_space (operation
, "input");
112 GeglOperationAreaFilter
*op_area
= GEGL_OPERATION_AREA_FILTER (operation
);
114 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
115 if (enough_with_3x3 (o
))
116 op_area
->left
= op_area
->right
= op_area
->top
= op_area
->bottom
= 1; /* 3 */
118 op_area
->left
= op_area
->right
= op_area
->top
= op_area
->bottom
= 2; /* 5 */
120 gegl_operation_set_format (operation
, "output",
121 babl_format_with_space ("RGBA float", space
));
125 make_matrix (GeglProperties
*o
,
126 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
129 if (matrix_size
== 3)
131 matrix
[0][0] = o
->b2
;
132 matrix
[0][1] = o
->b3
;
133 matrix
[0][2] = o
->b4
;
135 matrix
[1][0] = o
->c2
;
136 matrix
[1][1] = o
->c3
;
137 matrix
[1][2] = o
->c4
;
139 matrix
[2][0] = o
->d2
;
140 matrix
[2][1] = o
->d3
;
141 matrix
[2][2] = o
->d4
;
145 matrix
[0][0] = o
->a1
;
146 matrix
[0][1] = o
->a2
;
147 matrix
[0][2] = o
->a3
;
148 matrix
[0][3] = o
->a4
;
149 matrix
[0][4] = o
->a5
;
151 matrix
[1][0] = o
->b1
;
152 matrix
[1][1] = o
->b2
;
153 matrix
[1][2] = o
->b3
;
154 matrix
[1][3] = o
->b4
;
155 matrix
[1][4] = o
->b5
;
157 matrix
[2][0] = o
->c1
;
158 matrix
[2][1] = o
->c2
;
159 matrix
[2][2] = o
->c3
;
160 matrix
[2][3] = o
->c4
;
161 matrix
[2][4] = o
->c5
;
163 matrix
[3][0] = o
->d1
;
164 matrix
[3][1] = o
->d2
;
165 matrix
[3][2] = o
->d3
;
166 matrix
[3][3] = o
->d4
;
167 matrix
[3][4] = o
->d5
;
169 matrix
[4][0] = o
->e1
;
170 matrix
[4][1] = o
->e2
;
171 matrix
[4][2] = o
->e3
;
172 matrix
[4][3] = o
->e4
;
173 matrix
[4][4] = o
->e5
;
178 normalize_div_off (gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
184 gboolean valid
= FALSE
;
187 for (y
= 0; y
< matrix_size
; y
++)
188 for (x
= 0; x
< matrix_size
; x
++)
191 if (matrix
[x
][y
] != 0.0)
215 matrix_center_offset (const GeglRectangle
*extended
,
218 return (extended
->width
+ 1) * (matrix_size
/ 2);
222 convolve_pixel_componentwise (GeglProperties
*o
,
225 const GeglRectangle
*result
,
226 const GeglRectangle
*extended
,
227 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
238 gint s_stride
= (extended
->width
- matrix_size
) * 4;
239 for (i
= 0; i
< 4; i
++)
242 gint s_offset
= ss_offset
;
244 if ((i
== 0 && o
->red
) ||
245 (i
== 1 && o
->green
) ||
246 (i
== 2 && o
->blue
) ||
247 (i
== 3 && o
->alpha
))
250 for (y
= 0; y
< matrix_size
; y
++)
252 for (x
= 0; x
< matrix_size
; x
++)
254 sum
+= matrix
[x
][y
] * src_buf
[s_offset
+ i
];
257 s_offset
+= s_stride
;
259 sum
= sum
* inv_divisor
;
261 dst_buf
[d_offset
+ i
] = sum
;
265 s_offset
+= 4 * matrix_center_offset (extended
, matrix_size
);
266 dst_buf
[d_offset
+ i
] = src_buf
[s_offset
+ i
];
272 convolve_pixel (GeglProperties
*o
,
275 const GeglRectangle
*result
,
276 const GeglRectangle
*extended
,
277 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
288 gint s_stride
= (extended
->width
- matrix_size
) * 4;
289 for (i
= 0; i
< 4; i
++)
292 gint s_offset
= ss_offset
;
294 for (y
= 0; y
< matrix_size
; y
++)
296 for (x
= 0; x
< matrix_size
; x
++)
298 sum
+= matrix
[x
][y
] * src_buf
[s_offset
+ i
];
301 s_offset
+= s_stride
;
303 sum
= sum
* inv_divisor
;
305 dst_buf
[d_offset
+ i
] = sum
;
310 convolve_pixel_alpha_weight (GeglProperties
*o
,
313 const GeglRectangle
*result
,
314 const GeglRectangle
*extended
,
315 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
326 gint s_stride
= (extended
->width
- matrix_size
) * 4;
328 for (i
= 0; i
< 3; i
++)
331 gint s_offset
= ss_offset
;
333 for (y
= 0; y
< matrix_size
; y
++)
335 for (x
= 0; x
< matrix_size
; x
++)
337 sum
+= matrix
[x
][y
] * src_buf
[s_offset
+ i
] * src_buf
[s_offset
+ 3];
340 s_offset
+= s_stride
;
342 sum
= sum
* inv_divisor
+ offset
;
343 dst_buf
[d_offset
+ i
] = sum
;
347 gfloat alphasum
= 0.0;
348 gint s_offset
= ss_offset
;
351 for (y
= 0; y
< matrix_size
; y
++)
353 for (x
= 0; x
< matrix_size
; x
++)
355 float val
= matrix
[x
][y
] * src_buf
[s_offset
+ i
];
357 alphasum
+= fabsf (val
);
360 s_offset
+= s_stride
;
365 sum
= sum
* inv_divisor
;
366 sum
= sum
* matrixsum
/ alphasum
+ offset
;
370 dst_buf
[d_offset
+ i
] = sum
;
375 convolve_pixel_alpha_weight_componentwise (GeglProperties
*o
,
378 const GeglRectangle
*result
,
379 const GeglRectangle
*extended
,
380 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
],
391 gint s_stride
= (extended
->width
- matrix_size
) * 4;
393 for (i
= 0; i
< 3; i
++)
396 gint s_offset
= ss_offset
;
398 if ((i
== 0 && o
->red
) ||
399 (i
== 1 && o
->green
) ||
403 for (y
= 0; y
< matrix_size
; y
++)
405 for (x
= 0; x
< matrix_size
; x
++)
407 sum
+= matrix
[x
][y
] * src_buf
[s_offset
+ i
] * src_buf
[s_offset
+ 3];
410 s_offset
+= s_stride
;
412 sum
= sum
* inv_divisor
+ offset
;
416 s_offset
+= 4 * matrix_center_offset (extended
, matrix_size
);
417 sum
= src_buf
[s_offset
+ i
];
419 dst_buf
[d_offset
+ i
] = sum
;
423 gfloat alphasum
= 0.0;
424 gint s_offset
= ss_offset
;
430 for (y
= 0; y
< matrix_size
; y
++)
432 for (x
= 0; x
< matrix_size
; x
++)
434 float val
= matrix
[x
][y
] * src_buf
[s_offset
+ i
];
436 alphasum
+= fabsf (val
);
439 s_offset
+= s_stride
;
445 sum
= sum
* inv_divisor
;
446 sum
= sum
* matrixsum
/ alphasum
+ offset
;
453 s_offset
+= 4 * matrix_center_offset (extended
, matrix_size
);
454 sum
= src_buf
[s_offset
+ i
];
456 dst_buf
[d_offset
+ i
] = sum
;
461 process (GeglOperation
*operation
,
464 const GeglRectangle
*result
,
467 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
468 GeglOperationAreaFilter
*op_area
= GEGL_OPERATION_AREA_FILTER (operation
);
469 const Babl
*format
= gegl_operation_get_format (operation
, "output");
473 gfloat matrix
[MAX_MATRIX_SIZE
][MAX_MATRIX_SIZE
]={{0,}};
474 gfloat matrixsum
= 0.0;
475 gfloat divisor
= o
->divisor
;
476 gfloat offset
= o
->offset
;
479 gint matrix_size
= MAX_MATRIX_SIZE
;
481 if (enough_with_3x3 (o
))
484 make_matrix (o
, matrix
, matrix_size
);
487 normalize_div_off (matrix
, matrix_size
, &divisor
, &offset
);
488 inv_divisor
= 1.0 / divisor
;
490 for (x
= 0; x
< matrix_size
; x
++)
491 for (y
= 0; y
< matrix_size
; y
++)
492 matrixsum
+= fabsf (matrix
[x
][y
]);
494 rect
.x
= result
->x
- op_area
->left
;
495 rect
.width
= result
->width
+ op_area
->left
+ op_area
->right
;
496 rect
.y
= result
->y
- op_area
->top
;
497 rect
.height
= result
->height
+ op_area
->top
+ op_area
->bottom
;
499 src_buf
= g_new (gfloat
, rect
.width
* rect
.height
* 4);
500 dst_buf
= g_new (gfloat
, result
->width
* result
->height
* 4);
502 gegl_buffer_get (input
, &rect
, 1.0, format
, src_buf
,
503 GEGL_AUTO_ROWSTRIDE
, o
->border
);
507 gint ss_offset
= (result
->y
- matrix_size
/2 - rect
.y
) * rect
.width
* 4 +
508 (result
->x
- matrix_size
/2 - rect
.x
) * 4;
512 if (o
->red
== FALSE
|| o
->green
== FALSE
|| o
->blue
== FALSE
|| o
->alpha
== FALSE
)
515 for (y
= result
->y
; y
< result
->height
+ result
->y
; y
++)
517 for (x
= result
->x
; x
< result
->width
+ result
->x
; x
++)
519 convolve_pixel_alpha_weight_componentwise (o
, src_buf
, dst_buf
, result
, &rect
,
520 matrix
, matrix_size
, d_offset
, ss_offset
, x
, y
, matrixsum
, inv_divisor
, offset
);
524 ss_offset
+= (rect
.width
- result
->width
) * 4;
530 for (y
= result
->y
; y
< result
->height
+ result
->y
; y
++)
532 for (x
= result
->x
; x
< result
->width
+ result
->x
; x
++)
534 convolve_pixel_alpha_weight (o
, src_buf
, dst_buf
, result
, &rect
,
535 matrix
, matrix_size
, d_offset
, ss_offset
, x
, y
, matrixsum
, inv_divisor
, offset
);
539 ss_offset
+= (rect
.width
- result
->width
) * 4;
545 if (o
->red
== FALSE
|| o
->green
== FALSE
|| o
->blue
== FALSE
|| o
->alpha
== FALSE
)
548 for (y
= result
->y
; y
< result
->height
+ result
->y
; y
++)
550 for (x
= result
->x
; x
< result
->width
+ result
->x
; x
++)
552 convolve_pixel_componentwise (o
, src_buf
, dst_buf
, result
, &rect
,
553 matrix
, matrix_size
, d_offset
, ss_offset
, x
, y
, matrixsum
, inv_divisor
, offset
);
557 ss_offset
+= (rect
.width
- result
->width
) * 4;
563 for (y
= result
->y
; y
< result
->height
+ result
->y
; y
++)
565 for (x
= result
->x
; x
< result
->width
+ result
->x
; x
++)
567 convolve_pixel (o
, src_buf
, dst_buf
, result
, &rect
,
568 matrix
, matrix_size
, d_offset
, ss_offset
, x
, y
, matrixsum
, inv_divisor
, offset
);
572 ss_offset
+= (rect
.width
- result
->width
) * 4;
576 gegl_buffer_set (output
, result
, 0, format
,
577 dst_buf
, GEGL_AUTO_ROWSTRIDE
);
581 gegl_buffer_set (output
, &rect
, 0, format
,
582 src_buf
, GEGL_AUTO_ROWSTRIDE
);
592 get_bounding_box (GeglOperation
*operation
)
594 GeglRectangle result
= { 0, };
595 GeglRectangle
*in_rect
;
597 in_rect
= gegl_operation_source_get_bounding_box (operation
, "input");
604 static GeglAbyssPolicy
605 get_abyss_policy (GeglOperation
*operation
,
606 const gchar
*input_pad
)
608 GeglProperties
*o
= GEGL_PROPERTIES (operation
);
614 gegl_op_class_init (GeglOpClass
*klass
)
616 GeglOperationClass
*operation_class
;
617 GeglOperationFilterClass
*filter_class
;
618 GeglOperationAreaFilterClass
*area_class
;
620 operation_class
= GEGL_OPERATION_CLASS (klass
);
621 filter_class
= GEGL_OPERATION_FILTER_CLASS (klass
);
622 area_class
= GEGL_OPERATION_AREA_FILTER_CLASS (klass
);
624 area_class
->get_abyss_policy
= get_abyss_policy
;
625 filter_class
->process
= process
;
626 operation_class
->prepare
= prepare
;
627 operation_class
->get_bounding_box
= get_bounding_box
;
629 gegl_operation_class_set_keys (operation_class
,
630 "categories", "generic",
631 "name", "gegl:convolution-matrix",
632 "reference-hash", "22d2d47a2da3d3e7cd402ea9fa1a3a25",
633 "reference-hashB", "4eddc0aaa970a59ee8a813627874cdf3",
634 "title", _("Convolution Matrix"),
635 "description", _("Apply a generic 5x5 convolution matrix"),