1 /* This file is an image processing operation for GEGL 2 * 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. 7 * 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. 12 * 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/>. 15 * This program is free software: you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 3 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program. If not, see <https://www.gnu.org/licenses/>. 27 * 28 * 2002, 2014, 2016 (c) Øyvind Kolås pippin@gimp.org 29 */ 30 31 #include "config.h" 32 #include <glib/gi18n-lib.h> 33 34 #ifdef GEGL_PROPERTIES 35 36 property_color (from_0, _("From 0"), "black") 37 property_color (to_0, _("To 0"), "black") 38 property_double (weight_0, _("weight 0"), 100.0) 39 ui_range (0.0, 220.0) 40 property_color (from_1, _("From 1"), "black") 41 property_color (to_1, _("To 1"), "black") 42 property_double (weight_1, _("weight 1"), 100.0) 43 ui_range (0.0, 220.0) 44 property_color (from_2, _("From 2"), "black") 45 property_color (to_2, _("To 2"), "black") 46 property_double (weight_2, _("weight 2"), 100.0) 47 ui_range (0.0, 220.0) 48 property_color (from_3, _("From 3"), "black") 49 property_color (to_3, _("To 3"), "black") 50 property_double (weight_3, _("weight 3"), 100.0) 51 ui_range (0.0, 220.0) 52 property_color (from_4, _("From 4"), "black") 53 property_color (to_4, _("To 4"), "black") 54 property_double (weight_4, _("weight 4"), 100.0) 55 ui_range (0.0, 220.0) 56 property_color (from_5, _("From 5"), "black") 57 property_color (to_5, _("To 5"), "black") 58 property_double (weight_5, _("weight 5"), 100.0) 59 ui_range (0.0, 220.0) 60 property_color (from_6, _("From 6"), "black") 61 property_color (to_6, _("To 6"), "black") 62 property_double (weight_6, _("weight 6"), 100.0) 63 ui_range (0.0, 220.0) 64 property_color (from_7, _("From 7"), "black") 65 property_color (to_7, _("To 7"), "black") 66 property_double (weight_7, _("weight 7"), 100.0) 67 ui_range (0.0, 220.0) 68 property_double (weight, _("global weight scale"), 1.0) 69 ui_range (0.0, 1.0) 70 property_double (amount, _("amount"), 1.0) 71 ui_range (0.0, 1.0) 72 73 #else 74 75 #define GEGL_OP_POINT_FILTER 76 #define GEGL_OP_NAME color_warp 77 #define GEGL_OP_C_SOURCE color-warp.c 78 79 #include "gegl-op.h" 80 81 #define MAX_PAIRS 64 82 83 typedef struct CoordPair { 84 float a[3]; 85 float b[3]; 86 float weight; 87 } CoordPair; 88 89 typedef struct CoordWarp { 90 CoordPair pair[MAX_PAIRS]; 91 int count; 92 } CoordWarp; 93 94 static CoordWarp *cw_new (void) 95 { 96 CoordWarp *cw; 97 cw = g_new0 (CoordWarp, 1); 98 return cw; 99 } 100 101 static void cw_clear_pairs (CoordWarp *cw) 102 { 103 cw->count = 0; 104 } 105 106 static void cw_destroy (CoordWarp *cw) 107 { 108 g_free (cw); 109 } 110 111 static void cw_add_pair (CoordWarp *cw, 112 const float *coord_a, 113 const float *coord_b, 114 float weight) 115 { 116 int d; 117 if (cw->count +1 >= MAX_PAIRS) 118 return; 119 for (d = 0; d < 3; d++) 120 cw->pair[cw->count].a[d] = coord_a[d]; 121 for (d = 0; d < 3; d++) 122 cw->pair[cw->count].b[d] = coord_b[d]; 123 cw->pair[cw->count].weight = weight; 124 cw->count++; 125 } 126 127 static inline float sq_dist (CoordWarp *cw, const float *coord_a, const float *coord_b) 128 { 129 int d; 130 float sq_sum = 0; 131 for (d = 0; d < 3; d++) 132 sq_sum += (coord_b[d] - coord_a[d]) * (coord_b[d] - coord_a[d]); 133 return sq_sum; 134 } 135 136 static inline float calc_weight (float dist, float lowest_dist, float coord_weight) 137 { 138 float influence = coord_weight; 139 return expf (-dist / influence); 140 } 141 142 static void cw_map (CoordWarp *cw, 143 const float *coord_a, 144 float *coord_b) 145 { 146 int i; 147 int lowest_dist_no = 0; 148 double lowest_dist = 12345678900000.0; 149 double sum_wc = 0.0; 150 float warp[3] = {0,}; 151 152 for (i = 0; i < cw->count; i++) 153 { 154 float sqd = sq_dist (cw, coord_a, cw->pair[i].a); 155 if (sqd < lowest_dist) 156 { 157 lowest_dist = sqd; 158 lowest_dist_no = i; 159 } 160 } 161 162 for (i = 0; i < cw->count; i++) 163 { 164 float sqd = sq_dist (cw, coord_a, cw->pair[i].a); 165 sum_wc += lowest_dist / sqd; 166 } 167 168 if (lowest_dist > 0.0) 169 { 170 for (i = 0; i < cw->count; i++) 171 { 172 int d; 173 float sqd = sq_dist (cw, coord_a, cw->pair[i].a); 174 float weight = calc_weight (sqd, lowest_dist, cw->pair[i].weight); 175 weight = weight / sum_wc; 176 177 for (d = 0; d < 3; d++) 178 warp[d] += (cw->pair[i].a[d] - cw->pair[i].b[d]) * weight; 179 } 180 } 181 else 182 { 183 int d; 184 for (d = 0; d < 3; d++) 185 warp[d] = (cw->pair[lowest_dist_no].a[d] - cw->pair[lowest_dist_no].b[d]); 186 } 187 188 { 189 int d; 190 for (d = 0; d < 3; d++) 191 coord_b[d] = coord_a[d] - warp[d]; 192 } 193 } 194 195 static void maybe_add_pair (CoordWarp *cw, 196 GeglColor *colorA, 197 GeglColor *colorB, 198 float weight, 199 const Babl *colorformat) 200 { 201 gfloat from[4]; 202 gfloat to[4]; 203 gegl_color_get_pixel (colorA, colorformat, from); 204 gegl_color_get_pixel (colorB, colorformat, to); 205 if (from[0] == 0.0 && 206 from[1] == 0.0 && 207 from[2] == 0.0 && 208 to[0] == 0.0 && 209 to[1] == 0.0 && 210 to[2] == 0.0) 211 { 212 } 213 else 214 { 215 cw_add_pair (cw, from, to, weight); 216 } 217 } 218 219 static void prepare (GeglOperation *operation) 220 { 221 CoordWarp *cw; 222 GeglProperties *o = GEGL_PROPERTIES (operation); 223 const Babl *space = gegl_operation_get_source_space (operation, "input"); 224 const Babl *format = babl_format_with_space ("CIE Lab float", space); 225 gegl_operation_set_format (operation, "input", format); 226 gegl_operation_set_format (operation, "output", format); 227 228 if (o->user_data == NULL) 229 o->user_data = cw_new (); 230 cw = o->user_data; 231 232 cw_clear_pairs (cw); 233 maybe_add_pair (cw, o->from_0, o->to_0, o->weight * o->weight_0, format); 234 maybe_add_pair (cw, o->from_1, o->to_1, o->weight * o->weight_1, format); 235 maybe_add_pair (cw, o->from_2, o->to_2, o->weight * o->weight_2, format); 236 maybe_add_pair (cw, o->from_3, o->to_3, o->weight * o->weight_3, format); 237 maybe_add_pair (cw, o->from_4, o->to_4, o->weight * o->weight_4, format); 238 maybe_add_pair (cw, o->from_5, o->to_5, o->weight * o->weight_5, format); 239 maybe_add_pair (cw, o->from_6, o->to_6, o->weight * o->weight_6, format); 240 maybe_add_pair (cw, o->from_7, o->to_7, o->weight * o->weight_7, format); 241 } 242 243 static void 244 finalize (GObject *object) 245 { 246 GeglProperties *o = GEGL_PROPERTIES (object); 247 248 if (o->user_data) 249 { 250 cw_destroy (o->user_data); 251 o->user_data = NULL; 252 } 253 254 G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object); 255 } 256 257 static gboolean 258 process (GeglOperation *op, 259 void *in_buf, 260 void *out_buf, 261 glong n_pixels, 262 const GeglRectangle *roi, 263 gint level) 264 { 265 GeglProperties *o = GEGL_PROPERTIES (op); 266 gfloat *in_pixel = in_buf; 267 gfloat *out_pixel = out_buf; 268 CoordWarp *cw = o->user_data; 269 float amount = o->amount; 270 271 in_pixel = in_buf; 272 out_pixel = out_buf; 273 274 while (n_pixels--) 275 { 276 if (amount == 1.0) 277 { 278 cw_map (cw, in_pixel, out_pixel); 279 } 280 else 281 { 282 int d; 283 float res[4]; 284 cw_map (cw, in_pixel, res); 285 for (d = 0; d < 3; d++) 286 out_pixel[d] = in_pixel[d] * (1.0-amount) + res[d] * amount; 287 } 288 in_pixel += 3; 289 out_pixel += 3; 290 } 291 292 return TRUE; 293 } 294 295 static void 296 gegl_op_class_init (GeglOpClass *klass) 297 { 298 GObjectClass *object_class; 299 GeglOperationClass *operation_class; 300 GeglOperationPointFilterClass *point_filter_class; 301 gchar *composition = 302 "<?xml version='1.0' encoding='UTF-8'?>" 303 "<gegl>" 304 " <node operation='gegl:crop' width='200' height='200'/>" 305 " <node operation='gegl:over'>" 306 " <node operation='gegl:color-warp'>" 307 " <params>" 308 " <param name='from-0'>rgb(1.0000, 1.0000, 1.0000)</param>" 309 " <param name='to-0'>rgb(0.9900, 0.4500, 0.8500)</param>" 310 " <param name='weight-0'>100.00</param>" 311 " <param name='from-1'>rgb(0.0000, 0.0000, 0.0000)</param>" 312 " <param name='to-1'>rgb(0.5000, 0.0000, 0.5000)</param>" 313 " <param name='weight-1'>33.33</param>" 314 " <param name='weight'>1.00</param>" 315 " <param name='amount'>0.50</param>" 316 " </params>" 317 " </node>" 318 " <node operation='gegl:load' path='standard-input.png'/>" 319 " </node>" 320 " <node operation='gegl:checkerboard'>" 321 " <params>" 322 " <param name='color1'>rgb(0.25,0.25,0.25)</param>" 323 " <param name='color2'>rgb(0.75,0.75,0.75)</param>" 324 " </params>" 325 " </node>" 326 "</gegl>"; 327 328 object_class = G_OBJECT_CLASS (klass); 329 operation_class = GEGL_OPERATION_CLASS (klass); 330 point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass); 331 object_class->finalize = finalize; 332 operation_class->prepare = prepare; 333 point_filter_class->process = process; 334 335 gegl_operation_class_set_keys (operation_class, 336 "name", "gegl:color-warp", 337 "title", _("Color warp"), 338 "categories", "color", 339 "reference-composition", composition, 340 "reference-hash", "637569c3382fc061ee45513eaebf22d6", 341 "description", _("Warps the colors of an image between colors with weighted distortion factors, color pairs which are black to black get ignored when constructing the mapping."), 342 NULL); 343 } 344 345 #endif 346