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  *
16  * Copyright 2006 Øyvind Kolås <pippin@gimp.org>
17  */
18 
19 
20 #include "config.h"
21 #include <glib/gi18n-lib.h>
22 #include <stdlib.h>
23 
24 #ifdef GEGL_PROPERTIES
25 
26 property_int (x, _("Width"), 16)
27     description (_("Horizontal width of cells pixels"))
28     value_range (1, G_MAXINT)
29     ui_range    (1, 256)
30     ui_gamma    (1.5)
31     ui_meta     ("unit", "pixel-distance")
32     ui_meta     ("axis", "x")
33 
34 property_int (y, _("Height"), 16)
35     description (_("Vertical width of cells pixels"))
36     value_range (1, G_MAXINT)
37     ui_range    (1, 256)
38     ui_gamma    (1.5)
39     ui_meta     ("unit", "pixel-distance")
40     ui_meta     ("axis", "y")
41 
42 property_int (x_offset, _("Offset X"), 0.0)
43     description (_("Horizontal offset (from origin) for start of grid"))
44     ui_range    (-128, 128)
45     ui_meta     ("unit", "pixel-coordinate")
46     ui_meta     ("axis", "x")
47 
48 property_int (y_offset, _("Offset Y"), 0)
49     description (_("Vertical offset (from origin) for start of grid"))
50     ui_range    (-128, 128)
51     ui_meta     ("unit", "pixel-coordinate")
52     ui_meta     ("axis", "y")
53 
54 property_color  (color1, _("Color 1"), "black")
55     description (_("The first cell color"))
56     ui_meta     ("role", "color-primary")
57 
58 property_color  (color2, _("Color 2"), "white")
59     description (_("The second cell color"))
60     ui_meta     ("role", "color-secondary")
61 
62 property_format (format, _("Babl Format"), NULL)
63     description ( _("The babl format of the output"))
64 
65 #else
66 
67 #define GEGL_OP_POINT_RENDER
68 #define GEGL_OP_NAME     checkerboard
69 #define GEGL_OP_C_SOURCE checkerboard.c
70 
71 #include "gegl-op.h"
72 #include <gegl-buffer-cl-iterator.h>
73 #include <../../gegl/gegl-debug.h>
74 
75 static void
76 prepare (GeglOperation *operation)
77 {
78   GeglProperties *o = GEGL_PROPERTIES (operation);
79 
80   if (o->format)
81     gegl_operation_set_format (operation, "output", o->format);
82   else
83     gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
84 }
85 
86 static GeglRectangle
87 get_bounding_box (GeglOperation *operation)
88 {
89   return gegl_rectangle_infinite_plane ();
90 }
91 
92 #define TILE_INDEX(coordinate,stride) \
93   (((coordinate) >= 0)?\
94       (coordinate) / (stride):\
95       ((((coordinate) + 1) /(stride)) - 1))
96 
97 
98 #include "opencl/checkerboard.cl.h"
99 static GeglClRunData *cl_data = NULL;
100 
101 static gboolean
102 checkerboard_cl_process (GeglOperation       *operation,
103                          cl_mem               out_tex,
104                          size_t               global_worksize,
105                          const GeglRectangle *roi,
106                          gint                 level)
107 {
108   GeglProperties *o         = GEGL_PROPERTIES (operation);
109   const Babl   *out_format  = gegl_operation_get_format (operation, "output");
110   const size_t  gbl_size[2] = {roi->width, roi->height};
111   const size_t  gbl_offs[2] = {roi->x, roi->y};
112   cl_int        cl_err      = 0;
113   float         color1[4];
114   float         color2[4];
115 
116   if (!cl_data)
117   {
118     const char *kernel_name[] = {"kernel_checkerboard", NULL};
119     cl_data = gegl_cl_compile_and_build (checkerboard_cl_source, kernel_name);
120 
121     if (!cl_data)
122       return TRUE;
123   }
124 
125   gegl_color_get_pixel (o->color1, out_format, color1);
126   gegl_color_get_pixel (o->color2, out_format, color2);
127 
128   cl_err = gegl_cl_set_kernel_args (cl_data->kernel[0],
129                                     sizeof(cl_mem), &out_tex,
130                                     sizeof(color1), &color1,
131                                     sizeof(color2), &color2,
132                                     sizeof(cl_int), &o->x,
133                                     sizeof(cl_int), &o->y,
134                                     sizeof(cl_int), &o->x_offset,
135                                     sizeof(cl_int), &o->y_offset,
136                                     NULL);
137   CL_CHECK;
138 
139   cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
140                                         cl_data->kernel[0], 2,
141                                         gbl_offs, gbl_size, NULL,
142                                         0, NULL, NULL);
143   CL_CHECK;
144 
145   return FALSE;
146 error:
147   return TRUE;
148 }
149 
150 
151 static gboolean
152 checkerboard_process_simple (GeglOperation       *operation,
153                              void                *out_buf,
154                              glong                n_pixels,
155                              const GeglRectangle *roi,
156                              gint                 level)
157 {
158   gint        factor = 1 << level;
159   GeglProperties *o = GEGL_PROPERTIES (operation);
160   const Babl *out_format = gegl_operation_get_format (operation, "output");
161 
162   gint        pixel_size = babl_format_get_bytes_per_pixel (out_format);
163   guchar     *out_pixel = out_buf;
164   void       *color1 = alloca(pixel_size);
165   void       *color2 = alloca(pixel_size);
166 
167   gint        x = roi->x; /* initial x                   */
168   gint        y = roi->y; /*           and y coordinates */
169 
170   gegl_color_get_pixel (o->color1, out_format, color1);
171   gegl_color_get_pixel (o->color2, out_format, color2);
172 
173   while (n_pixels--)
174     {
175       gint nx,ny;
176 
177       if ((x - o->x_offset) < 0)
178         {
179           nx = div (x - o->x_offset + 1, o->x / factor).quot;
180         }
181       else
182         {
183           nx = div (x - o->x_offset, o->x / factor).quot;
184         }
185 
186       if ((y - o->y_offset) < 0)
187         {
188           ny = div (y - o->y_offset + 1, o->y / factor).quot;
189         }
190       else
191         {
192           ny = div (y - o->y_offset, o->y / factor).quot;
193         }
194 
195       /* shift negative cell indices */
196       nx -= (x - o->x_offset) < 0 ? 1 : 0;
197       ny -= (y - o->y_offset) < 0 ? 1 : 0;
198 
199       if ( (nx+ny) % 2 == 0)
200         memcpy (out_pixel, color1, pixel_size);
201       else
202         memcpy (out_pixel, color2, pixel_size);
203 
204       out_pixel += pixel_size;
205 
206       /* update x and y coordinates */
207       x++;
208       if (x>=roi->x + roi->width)
209         {
210           x=roi->x;
211           y++;
212         }
213     }
214 
215   return  TRUE;
216 }
217 
218 static gboolean
219 checkerboard_process (GeglOperation       *operation,
220                       void                *out_buf,
221                       glong                n_pixels,
222                       const GeglRectangle *roi,
223                       gint                 level)
224 {
225   GeglProperties *o = GEGL_PROPERTIES (operation);
226   const Babl *out_format = gegl_operation_get_format (operation, "output");
227   gint        pixel_size = babl_format_get_bytes_per_pixel (out_format);
228   guchar     *out_pixel = out_buf;
229   void       *color1 = alloca(pixel_size);
230   void       *color2 = alloca(pixel_size);
231   gint        y;
232   const gint  x_min = roi->x - o->x_offset;
233   const gint  y_min = roi->y - o->y_offset;
234   const gint  x_max = roi->x + roi->width - o->x_offset;
235   const gint  y_max = roi->y + roi->height - o->y_offset;
236 
237   const gint  square_width  = o->x;
238   const gint  square_height = o->y;
239 
240   if (level)
241     return checkerboard_process_simple (operation, out_buf, n_pixels, roi, level);
242 
243   gegl_color_get_pixel (o->color1, out_format, color1);
244   gegl_color_get_pixel (o->color2, out_format, color2);
245 
246   for (y = y_min; y < y_max; y++)
247     {
248       gint  x = x_min;
249       void *cur_color;
250 
251       /* Figure out which box we're in */
252       gint tilex = TILE_INDEX (x, square_width);
253       gint tiley = TILE_INDEX (y, square_height);
254       if ((tilex + tiley) % 2 == 0)
255         cur_color = color1;
256       else
257         cur_color = color2;
258 
259       while (x < x_max)
260         {
261           /* Figure out how long this stripe is */
262           gint count;
263           gint stripe_end = (TILE_INDEX (x, square_width) + 1) * square_width;
264                stripe_end = stripe_end > x_max ? x_max : stripe_end;
265 
266           count = stripe_end - x;
267 
268           gegl_memset_pattern (out_pixel, cur_color, pixel_size, count);
269           out_pixel += count * pixel_size;
270           x = stripe_end;
271 
272           if (cur_color == color1)
273             cur_color = color2;
274           else
275             cur_color = color1;
276         }
277     }
278 
279   return TRUE;
280 }
281 
282 
283 static gboolean
284 operation_source_process (GeglOperation       *operation,
285                           GeglBuffer          *output,
286                           const GeglRectangle *result,
287                           gint                 level)
288 {
289   const Babl *out_format = gegl_operation_get_format (operation, "output");
290 
291   if ((result->width > 0) && (result->height > 0))
292     {
293       GeglBufferIterator *iter;
294       if (gegl_operation_use_opencl (operation) &&
295           babl_format_get_n_components (out_format) == 4 &&
296           babl_format_get_type (out_format, 0) == babl_type ("float"))
297         {
298           GeglBufferClIterator *cl_iter;
299           gboolean err;
300 
301           GEGL_NOTE (GEGL_DEBUG_OPENCL, "GEGL_OPERATION_POINT_RENDER: %s", GEGL_OPERATION_GET_CLASS (operation)->name);
302 
303           cl_iter = gegl_buffer_cl_iterator_new (output, result, out_format, GEGL_CL_BUFFER_WRITE);
304 
305           while (gegl_buffer_cl_iterator_next (cl_iter, &err) && !err)
306             {
307               err = checkerboard_cl_process (operation, cl_iter->tex[0], cl_iter->size[0], &cl_iter->roi[0], level);
308 
309               if (err)
310                 {
311                   gegl_buffer_cl_iterator_stop (cl_iter);
312                   break;
313                 }
314             }
315 
316           if (err)
317             GEGL_NOTE (GEGL_DEBUG_OPENCL, "Error: %s", GEGL_OPERATION_GET_CLASS (operation)->name);
318           else
319             return TRUE;
320         }
321 
322       iter = gegl_buffer_iterator_new (output, result, level, out_format,
323                                        GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
324 
325       while (gegl_buffer_iterator_next (iter))
326         checkerboard_process (operation, iter->items[0].data, iter->length, &iter->items[0].roi, level);
327     }
328   return TRUE;
329 }
330 
331 static void
332 gegl_op_class_init (GeglOpClass *klass)
333 {
334   GeglOperationClass            *operation_class;
335   GeglOperationSourceClass      *source_class;
336 
337   operation_class = GEGL_OPERATION_CLASS (klass);
338   source_class = GEGL_OPERATION_SOURCE_CLASS (klass);
339 
340   source_class->process = operation_source_process;
341   operation_class->get_bounding_box = get_bounding_box;
342   operation_class->prepare = prepare;
343   operation_class->opencl_support = TRUE;
344 
345   gegl_operation_class_set_keys (operation_class,
346     "name",               "gegl:checkerboard",
347     "categories",         "render",
348     "title",              _("Checkerboard"),
349     "reference-hash",     "b2f5f85a0ec1de87639c1b0cfcd17fbc",
350     "position-dependent", "true",
351     "description",        _("Render a checkerboard pattern"),
352     NULL);
353 }
354 
355 #endif
356