1 /* This file is part of GEGL
2 *
3 * GEGL is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as
5 * published by the Free Software Foundation; either version 3 of the
6 * License, or (at your option) any later version.
7 *
8 * GEGL is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
11 * 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
15 * <https://www.gnu.org/licenses/>.
16 *
17 * 2007 © Øyvind Kolås
18 * 2009,2012 © Nicolas Robidoux
19 * 2011 © Adam Turcotte
20 */
21 #include "config.h"
22
23 #include <glib-object.h>
24 #include <string.h>
25 #include <math.h>
26
27 #include "gegl-buffer.h"
28 #include "gegl-buffer-types.h"
29 #include "gegl-buffer-private.h"
30
31 #include "gegl-sampler-nearest.h"
32 #include "gegl-sampler-linear.h"
33 #include "gegl-sampler-cubic.h"
34 #include "gegl-sampler-nohalo.h"
35 #include "gegl-sampler-lohalo.h"
36 #include "gegl-buffer-formats.h"
37
38
39 enum
40 {
41 PROP_0,
42 PROP_BUFFER,
43 PROP_FORMAT,
44 PROP_LEVEL,
45 PROP_CONTEXT_RECT,
46 PROP_LAST
47 };
48
49 static void gegl_sampler_class_init (GeglSamplerClass *klass);
50
51 static void gegl_sampler_init (GeglSampler *self);
52
53 static void finalize (GObject *gobject);
54
55 static void dispose (GObject *gobject);
56
57 static void get_property (GObject *gobject,
58 guint property_id,
59 GValue *value,
60 GParamSpec *pspec);
61
62 static void set_property (GObject *gobject,
63 guint property_id,
64 const GValue *value,
65 GParamSpec *pspec);
66
67 static void set_buffer (GeglSampler *self,
68 GeglBuffer *buffer);
69
70 static void buffer_contents_changed (GeglBuffer *buffer,
71 const GeglRectangle *changed_rect,
72 gpointer userdata);
73
74 static void constructed (GObject *sampler);
75
76 static GType gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type);
77
G_DEFINE_TYPE(GeglSampler,gegl_sampler,G_TYPE_OBJECT)78 G_DEFINE_TYPE (GeglSampler, gegl_sampler, G_TYPE_OBJECT)
79
80 static void
81 gegl_sampler_class_init (GeglSamplerClass *klass)
82 {
83 GObjectClass *object_class = G_OBJECT_CLASS (klass);
84
85 object_class->finalize = finalize;
86 object_class->dispose = dispose;
87 object_class->constructed = constructed;
88
89 klass->prepare = NULL;
90 klass->get = NULL;
91 klass->interpolate = NULL;
92 klass->set_buffer = set_buffer;
93
94 object_class->set_property = set_property;
95 object_class->get_property = get_property;
96
97 g_object_class_install_property (
98 object_class,
99 PROP_FORMAT,
100 g_param_spec_pointer ("format",
101 "format",
102 "babl format",
103 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
104
105 g_object_class_install_property (
106 object_class,
107 PROP_LEVEL,
108 g_param_spec_int ("level",
109 "level",
110 "mimmap level to sample from",
111 0, 100, 0,
112 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
113
114 g_object_class_install_property (
115 object_class,
116 PROP_BUFFER,
117 g_param_spec_object ("buffer",
118 "Buffer",
119 "Input pad, for image buffer input.",
120 GEGL_TYPE_BUFFER,
121 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
122 }
123
124 static void
gegl_sampler_init(GeglSampler * sampler)125 gegl_sampler_init (GeglSampler *sampler)
126 {
127 gint i = 0;
128 sampler->buffer = NULL;
129 do {
130 GeglRectangle context_rect = {0,0,1,1};
131 GeglRectangle sampler_rectangle = {0,0,0,0};
132 sampler->level[i].sampler_buffer = NULL;
133 sampler->level[i].context_rect = context_rect;
134 sampler->level[i].sampler_rectangle = sampler_rectangle;
135 } while ( ++i<GEGL_SAMPLER_MIPMAP_LEVELS );
136
137 sampler->level[0].sampler_buffer =
138 g_malloc (GEGL_SAMPLER_MAXIMUM_WIDTH *
139 GEGL_SAMPLER_MAXIMUM_HEIGHT * 5 * 4); // XXX : maxes out at 5 components
140 }
141
142 static void
constructed(GObject * self)143 constructed (GObject *self)
144 {
145 GeglSampler *sampler = (void*)(self);
146 GeglSamplerClass *klass = GEGL_SAMPLER_GET_CLASS (sampler);
147
148 sampler->get = klass->get;
149 sampler->interpolate = klass->interpolate;
150
151 if (sampler->buffer)
152 {
153 GeglSamplerLevel *level = &sampler->level[0];
154
155 level->abyss_rect = sampler->buffer->abyss;
156
157 level->abyss_rect.x -= level->context_rect.x +
158 level->context_rect.width;
159 level->abyss_rect.y -= level->context_rect.y +
160 level->context_rect.height;
161 level->abyss_rect.width += level->context_rect.width + 1;
162 level->abyss_rect.height += level->context_rect.height + 1;
163 }
164 }
165
166 void
gegl_sampler_get(GeglSampler * self,gdouble x,gdouble y,GeglBufferMatrix2 * scale,void * output,GeglAbyssPolicy repeat_mode)167 gegl_sampler_get (GeglSampler *self,
168 gdouble x,
169 gdouble y,
170 GeglBufferMatrix2 *scale,
171 void *output,
172 GeglAbyssPolicy repeat_mode)
173 {
174 if (G_UNLIKELY(!isfinite (x)))
175 x = 0.0;
176 if (G_UNLIKELY(!isfinite (y)))
177 y = 0.0;
178
179 if (G_UNLIKELY (self->lvel))
180 {
181 double factor = 1.0 / (1 << self->lvel);
182 GeglRectangle rect={int_floorf (x * factor), int_floorf (y * factor),1,1};
183 gegl_buffer_get (self->buffer, &rect, factor, self->format, output, GEGL_AUTO_ROWSTRIDE, repeat_mode);
184 return;
185 }
186
187 if (G_UNLIKELY (gegl_buffer_ext_flush))
188 {
189 GeglRectangle rect={x,y,1,1};
190 gegl_buffer_ext_flush (self->buffer, &rect);
191 }
192 self->get (self, x, y, scale, output, repeat_mode);
193 }
194
195 void
gegl_sampler_prepare(GeglSampler * self)196 gegl_sampler_prepare (GeglSampler *self)
197 {
198 GeglSamplerClass *klass;
199
200 g_return_if_fail (GEGL_IS_SAMPLER (self));
201
202 klass = GEGL_SAMPLER_GET_CLASS (self);
203
204 if (!self->buffer) /* happens when extent of sampler is queried */
205 return;
206 if (!self->format)
207 self->format = self->buffer->soft_format;
208
209 if (klass->prepare)
210 klass->prepare (self);
211
212 {
213 const Babl *model = babl_format_get_model (self->format);
214
215 if (babl_model_is (model, "Y")||
216 babl_model_is (model, "Y'")||
217 babl_model_is (model, "Y~")||
218 babl_model_is (model, "YA")||
219 babl_model_is (model, "YaA")||
220 babl_model_is (model, "Y'aA")||
221 babl_model_is (model, "Y'A")||
222 babl_model_is (model, "Y~A"))
223 {
224 self->interpolate_format = babl_format_with_space ("YaA float",
225 gegl_buffer_get_format(self->buffer));
226 }
227 else if (babl_model_is (model, "cmyk")||
228 babl_model_is (model, "cmykA") ||
229 babl_model_is (model, "camayakaA"))
230 {
231 self->interpolate_format = babl_format_with_space ("camayakaA float",
232 gegl_buffer_get_format(self->buffer));
233 }
234 else if (
235 babl_model_is (model, "CMYK")||
236 babl_model_is (model, "CMYKA") ||
237 babl_model_is (model, "CaMaYaKaA"))
238 {
239 self->interpolate_format = babl_format_with_space ("CaMaYaKaA float",
240 gegl_buffer_get_format(self->buffer));
241 }
242 #if 0
243 else if (babl_model_is (model, "RGB")||
244 babl_model_is (model, "R'G'B'")||
245 babl_model_is (model, "R~G~B~") ||
246 babl_model_is (model, "RGBA")||
247 babl_model_is (model, "R'G'B'A")||
248 babl_model_is (model, "R~G~B~A")||
249 babl_model_is (model, "RaGaBaA")||
250 babl_model_is (model, "R'aG'aB'aA")||
251 babl_model_is (model, "R~aG~aB~aA"))
252 {
253 self->interpolate_format = babl_format_with_space ("RaGaBaA float",
254 gegl_buffer_get_format(self->buffer));
255 }
256 #endif
257 else
258 {
259 self->interpolate_format = babl_format_with_space ("RaGaBaA float",
260 gegl_buffer_get_format(self->buffer));
261 }
262
263 self->interpolate_bpp = babl_format_get_bytes_per_pixel (self->interpolate_format);
264 self->interpolate_components = babl_format_get_n_components (self->interpolate_format);
265 }
266
267 if (!self->fish)
268 self->fish = babl_fish (self->interpolate_format, self->format);
269
270 /*
271 * This makes the cache rect invalid, in case the data in the buffer
272 * has changed:
273 */
274 self->level[0].sampler_rectangle.width = 0;
275 self->level[0].sampler_rectangle.height = 0;
276 }
277
278 void
gegl_sampler_set_buffer(GeglSampler * self,GeglBuffer * buffer)279 gegl_sampler_set_buffer (GeglSampler *self, GeglBuffer *buffer)
280 {
281 GeglSamplerClass *klass;
282
283 g_return_if_fail (GEGL_IS_SAMPLER (self));
284
285 klass = GEGL_SAMPLER_GET_CLASS (self);
286
287 if (klass->set_buffer)
288 klass->set_buffer (self, buffer);
289 }
290
291 static void
finalize(GObject * gobject)292 finalize (GObject *gobject)
293 {
294 GeglSampler *sampler = GEGL_SAMPLER (gobject);
295 int i = 0;
296 do {
297 if (sampler->level[i].sampler_buffer)
298 {
299 g_free (sampler->level[i].sampler_buffer);
300 sampler->level[i].sampler_buffer = NULL;
301 }
302 } while ( ++i<GEGL_SAMPLER_MIPMAP_LEVELS );
303 G_OBJECT_CLASS (gegl_sampler_parent_class)->finalize (gobject);
304 }
305
306 static void
dispose(GObject * gobject)307 dispose (GObject *gobject)
308 {
309 GeglSampler *sampler = GEGL_SAMPLER (gobject);
310
311 /* This call handles unreffing the buffer and disconnecting signals */
312 set_buffer (sampler, NULL);
313
314 G_OBJECT_CLASS (gegl_sampler_parent_class)->dispose (gobject);
315 }
316
317
318 gfloat *
gegl_sampler_get_from_mipmap(GeglSampler * sampler,gint x,gint y,gint level_no,GeglAbyssPolicy repeat_mode)319 gegl_sampler_get_from_mipmap (GeglSampler *sampler,
320 gint x,
321 gint y,
322 gint level_no,
323 GeglAbyssPolicy repeat_mode)
324 {
325 GeglSamplerLevel *level = &sampler->level[level_no];
326 guchar *buffer_ptr;
327 gint dx;
328 gint dy;
329 gint sof;
330
331 const gdouble scale = 1. / ((gdouble) (1 << level_no));
332
333 const gint maximum_width = GEGL_SAMPLER_MAXIMUM_WIDTH;
334 const gint maximum_height = GEGL_SAMPLER_MAXIMUM_HEIGHT;
335
336 if (G_UNLIKELY (gegl_buffer_ext_flush))
337 {
338 GeglRectangle rect = {x, y, 1, 1};
339 gegl_buffer_ext_flush (sampler->buffer, &rect);
340 }
341
342 g_assert (level_no >= 0 && level_no < GEGL_SAMPLER_MIPMAP_LEVELS);
343 g_assert (level->context_rect.width <= maximum_width);
344 g_assert (level->context_rect.height <= maximum_height);
345
346 if ((level->sampler_buffer == NULL) ||
347 (x + level->context_rect.x < level->sampler_rectangle.x) ||
348 (y + level->context_rect.y < level->sampler_rectangle.y) ||
349 (x + level->context_rect.x + level->context_rect.width >
350 level->sampler_rectangle.x + level->sampler_rectangle.width) ||
351 (y + level->context_rect.y + level->context_rect.height >
352 level->sampler_rectangle.y + level->sampler_rectangle.height))
353 {
354 /*
355 * fetch_rectangle will become the value of
356 * sampler->sampler_rectangle[level]:
357 */
358 level->sampler_rectangle = _gegl_sampler_compute_rectangle (sampler, x, y,
359 level_no);
360 if (!level->sampler_buffer)
361 level->sampler_buffer =
362 g_malloc (GEGL_SAMPLER_MAXIMUM_WIDTH * sampler->interpolate_bpp * GEGL_SAMPLER_MAXIMUM_HEIGHT);
363
364 gegl_buffer_get (sampler->buffer,
365 &level->sampler_rectangle,
366 scale,
367 sampler->interpolate_format,
368 level->sampler_buffer,
369 GEGL_SAMPLER_MAXIMUM_WIDTH * sampler->interpolate_bpp,
370 repeat_mode);
371 }
372
373 dx = x - level->sampler_rectangle.x;
374 dy = y - level->sampler_rectangle.y;
375 buffer_ptr = (guchar *) level->sampler_buffer;
376 sof = (dx + dy * GEGL_SAMPLER_MAXIMUM_WIDTH) * sampler->interpolate_bpp;
377
378 return (gfloat*) (buffer_ptr + sof);
379 }
380
381 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)382 get_property (GObject *object,
383 guint property_id,
384 GValue *value,
385 GParamSpec *pspec)
386 {
387 GeglSampler *self = GEGL_SAMPLER (object);
388
389 switch (property_id)
390 {
391 case PROP_BUFFER:
392 g_value_set_object (value, self->buffer);
393 break;
394
395 case PROP_FORMAT:
396 g_value_set_pointer (value, (void*)self->format);
397 break;
398
399 case PROP_LEVEL:
400 g_value_set_int (value, self->lvel);
401 break;
402
403 default:
404 break;
405 }
406 }
407
408 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)409 set_property (GObject *object,
410 guint property_id,
411 const GValue *value,
412 GParamSpec *pspec)
413 {
414 GeglSampler *self = GEGL_SAMPLER (object);
415
416 switch (property_id)
417 {
418 case PROP_BUFFER:
419 gegl_sampler_set_buffer (self, GEGL_BUFFER (g_value_get_object (value)));
420 break;
421
422 case PROP_FORMAT:
423 self->format = g_value_get_pointer (value);
424 break;
425
426 case PROP_LEVEL:
427 self->lvel = g_value_get_int (value);
428 break;
429
430 default:
431 break;
432 }
433 }
434
435 static void
set_buffer(GeglSampler * self,GeglBuffer * buffer)436 set_buffer (GeglSampler *self, GeglBuffer *buffer)
437 {
438 if (self->buffer != buffer)
439 {
440 if (GEGL_IS_BUFFER (self->buffer))
441 {
442 g_signal_handlers_disconnect_by_func (self->buffer,
443 G_CALLBACK (buffer_contents_changed),
444 self);
445 self->buffer->changed_signal_connections--;
446 g_object_remove_weak_pointer ((GObject*) self->buffer, (void**) &self->buffer);
447 }
448
449 if (GEGL_IS_BUFFER (buffer))
450 {
451 self->buffer = buffer;
452 g_object_add_weak_pointer ((GObject*) self->buffer, (void**) &self->buffer);
453 gegl_buffer_signal_connect (buffer, "changed",
454 G_CALLBACK (buffer_contents_changed),
455 self);
456 }
457 else
458 self->buffer = NULL;
459
460 buffer_contents_changed (buffer, NULL, self);
461 }
462 }
463
464 static inline GType
gegl_sampler_gtype_from_enum(GeglSamplerType sampler_type)465 gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type)
466 {
467 switch (sampler_type)
468 {
469 case GEGL_SAMPLER_NEAREST:
470 return GEGL_TYPE_SAMPLER_NEAREST;
471 case GEGL_SAMPLER_LINEAR:
472 return GEGL_TYPE_SAMPLER_LINEAR;
473 case GEGL_SAMPLER_CUBIC:
474 return GEGL_TYPE_SAMPLER_CUBIC;
475 case GEGL_SAMPLER_NOHALO:
476 return GEGL_TYPE_SAMPLER_NOHALO;
477 case GEGL_SAMPLER_LOHALO:
478 return GEGL_TYPE_SAMPLER_LOHALO;
479 default:
480 return GEGL_TYPE_SAMPLER_LINEAR;
481 }
482 }
483
484 static inline void
_gegl_buffer_sample_at_level(GeglBuffer * buffer,gdouble x,gdouble y,GeglBufferMatrix2 * scale,gpointer dest,const Babl * format,gint level,GeglSamplerType sampler_type,GeglAbyssPolicy repeat_mode)485 _gegl_buffer_sample_at_level (GeglBuffer *buffer,
486 gdouble x,
487 gdouble y,
488 GeglBufferMatrix2 *scale,
489 gpointer dest,
490 const Babl *format,
491 gint level,
492 GeglSamplerType sampler_type,
493 GeglAbyssPolicy repeat_mode)
494 {
495 GeglSampler *sampler;
496
497 if (sampler_type == GEGL_SAMPLER_NEAREST &&
498 level == 0)
499 {
500 GeglRectangle rect = {x, y, 1, 1};
501 gegl_buffer_get (buffer, &rect, 1.0,
502 format, dest, GEGL_AUTO_ROWSTRIDE,
503 repeat_mode);
504 return;
505 }
506
507 if (G_UNLIKELY (!format))
508 format = buffer->soft_format;
509
510 sampler = gegl_buffer_sampler_new_at_level (buffer,
511 format, sampler_type, level);
512
513 gegl_sampler_get (sampler, x, y, scale, dest, repeat_mode);
514
515 g_object_unref (sampler);
516 }
517
518 void
gegl_buffer_sample_at_level(GeglBuffer * buffer,gdouble x,gdouble y,GeglBufferMatrix2 * scale,gpointer dest,const Babl * format,gint level,GeglSamplerType sampler_type,GeglAbyssPolicy repeat_mode)519 gegl_buffer_sample_at_level (GeglBuffer *buffer,
520 gdouble x,
521 gdouble y,
522 GeglBufferMatrix2 *scale,
523 gpointer dest,
524 const Babl *format,
525 gint level,
526 GeglSamplerType sampler_type,
527 GeglAbyssPolicy repeat_mode)
528 {
529 _gegl_buffer_sample_at_level (buffer, x, y, scale, dest,
530 format, level, sampler_type, repeat_mode);
531 }
532
533
534 void
gegl_buffer_sample(GeglBuffer * buffer,gdouble x,gdouble y,GeglBufferMatrix2 * scale,gpointer dest,const Babl * format,GeglSamplerType sampler_type,GeglAbyssPolicy repeat_mode)535 gegl_buffer_sample (GeglBuffer *buffer,
536 gdouble x,
537 gdouble y,
538 GeglBufferMatrix2 *scale,
539 gpointer dest,
540 const Babl *format,
541 GeglSamplerType sampler_type,
542 GeglAbyssPolicy repeat_mode)
543 {
544 _gegl_buffer_sample_at_level (buffer, x, y, scale, dest, format, 0, sampler_type, repeat_mode);
545 }
546
547 void
gegl_buffer_sample_cleanup(GeglBuffer * buffer)548 gegl_buffer_sample_cleanup (GeglBuffer *buffer)
549 {
550 /* this is a nop. we used to have a per-buffer cached sampler, for use by
551 * gegl_buffer_sample[_at_level](), which this function would clear, but we
552 * don't anymore.
553 */
554 }
555
556 GeglSampler *
gegl_buffer_sampler_new_at_level(GeglBuffer * buffer,const Babl * format,GeglSamplerType sampler_type,gint level)557 gegl_buffer_sampler_new_at_level (GeglBuffer *buffer,
558 const Babl *format,
559 GeglSamplerType sampler_type,
560 gint level)
561 {
562 GeglSampler *sampler;
563 GType desired_type;
564
565 if (format == NULL)
566 {
567 format = gegl_babl_rgbA_linear_float ();
568 }
569
570 desired_type = gegl_sampler_gtype_from_enum (sampler_type);
571
572 sampler = g_object_new (desired_type,
573 "buffer", buffer,
574 "format", format,
575 "level", level,
576 NULL);
577
578 gegl_sampler_prepare (sampler);
579
580 return sampler;
581 }
582
583 GeglSampler *
gegl_buffer_sampler_new(GeglBuffer * buffer,const Babl * format,GeglSamplerType sampler_type)584 gegl_buffer_sampler_new (GeglBuffer *buffer,
585 const Babl *format,
586 GeglSamplerType sampler_type)
587 {
588 return gegl_buffer_sampler_new_at_level (buffer, format, sampler_type, 0);
589 }
590
591
592 const GeglRectangle*
gegl_sampler_get_context_rect(GeglSampler * sampler)593 gegl_sampler_get_context_rect (GeglSampler *sampler)
594 {
595 return &(sampler->level[0].context_rect);
596 }
597
598 static void
buffer_contents_changed(GeglBuffer * buffer,const GeglRectangle * changed_rect,gpointer userdata)599 buffer_contents_changed (GeglBuffer *buffer,
600 const GeglRectangle *changed_rect,
601 gpointer userdata)
602 {
603 GeglSampler *self = GEGL_SAMPLER (userdata);
604 int i;
605
606 /*
607 * Invalidate all mipmap levels by setting the width and height of the
608 * rectangles to zero. The x and y coordinates do not matter any more, so we
609 * can just call memset to do this.
610 * XXX: it might be faster to only invalidate rects that intersect
611 * changed_rect
612 */
613 for (i = 0; i < GEGL_SAMPLER_MIPMAP_LEVELS; i++)
614 memset (&self->level[i].sampler_rectangle, 0, sizeof (self->level[0].sampler_rectangle));
615
616 return;
617 }
618
gegl_sampler_get_fun(GeglSampler * sampler)619 GeglSamplerGetFun gegl_sampler_get_fun (GeglSampler *sampler)
620 {
621 /* this flushes the buffer in preparation for the use of the sampler,
622 * thus one can consider the handed out sampler function only temporarily
623 * available*/
624 if (gegl_buffer_ext_flush)
625 gegl_buffer_ext_flush (sampler->buffer, NULL);
626 return sampler->get;
627 }
628
629