1 /* This file is an image processing operation for GEGL 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation; either version 3 of the License, or 6 * (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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * 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 * Tile paper ported to GEGL: 17 * Copyright 2015 Akash Hiremath (akash akya) <akashh246@gmail.com> 18 */ 19 20 #include "config.h" 21 #include <glib/gi18n-lib.h> 22 #include <stdlib.h> 23 24 25 #ifdef GEGL_PROPERTIES 26 27 enum_start (gegl_tile_paper_background_type) 28 enum_value (GEGL_BACKGROUND_TYPE_TRANSPARENT, "transparent", N_("Transparent")) 29 enum_value (GEGL_BACKGROUND_TYPE_INVERT, "invert", N_("Inverted image")) 30 enum_value (GEGL_BACKGROUND_TYPE_IMAGE, "image", N_("Image")) 31 enum_value (GEGL_BACKGROUND_TYPE_COLOR, "color", N_("Color")) 32 enum_end (GeglTilePaperBackgroundType) 33 34 enum_start (gegl_tile_paper_fractional_type) 35 enum_value (GEGL_FRACTIONAL_TYPE_BACKGROUND, "background", N_("Background")) 36 enum_value (GEGL_FRACTIONAL_TYPE_IGNORE, "ignore", N_("Ignore")) 37 enum_value (GEGL_FRACTIONAL_TYPE_FORCE, "force", N_("Force")) 38 enum_end (GeglTilePaperFractionalType) 39 40 property_int (tile_width, _("Tile Width"), 155) 41 description (_("Width of the tile")) 42 value_range (1, G_MAXINT) 43 ui_range (1, 1500) 44 ui_meta ("unit", "pixel-distance") 45 ui_meta ("axis", "x") 46 47 property_int (tile_height, _("Tile Height"), 56) 48 description (_("Height of the tile")) 49 value_range (1, G_MAXINT) 50 ui_range (1, 1500) 51 ui_meta ("unit", "pixel-distance") 52 ui_meta ("axis", "y") 53 54 property_double (move_rate, _("Move rate"), 25.0) 55 description (_("Move rate")) 56 value_range (1.0, 100.0) 57 ui_range (1.0, 100.0) 58 ui_meta ("unit", "percent") 59 60 property_boolean (wrap_around, _("Wrap around"), FALSE) 61 description (_("Wrap the fractional tiles")) 62 63 property_enum (fractional_type, _("Fractional type"), 64 GeglTilePaperFractionalType, gegl_tile_paper_fractional_type, 65 GEGL_FRACTIONAL_TYPE_FORCE) 66 description (_("Fractional Type")) 67 68 property_boolean (centering, _("Centering"), TRUE) 69 description (_("Centering of the tiles")) 70 71 property_enum (background_type, _("Background type"), 72 GeglTilePaperBackgroundType, gegl_tile_paper_background_type, 73 GEGL_BACKGROUND_TYPE_INVERT) 74 description (_("Background type")) 75 76 property_color (bg_color, _("Background color"), "rgba(0.0, 0.0, 0.0, 1.0)") 77 description (_("The tiles' background color")) 78 ui_meta ("role", "color-primary") 79 ui_meta ("visible", "background-type {color}") 80 81 property_seed (seed, _("Random seed"), rand) 82 83 #else 84 85 #define GEGL_OP_FILTER 86 #define GEGL_OP_NAME tile_paper 87 #define GEGL_OP_C_SOURCE tile-paper.c 88 89 #include "gegl-op.h" 90 91 92 typedef struct _Tile 93 { 94 guint x; 95 guint y; 96 gint z; 97 guint width; 98 guint height; 99 gint move_x; 100 gint move_y; 101 } Tile; 102 103 104 static gint 105 tile_compare (const void *x, 106 const void *y) 107 { 108 return ((Tile *) x)->z - ((Tile *) y)->z; 109 } 110 111 static inline void 112 random_move (gint tile_x, 113 gint tile_y, 114 gint *x, 115 gint *y, 116 gint max, 117 GeglProperties *o) 118 { 119 gdouble angle = gegl_random_float_range (o->rand, tile_x, tile_y, 0, 1, 120 0.0, 1.0) * G_PI; 121 gdouble radius = gegl_random_float_range (o->rand, tile_x, tile_y, 0, 2, 122 0.0, 1.0) * (gdouble) max; 123 124 *x = (gint) (radius * cos (angle)); 125 *y = (gint) (radius * sin (angle)); 126 } 127 128 static void 129 randomize_tiles (GeglProperties *o, 130 const GeglRectangle *rect, 131 gint division_x, 132 gint division_y, 133 gint offset_x, 134 gint offset_y, 135 gint n_tiles, 136 Tile *tiles) 137 { 138 Tile *t = tiles; 139 gint move_max_pixels = o->move_rate * o->tile_width / 100; 140 gint x; 141 gint y; 142 143 for (y = 0; y < division_y; y++) 144 { 145 gint srcy = offset_y + o->tile_height * y; 146 147 for (x = 0; x < division_x; x++, t++) 148 { 149 gint srcx = offset_x + o->tile_width * x; 150 151 if (srcx < 0) 152 { 153 t->x = 0; 154 t->width = srcx + o->tile_width; 155 } 156 else if (srcx + o->tile_width < rect->width) 157 { 158 t->x = srcx; 159 t->width = o->tile_width; 160 } 161 else 162 { 163 t->x = srcx; 164 t->width = rect->width - srcx; 165 } 166 167 if (srcy < 0) 168 { 169 t->y = 0; 170 t->height = srcy + o->tile_height; 171 } 172 else if (srcy + o->tile_height < rect->height) 173 { 174 t->y = srcy; 175 t->height = o->tile_height; 176 } 177 else 178 { 179 t->y = srcy; 180 t->height = rect->height - srcy; 181 } 182 183 t->z = gegl_random_int (o->rand, x, y, 0, 0); 184 random_move (x, y, &t->move_x, &t->move_y, move_max_pixels, o); 185 } 186 } 187 188 qsort (tiles, n_tiles, sizeof (*tiles), tile_compare); 189 } 190 191 static void 192 draw_tiles (GeglProperties *o, 193 const GeglRectangle *rect, 194 GeglBuffer *input, 195 GeglBuffer *output, 196 gint num_of_tiles, 197 Tile *tiles) 198 { 199 const Babl *format; 200 gfloat *tile_buffer; 201 Tile *t; 202 gint i; 203 204 format = babl_format ("RGBA float"); 205 tile_buffer = g_new0 (gfloat, 4 * o->tile_width * o->tile_height); 206 207 if (o->wrap_around) 208 { 209 for (t = tiles, i = 0; i < num_of_tiles; i++, t++) 210 { 211 GeglRectangle tile_rect = { t->x, t->y, t->width, t->height }; 212 213 gegl_buffer_get (input, &tile_rect, 1.0, format, tile_buffer, 214 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); 215 216 tile_rect.x += t->move_x; 217 tile_rect.y += t->move_y; 218 219 gegl_buffer_set (output, &tile_rect, 0, format, 220 tile_buffer, GEGL_AUTO_ROWSTRIDE); 221 222 if (tile_rect.x < 0 || tile_rect.x + tile_rect.width > rect->width || 223 tile_rect.y < 0 || tile_rect.y + tile_rect.height > rect->height) 224 { 225 if (tile_rect.x < 0) 226 { 227 tile_rect.x = rect->width + tile_rect.x; 228 } 229 else if (tile_rect.x+tile_rect.width > rect->width) 230 { 231 tile_rect.x -= rect->width; 232 } 233 234 if (tile_rect.y < 0) 235 { 236 tile_rect.y = rect->height + tile_rect.y; 237 } 238 else if (tile_rect.y + tile_rect.height > rect->height) 239 { 240 tile_rect.y -= rect->height; 241 } 242 243 gegl_buffer_set (output, &tile_rect, 0, format, 244 tile_buffer, GEGL_AUTO_ROWSTRIDE); 245 } 246 } 247 } 248 else 249 { 250 for (t = tiles, i = 0; i < num_of_tiles; i++, t++) 251 { 252 GeglRectangle tile_rect = { t->x, t->y, t->width, t->height }; 253 254 gegl_buffer_get (input, &tile_rect, 1.0, format, tile_buffer, 255 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); 256 257 tile_rect.x += t->move_x; 258 tile_rect.y += t->move_y; 259 260 gegl_buffer_set (output, &tile_rect, 0, format, 261 tile_buffer, GEGL_AUTO_ROWSTRIDE); 262 } 263 } 264 265 g_free (tile_buffer); 266 } 267 268 static void 269 set_background (GeglProperties *o, 270 const GeglRectangle *rect, 271 GeglBuffer *input, 272 GeglBuffer *output, 273 gint division_x, 274 gint division_y, 275 gint offset_x, 276 gint offset_y) 277 { 278 const Babl *format = babl_format ("RGBA float"); 279 280 if (o->background_type == GEGL_BACKGROUND_TYPE_TRANSPARENT) 281 { 282 GeglColor *color = gegl_color_new ("rgba(0.0,0.0,0.0,0.0)"); 283 gegl_buffer_set_color (output, rect, color); 284 g_object_unref (color); 285 } 286 else if (o->background_type == GEGL_BACKGROUND_TYPE_COLOR) 287 { 288 gegl_buffer_set_color (output, rect, o->bg_color); 289 } 290 else if (o->background_type == GEGL_BACKGROUND_TYPE_IMAGE) 291 { 292 gegl_buffer_copy (input, NULL, GEGL_ABYSS_NONE, output, NULL); 293 } 294 else 295 { 296 /* GEGL_BACKGROUND_TYPE_INVERT */ 297 298 GeglBufferIterator *iter; 299 GeglRectangle clear = *rect; 300 301 if (o->fractional_type == GEGL_FRACTIONAL_TYPE_IGNORE) 302 { 303 clear.x = offset_x; 304 clear.y = offset_y; 305 clear.width = o->tile_width * (rect->width / o->tile_width); 306 clear.height = o->tile_height * (rect->height / o->tile_height); 307 } 308 309 iter = gegl_buffer_iterator_new (input, &clear, 0, format, 310 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); 311 gegl_buffer_iterator_add (iter, output, &clear, 0, format, 312 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); 313 314 while (gegl_buffer_iterator_next (iter)) 315 { 316 gfloat *src = iter->items[0].data; 317 gfloat *dst = iter->items[1].data; 318 glong n_pixels = iter->length; 319 320 while (n_pixels--) 321 { 322 *dst++ = 1.f - *src++; 323 *dst++ = 1.f - *src++; 324 *dst++ = 1.f - *src++; 325 *dst++ = *src++; 326 } 327 } 328 } 329 } 330 331 static gboolean 332 process (GeglOperation *operation, 333 GeglBuffer *input, 334 GeglBuffer *output, 335 const GeglRectangle *result, 336 gint level) 337 { 338 GeglProperties *o = GEGL_PROPERTIES (operation); 339 Tile *tiles; 340 gint offset_x; 341 gint offset_y; 342 gint division_x; 343 gint division_y; 344 gint n_tiles; 345 346 division_x = result->width / o->tile_width; 347 division_y = result->height / o->tile_height; 348 349 offset_x = 0; 350 offset_y = 0; 351 352 if (o->fractional_type == GEGL_FRACTIONAL_TYPE_FORCE) 353 { 354 if (o->centering) 355 { 356 if (1 < result->width % o->tile_width) 357 { 358 division_x++; 359 offset_x = (result->width % o->tile_width) / 2 - o->tile_width; 360 } 361 362 if (1 < result->height % o->tile_height) 363 { 364 division_y++; 365 offset_y = (result->height % o->tile_height) / 2 - o->tile_height; 366 } 367 } 368 } 369 else 370 { 371 if (o->centering) 372 { 373 offset_x = (result->width % o->tile_width) / 2; 374 offset_y = (result->height % o->tile_height) / 2; 375 } 376 } 377 378 n_tiles = division_x * division_y; 379 tiles = g_new (Tile, n_tiles); 380 381 randomize_tiles (o, result, division_x, division_y, 382 offset_x, offset_y, n_tiles, tiles); 383 384 set_background (o, result, input, output, division_x, division_y, 385 offset_x, offset_y); 386 387 draw_tiles (o, result, input, output, n_tiles, tiles); 388 389 g_free (tiles); 390 391 return TRUE; 392 } 393 394 static GeglRectangle 395 get_cached_region (GeglOperation *operation, 396 const GeglRectangle *roi) 397 { 398 const GeglRectangle *in_rect = 399 gegl_operation_source_get_bounding_box (operation, "input"); 400 401 if (! in_rect || gegl_rectangle_is_infinite_plane (in_rect)) 402 return *roi; 403 404 return *in_rect; 405 } 406 407 /* Compute the input rectangle required to compute the specified 408 * region of interest (roi). 409 */ 410 static GeglRectangle 411 get_required_for_output (GeglOperation *operation, 412 const gchar *input_pad, 413 const GeglRectangle *roi) 414 { 415 return get_cached_region (operation, roi); 416 } 417 418 static gboolean 419 operation_process (GeglOperation *operation, 420 GeglOperationContext *context, 421 const gchar *output_prop, 422 const GeglRectangle *result, 423 gint level) 424 { 425 GeglOperationClass *operation_class; 426 427 const GeglRectangle *in_rect = 428 gegl_operation_source_get_bounding_box (operation, "input"); 429 430 if (in_rect && gegl_rectangle_is_infinite_plane (in_rect)) 431 { 432 gpointer in = gegl_operation_context_get_object (context, "input"); 433 gegl_operation_context_take_object (context, "output", 434 g_object_ref (G_OBJECT (in))); 435 return TRUE; 436 } 437 438 operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class); 439 440 return operation_class->process (operation, context, output_prop, result, 441 gegl_operation_context_get_level (context)); 442 } 443 444 static void 445 gegl_op_class_init (GeglOpClass *klass) 446 { 447 GeglOperationClass *operation_class; 448 GeglOperationFilterClass *filter_class; 449 450 operation_class = GEGL_OPERATION_CLASS (klass); 451 filter_class = GEGL_OPERATION_FILTER_CLASS (klass); 452 453 operation_class->threaded = FALSE; 454 operation_class->get_required_for_output = get_required_for_output; 455 operation_class->get_cached_region = get_cached_region; 456 operation_class->process = operation_process; 457 458 filter_class->process = process; 459 460 gegl_operation_class_set_keys (operation_class, 461 "name", "gegl:tile-paper", 462 "title", _("Paper Tile"), 463 "categories", "artistic:map", 464 "license", "GPL3+", 465 "position-dependent", "true", 466 "reference-hash", "cbff6974b1a06777de798ce16e215a99", 467 "description", _("Cut image into paper tiles, and slide them"), 468 NULL); 469 } 470 471 #endif 472