/* This file is part of GEGL * * GEGL is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * GEGL is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see * . * * 2007 © Øyvind Kolås * 2009 © Nicolas Robidoux * 2011 © Adam Turcotte */ #include "config.h" #include #include #include #include "gegl.h" #include "gegl-types-internal.h" #include "gegl-buffer.h" #include "gegl-utils.h" #include "gegl-buffer-private.h" #include "gegl-sampler-nearest.h" #include "gegl-sampler-linear.h" #include "gegl-sampler-cubic.h" #include "gegl-sampler-lohalo.h" enum { PROP_0, PROP_BUFFER, PROP_FORMAT, PROP_CONTEXT_RECT, PROP_LAST }; static void gegl_sampler_class_init (GeglSamplerClass *klass); static void gegl_sampler_init (GeglSampler *self); static void finalize (GObject *gobject); static void dispose (GObject *gobject); static void get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *pspec); static void set_property ( GObject *gobject, guint property_id, const GValue *value, GParamSpec *pspec); static void set_buffer (GeglSampler *self, GeglBuffer *buffer); G_DEFINE_TYPE (GeglSampler, gegl_sampler, G_TYPE_OBJECT) static void gegl_sampler_class_init (GeglSamplerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = finalize; object_class->dispose = dispose; klass->prepare = NULL; klass->get = NULL; klass->set_buffer = set_buffer; object_class->set_property = set_property; object_class->get_property = get_property; g_object_class_install_property ( object_class, PROP_FORMAT, g_param_spec_pointer ("format", "format", "babl format", G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property ( object_class, PROP_BUFFER, g_param_spec_object ("buffer", "Buffer", "Input pad, for image buffer input.", GEGL_TYPE_BUFFER, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); } static void gegl_sampler_init (GeglSampler *self) { int i; self->buffer = NULL; for (i=0; isampler_buffer[i] = NULL; self->context_rect[i] = context_rect; self->sampler_rectangle[i] = sampler_rectangle; } } void gegl_sampler_get (GeglSampler *self, gdouble x, gdouble y, GeglMatrix2 *scale, void *output, GeglAbyssPolicy repeat_mode) { self->get (self, x, y, scale, output); } void gegl_sampler_prepare (GeglSampler *self) { GeglSamplerClass *klass; g_return_if_fail (GEGL_IS_SAMPLER (self)); klass = GEGL_SAMPLER_GET_CLASS (self); if (klass->prepare) klass->prepare (self); self->fish = babl_fish (self->interpolate_format, self->format); /* * This makes the cache rect invalid, in case the data in the buffer * has changed: */ self->sampler_rectangle[0].width = 0; self->sampler_rectangle[0].height = 0; #if 0 if (self->cache_buffer) /* Force a regetting of the region, even though the cached getter may be valid. */ { g_free (self->cache_buffer); self->cache_buffer = NULL; } #endif self->get = klass->get; /* cache the sampler in the instance */ } void gegl_sampler_set_buffer (GeglSampler *self, GeglBuffer *buffer) { GeglSamplerClass *klass; g_return_if_fail (GEGL_IS_SAMPLER (self)); klass = GEGL_SAMPLER_GET_CLASS (self); if (klass->set_buffer) klass->set_buffer (self, buffer); } static void finalize (GObject *gobject) { int i; GeglSampler *sampler = GEGL_SAMPLER (gobject); for (i=0; isampler_buffer[i]) { g_free (sampler->sampler_buffer[i]); sampler->sampler_buffer[i] = NULL; } } G_OBJECT_CLASS (gegl_sampler_parent_class)->finalize (gobject); } static void dispose (GObject *gobject) { GeglSampler *sampler = GEGL_SAMPLER (gobject); if (sampler->buffer) { g_object_unref (sampler->buffer); sampler->buffer = NULL; } G_OBJECT_CLASS (gegl_sampler_parent_class)->dispose (gobject); } /* * Gets a pointer to the center pixel, within a buffer that has a * rowstride of 64px * 16bpp: */ gfloat * gegl_sampler_get_ptr (GeglSampler *const sampler, const gint x, const gint y) { guchar *buffer_ptr; gint dx; gint dy; gint sof; const gint bpp = babl_format_get_bytes_per_pixel (sampler->interpolate_format); /* * maximum_width_and_height is the largest number of pixels which * can be be requested in the horizontal or vertical directions (64 * in GEGL). */ const gint maximum_width_and_height = 64; g_assert (sampler->context_rect[0].width <= maximum_width_and_height); g_assert (sampler->context_rect[0].height <= maximum_width_and_height); if (( sampler->sampler_buffer[0] == NULL ) || ( x + sampler->context_rect[0].x < sampler->sampler_rectangle[0].x ) || ( y + sampler->context_rect[0].y < sampler->sampler_rectangle[0].y ) || ( x + sampler->context_rect[0].x + sampler->context_rect[0].width > sampler->sampler_rectangle[0].x + sampler->sampler_rectangle[0].width ) || ( y + sampler->context_rect[0].y + sampler->context_rect[0].height > sampler->sampler_rectangle[0].y + sampler->sampler_rectangle[0].height )) { /* * fetch_rectangle will become the value of * sampler->sampler_rectangle[0]: */ GeglRectangle fetch_rectangle; /* * Override the fetch rectangle needed by the sampler, hoping * that the extra pixels are useful for subsequent requests, * assuming that it is more likely that further access is to the * right or down of our currently requested * position. Consequently, we move the top left corner of the * context_rect by about one fourth of the maximal distance we * can (one fourth of one half = one eight). Given that the * maximum width and height of the fetch_rectangle is 64, so * that half of it is 32, one fourth of the elbow room is at * most 8. If context_rect is large, the corner is not moved * much if at all, as should be. */ fetch_rectangle.x = x + sampler->context_rect[0].x - ( maximum_width_and_height - sampler->context_rect[0].width ) / 8; fetch_rectangle.y = y + sampler->context_rect[0].y - ( maximum_width_and_height - sampler->context_rect[0].height ) / 8; fetch_rectangle.width = maximum_width_and_height; fetch_rectangle.height = maximum_width_and_height; if (sampler->sampler_buffer[0] == NULL) { /* * Always request the same amount of pixels: */ sampler->sampler_buffer[0] = g_malloc0 (( maximum_width_and_height * maximum_width_and_height ) * bpp); } gegl_buffer_get (sampler->buffer, &fetch_rectangle, 1.0, sampler->interpolate_format, sampler->sampler_buffer[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); sampler->sampler_rectangle[0] = fetch_rectangle; } dx = x - sampler->sampler_rectangle[0].x; dy = y - sampler->sampler_rectangle[0].y; buffer_ptr = (guchar *)sampler->sampler_buffer[0]; sof = ( dx + dy * sampler->sampler_rectangle[0].width ) * bpp; return (gfloat*)(buffer_ptr+sof); } gfloat * gegl_sampler_get_from_buffer (GeglSampler *const sampler, const gint x, const gint y) { guchar *buffer_ptr; gint dx; gint dy; gint sof; const gint bpp = babl_format_get_bytes_per_pixel (sampler->interpolate_format); /* * maximum_width_and_height is the largest number of pixels which * can be be requested in the horizontal or vertical directions (64 * in GEGL). */ const gint maximum_width_and_height = 64; g_assert (sampler->context_rect[0].width <= maximum_width_and_height); g_assert (sampler->context_rect[0].height <= maximum_width_and_height); if (( sampler->sampler_buffer[0] == NULL ) || ( x < sampler->sampler_rectangle[0].x ) || ( y < sampler->sampler_rectangle[0].y ) || ( x >= sampler->sampler_rectangle[0].x + sampler->sampler_rectangle[0].width ) || ( y >= sampler->sampler_rectangle[0].y + sampler->sampler_rectangle[0].height )) { /* * fetch_rectangle will become the value of * sampler->sampler_rectangle: */ GeglRectangle fetch_rectangle; fetch_rectangle.x = x - ( maximum_width_and_height - sampler->context_rect[0].width ) / 8; fetch_rectangle.y = y - ( maximum_width_and_height - sampler->context_rect[0].height ) / 8; fetch_rectangle.width = maximum_width_and_height; fetch_rectangle.height = maximum_width_and_height; if (sampler->sampler_buffer[0] == NULL) { /* * Always request the same amount of pixels: */ sampler->sampler_buffer[0] = g_malloc0 (( maximum_width_and_height * maximum_width_and_height ) * bpp); } gegl_buffer_get (sampler->buffer, &fetch_rectangle, 1.0, sampler->interpolate_format, sampler->sampler_buffer[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); sampler->sampler_rectangle[0] = fetch_rectangle; } dx = x - sampler->sampler_rectangle[0].x; dy = y - sampler->sampler_rectangle[0].y; buffer_ptr = (guchar *)sampler->sampler_buffer[0]; sof = ( dx + dy * sampler->sampler_rectangle[0].width ) * bpp; return (gfloat*)(buffer_ptr+sof); } gfloat * gegl_sampler_get_from_mipmap (GeglSampler *const sampler, const gint x, const gint y, const gint level) { guchar *buffer_ptr; gint dx; gint dy; gint sof; const gdouble scale = 1. / ( (gdouble) (1<interpolate_format); /* * maximum_width_and_height is the largest number of pixels which * can be be requested in the horizontal or vertical directions (64 * in GEGL). */ const gint maximum_width_and_height = 64; g_assert (sampler->context_rect[level].width <= maximum_width_and_height); g_assert (sampler->context_rect[level].height <= maximum_width_and_height); g_assert (level >= 0 && level < GEGL_SAMPLER_MIPMAP_LEVELS); if (( sampler->sampler_buffer[level] == NULL ) || ( x + sampler->context_rect[level].x < sampler->sampler_rectangle[level].x ) || ( y + sampler->context_rect[level].y < sampler->sampler_rectangle[level].y ) || ( x + sampler->context_rect[level].x + sampler->context_rect[level].width > sampler->sampler_rectangle[level].x + sampler->sampler_rectangle[level].width ) || ( y + sampler->context_rect[level].y + sampler->context_rect[level].height > sampler->sampler_rectangle[level].y + sampler->sampler_rectangle[level].height )) { /* * fetch_rectangle will become the value of * sampler->sampler_rectangle[level]: */ GeglRectangle fetch_rectangle; /* * Override the fetch rectangle needed by the sampler, hoping * that the extra pixels are useful for subsequent requests, * assuming that it is more likely that further access is to the * right or down of our currently requested * position. Consequently, we move the top left corner of the * context_rect by about one fourth of the maximal distance we * can (one fourth of one half = one eight). Given that the * maximum width and height of the fetch_rectangle is 64, so * that half of it is 32, one fourth of the elbow room is at * most 8. If context_rect is large, the corner is not moved * much if at all, as should be. */ fetch_rectangle.x = x + sampler->context_rect[level].x - ( maximum_width_and_height - sampler->context_rect[level].width ) / 8; fetch_rectangle.y = y + sampler->context_rect[level].y - ( maximum_width_and_height - sampler->context_rect[level].height ) / 8; fetch_rectangle.width = maximum_width_and_height; fetch_rectangle.height = maximum_width_and_height; if (sampler->sampler_buffer[level] == NULL) { /* * Always request the same amount of pixels: */ sampler->sampler_buffer[level] = g_malloc0 (( maximum_width_and_height * maximum_width_and_height ) * bpp); } gegl_buffer_get (sampler->buffer, &fetch_rectangle, scale, sampler->interpolate_format, sampler->sampler_buffer[level], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); sampler->sampler_rectangle[level] = fetch_rectangle; } dx = x - sampler->sampler_rectangle[level].x; dy = y - sampler->sampler_rectangle[level].y; buffer_ptr = (guchar *)sampler->sampler_buffer[level]; sof = ( dx + dy * sampler->sampler_rectangle[level].width ) * bpp; return (gfloat*)(buffer_ptr+sof); } static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GeglSampler *self = GEGL_SAMPLER (object); switch (property_id) { case PROP_BUFFER: g_value_set_object (value, self->buffer); break; case PROP_FORMAT: g_value_set_pointer (value, (void*)self->format); break; default: break; } } static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GeglSampler *self = GEGL_SAMPLER (object); switch (property_id) { case PROP_BUFFER: self->buffer = GEGL_BUFFER (g_value_dup_object (value)); break; case PROP_FORMAT: self->format = g_value_get_pointer (value); break; default: break; } } static void set_buffer (GeglSampler *self, GeglBuffer *buffer) { if (self->buffer != buffer) { if (GEGL_IS_BUFFER(self->buffer)) g_object_unref(self->buffer); if (GEGL_IS_BUFFER (buffer)) self->buffer = gegl_buffer_dup (buffer); else self->buffer = NULL; } } GeglSamplerType gegl_sampler_type_from_string (const gchar *string) { if (g_str_equal (string, "nearest") || g_str_equal (string, "none")) return GEGL_SAMPLER_NEAREST; if (g_str_equal (string, "linear") || g_str_equal (string, "bilinear")) return GEGL_SAMPLER_LINEAR; if (g_str_equal (string, "cubic") || g_str_equal (string, "bicubic")) return GEGL_SAMPLER_CUBIC; if (g_str_equal (string, "lohalo")) return GEGL_SAMPLER_LOHALO; return GEGL_SAMPLER_NEAREST; } GType gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type); GType gegl_sampler_gtype_from_enum (GeglSamplerType sampler_type) { switch (sampler_type) { case GEGL_SAMPLER_NEAREST: return GEGL_TYPE_SAMPLER_NEAREST; case GEGL_SAMPLER_LINEAR: return GEGL_TYPE_SAMPLER_LINEAR; case GEGL_SAMPLER_CUBIC: return GEGL_TYPE_SAMPLER_CUBIC; case GEGL_SAMPLER_LOHALO: return GEGL_TYPE_SAMPLER_LOHALO; default: return GEGL_TYPE_SAMPLER_LINEAR; } } GeglSampler * gegl_buffer_sampler_new (GeglBuffer *buffer, const Babl *format, GeglSamplerType sampler_type) { GeglSampler *sampler; GType desired_type; if (format == NULL) format = babl_format ("RaGaBaA float"); desired_type = gegl_sampler_gtype_from_enum (sampler_type); sampler = g_object_new (desired_type, "buffer", buffer, "format", format, NULL); gegl_sampler_prepare (sampler); return sampler; } const GeglRectangle* gegl_sampler_get_context_rect (GeglSampler *sampler) { return &(sampler->context_rect[0]); }