/* 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 * . * * Copyright 2011-2012 Nicolas Robidoux based on earlier code * 2012 Massimo Valentini */ #include "config.h" #include #include "gegl-buffer.h" #include "gegl-buffer-formats.h" #include "gegl-sampler-linear.h" enum { PROP_0, PROP_LAST }; static inline void gegl_sampler_linear_interpolate ( GeglSampler* restrict self, const gdouble absolute_x, const gdouble absolute_y, gfloat* restrict output, GeglAbyssPolicy repeat_mode); static void gegl_sampler_linear_get ( GeglSampler* restrict self, const gdouble absolute_x, const gdouble absolute_y, GeglBufferMatrix2 *scale, void* restrict output, GeglAbyssPolicy repeat_mode); G_DEFINE_TYPE (GeglSamplerLinear, gegl_sampler_linear, GEGL_TYPE_SAMPLER) static void gegl_sampler_linear_class_init (GeglSamplerLinearClass *klass) { GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass); sampler_class->get = gegl_sampler_linear_get; sampler_class->interpolate = gegl_sampler_linear_interpolate; } /* * In principle, x=y=0 and width=height=2 are enough. The following * values are chosen so as to make the context_rect symmetrical * w.r.t. the anchor point. This is so that enough elbow room is added * with transformations that reflect the context rect. If the * context_rect is not symmetrical, the transformation may turn right * into left, and if the context_rect does not stretch far enough on * the left, pixel lookups will fail. * * Additional extra elbow room is added all around. It could be set to * 0 if it is found that round off error never sends things "too far * away". Nicolas would be very surprised if more than 1 is necessary. */ #define LINEAR_EXTRA_ELBOW_ROOM 0 static void gegl_sampler_linear_init (GeglSamplerLinear *self) { GEGL_SAMPLER (self)->level[0].context_rect.x = -1 - LINEAR_EXTRA_ELBOW_ROOM; GEGL_SAMPLER (self)->level[0].context_rect.y = -1 - LINEAR_EXTRA_ELBOW_ROOM; GEGL_SAMPLER (self)->level[0].context_rect.width = 3 + 2*LINEAR_EXTRA_ELBOW_ROOM; GEGL_SAMPLER (self)->level[0].context_rect.height = 3 + 2*LINEAR_EXTRA_ELBOW_ROOM; } static inline void gegl_sampler_linear_interpolate ( GeglSampler *self, const gdouble absolute_x, const gdouble absolute_y, gfloat *output, GeglAbyssPolicy repeat_mode) { gint nc = self->interpolate_components; const gint pixels_per_buffer_row = GEGL_SAMPLER_MAXIMUM_WIDTH; /* * The "-1/2"s are there because we want the index of the pixel to * the left and top of the location, and with GIMP's convention the * top left of the top left pixel is located at * (1/2,1/2). Basically, we are converting from a coordinate system * in which the origin is at the top left pixel of the pixel with * index (0,0), to a coordinate system in which the origin is at the * center of the same pixel. */ const float iabsolute_x = (float) absolute_x - 0.5; const float iabsolute_y = (float) absolute_y - 0.5; const gint ix = int_floorf (iabsolute_x); const gint iy = int_floorf (iabsolute_y); /* * Point the data tile pointer to the first channel of the top_left * pixel value: */ const gfloat* restrict in_bptr = gegl_sampler_get_ptr (self, ix, iy, repeat_mode); /* * x is the x-coordinate of the sampling point relative to the * position of the center of the top left pixel. Similarly for * y. Range of values: [0,1]. */ const gfloat x = iabsolute_x - ix; const gfloat y = iabsolute_y - iy; /* * First bilinear weight: */ const gfloat x_times_y = x * y; /* * Load top row: */ gfloat top_left[nc]; gfloat top_rite[nc]; for (gint c = 0; c < nc; c++) top_left[c] = *in_bptr++; for (gint c = 0; c < nc; c++) top_rite[c] = *in_bptr++; in_bptr += ( pixels_per_buffer_row - 2 ) * nc; { /* * More bilinear weights: * * (Note: w = 1-x and z = 1-y.) */ const gfloat w_times_y = y - x_times_y; const gfloat x_times_z = x - x_times_y; /* * Load bottom row: */ gfloat bot_left[5]; gfloat bot_rite[5]; for (gint c = 0; c < nc; c++) bot_left[c] = *in_bptr++; for (gint c = 0; c < nc; c++) bot_rite[c] = *in_bptr++; /* * Last bilinear weight: */ { const gfloat w_times_z = (gfloat) 1. - ( x + w_times_y ); for (gint c = 0; c < nc; c++) { output[c] = x_times_y * bot_rite[c] + w_times_y * bot_left[c] + x_times_z * top_rite[c] + w_times_z * top_left[c]; } } } } static void gegl_sampler_linear_get ( GeglSampler *self, const gdouble absolute_x, const gdouble absolute_y, GeglBufferMatrix2 *scale, void *output, GeglAbyssPolicy repeat_mode) { if (G_LIKELY(! _gegl_sampler_box_get (self, absolute_x, absolute_y, scale, output, repeat_mode, 4))) { gfloat result[5]; gegl_sampler_linear_interpolate (self, absolute_x, absolute_y, result, repeat_mode); #if (BABL_MINOR_VERSION >=1) && (BABL_MICRO_VERSION >= 90) self->fish_process #else babl_process #endif (self->fish, (void*)result, (void*)output, 1); } }