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