/* 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 .
*
*/
#include "config.h"
#include
#include
#include "gegl-buffer.h"
#include "gegl-buffer-formats.h"
#include "gegl-buffer-types.h"
#include "gegl-buffer.h"
#include "gegl-buffer-private.h"
#include "gegl-tile-storage.h"
#include "gegl-tile-backend.h"
#include "gegl-sampler-nearest.h"
enum
{
PROP_0,
PROP_LAST
};
static void
gegl_sampler_nearest_dispose (GObject *self);
static void
gegl_sampler_nearest_get (GeglSampler* restrict self,
const gdouble absolute_x,
const gdouble absolute_y,
GeglBufferMatrix2 *scale,
void* restrict output,
GeglAbyssPolicy repeat_mode);
static void
gegl_sampler_nearest_prepare (GeglSampler* restrict self);
G_DEFINE_TYPE (GeglSamplerNearest, gegl_sampler_nearest, GEGL_TYPE_SAMPLER)
static void
gegl_sampler_nearest_class_init (GeglSamplerNearestClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
object_class->dispose = gegl_sampler_nearest_dispose;
sampler_class->get = gegl_sampler_nearest_get;
sampler_class->prepare = gegl_sampler_nearest_prepare;
}
/*
* It would seem that x=y=0 and width=height=1 should be enough, but
* apparently safety w.r.t. round off or something else makes things
* work better with width=height=3 and centering.
*/
static void
gegl_sampler_nearest_init (GeglSamplerNearest *self)
{
GEGL_SAMPLER (self)->level[0].context_rect.x = 0;
GEGL_SAMPLER (self)->level[0].context_rect.y = 0;
GEGL_SAMPLER (self)->level[0].context_rect.width = 1;
GEGL_SAMPLER (self)->level[0].context_rect.height = 1;
}
static void
gegl_sampler_nearest_dispose (GObject *object)
{
GeglSamplerNearest *nearest_sampler = GEGL_SAMPLER_NEAREST (object);
if (nearest_sampler->hot_tile)
{
gegl_tile_read_unlock (nearest_sampler->hot_tile);
g_clear_pointer (&nearest_sampler->hot_tile, gegl_tile_unref);
}
G_OBJECT_CLASS (gegl_sampler_nearest_parent_class)->dispose (object);
}
static void inline
gegl_sampler_get_pixel (GeglSampler *sampler,
gint x,
gint y,
gpointer data,
GeglAbyssPolicy repeat_mode)
{
GeglSamplerNearest *nearest_sampler = (GeglSamplerNearest*)(sampler);
GeglBuffer *buffer = sampler->buffer;
const GeglRectangle *abyss = &buffer->abyss;
guchar *buf = data;
if (y < abyss->y ||
x < abyss->x ||
y >= abyss->y + abyss->height ||
x >= abyss->x + abyss->width)
{
switch (repeat_mode)
{
case GEGL_ABYSS_CLAMP:
x = CLAMP (x, abyss->x, abyss->x+abyss->width-1);
y = CLAMP (y, abyss->y, abyss->y+abyss->height-1);
break;
case GEGL_ABYSS_LOOP:
x = abyss->x + GEGL_REMAINDER (x - abyss->x, abyss->width);
y = abyss->y + GEGL_REMAINDER (y - abyss->y, abyss->height);
break;
case GEGL_ABYSS_BLACK:
{
gfloat color[4] = {0.0, 0.0, 0.0, 1.0};
babl_process (babl_fish (gegl_babl_rgba_linear_float (), sampler->format),
color,
buf,
1);
return;
}
case GEGL_ABYSS_WHITE:
{
gfloat color[4] = {1.0, 1.0, 1.0, 1.0};
babl_process (babl_fish (gegl_babl_rgba_linear_float (),
sampler->format),
color,
buf,
1);
return;
}
default:
case GEGL_ABYSS_NONE:
memset (buf, 0x00, babl_format_get_bytes_per_pixel (sampler->format));
return;
}
}
gegl_buffer_lock (sampler->buffer);
{
gint tile_width = buffer->tile_width;
gint tile_height = buffer->tile_height;
gint tiledy = y + buffer->shift_y;
gint tiledx = x + buffer->shift_x;
gint indice_x = gegl_tile_indice (tiledx, tile_width);
gint indice_y = gegl_tile_indice (tiledy, tile_height);
GeglTile *tile = nearest_sampler->hot_tile;
if (!(tile &&
tile->x == indice_x &&
tile->y == indice_y))
{
g_rec_mutex_lock (&buffer->tile_storage->mutex);
if (tile)
{
gegl_tile_read_unlock (tile);
gegl_tile_unref (tile);
}
tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
indice_x, indice_y,
0);
nearest_sampler->hot_tile = tile;
gegl_tile_read_lock (tile);
g_rec_mutex_unlock (&buffer->tile_storage->mutex);
}
if (tile)
{
gint tile_origin_x = indice_x * tile_width;
gint tile_origin_y = indice_y * tile_height;
gint offsetx = tiledx - tile_origin_x;
gint offsety = tiledy - tile_origin_y;
guchar *tp;
tp = gegl_tile_get_data (tile) +
(offsety * tile_width + offsetx) * nearest_sampler->buffer_bpp;
#if (BABL_MINOR_VERSION >=1) && (BABL_MICRO_VERSION >= 90)
sampler->fish_process
#else
babl_process
#endif
(sampler->fish, (void*)tp, (void*)buf, 1);
}
}
gegl_buffer_unlock (sampler->buffer);
}
static void
gegl_sampler_nearest_get ( GeglSampler* restrict sampler,
const gdouble absolute_x,
const gdouble absolute_y,
GeglBufferMatrix2 *scale,
void* restrict output,
GeglAbyssPolicy repeat_mode)
{
gegl_sampler_get_pixel (sampler,
int_floorf(absolute_x), int_floorf(absolute_y),
output, repeat_mode);
}
static void
gegl_sampler_nearest_prepare (GeglSampler* restrict sampler)
{
if (!sampler->buffer) /* this happens when querying the extent of a sampler */
return;
GEGL_SAMPLER_NEAREST (sampler)->buffer_bpp = babl_format_get_bytes_per_pixel (sampler->buffer->format);
sampler->fish = babl_fish (sampler->buffer->soft_format, sampler->format);
#if (BABL_MINOR_VERSION >=1) && (BABL_MICRO_VERSION >= 90)
sampler->fish_process = babl_fish_get_process (sampler->fish);
#endif
}