gegl-sampler-nearest.c 6.84 KB
Newer Older
1 2 3 4 5
/* 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
6
 * version 3 of the License, or (at your option) any later version.
7 8 9 10 11 12 13
 *
 * 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
14
 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15 16
 *
 */
17

18
#include "config.h"
19
#include <math.h>
20
#include <string.h>
21

22
#include "gegl-buffer.h"
23
#include "gegl-buffer-formats.h"
24 25 26 27 28
#include "gegl-buffer-types.h"
#include "gegl-buffer.h"
#include "gegl-buffer-private.h"
#include "gegl-tile-storage.h"
#include "gegl-tile-backend.h"
29
#include "gegl-sampler-nearest.h"
30

31 32 33 34 35
enum
{
  PROP_0,
  PROP_LAST
};
36

37 38 39
static void
gegl_sampler_nearest_dispose (GObject *self);

40
static void
41 42 43
gegl_sampler_nearest_get (GeglSampler*    restrict self,
                          const gdouble            absolute_x,
                          const gdouble            absolute_y,
44
                          GeglBufferMatrix2       *scale,
45 46 47 48 49
                          void*           restrict output,
                          GeglAbyssPolicy          repeat_mode);

static void
gegl_sampler_nearest_prepare (GeglSampler*    restrict self);
50

51
G_DEFINE_TYPE (GeglSamplerNearest, gegl_sampler_nearest, GEGL_TYPE_SAMPLER)
52 53

static void
54
gegl_sampler_nearest_class_init (GeglSamplerNearestClass *klass)
55
{
56
  GObjectClass     *object_class  = G_OBJECT_CLASS (klass);
57
  GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
58

59 60
  object_class->dispose = gegl_sampler_nearest_dispose;

61
  sampler_class->get = gegl_sampler_nearest_get;
62
  sampler_class->prepare = gegl_sampler_nearest_prepare;
63 64
}

65 66 67 68 69
/*
 * 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.
 */
70
static void
71
gegl_sampler_nearest_init (GeglSamplerNearest *self)
72
{
73 74 75 76
  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;
77 78
}

79 80 81 82 83
static void
gegl_sampler_nearest_dispose (GObject *object)
{
  GeglSamplerNearest *nearest_sampler = GEGL_SAMPLER_NEAREST (object);

84 85 86 87 88 89
  if (nearest_sampler->hot_tile)
    {
      gegl_tile_read_unlock (nearest_sampler->hot_tile);

      g_clear_pointer (&nearest_sampler->hot_tile, gegl_tile_unref);
    }
90 91 92 93

  G_OBJECT_CLASS (gegl_sampler_nearest_parent_class)->dispose (object);
}

94 95 96 97 98 99 100
static void inline
gegl_sampler_get_pixel (GeglSampler    *sampler,
                        gint            x,
                        gint            y,
                        gpointer        data,
                        GeglAbyssPolicy repeat_mode)
{
101
  GeglSamplerNearest *nearest_sampler = (GeglSamplerNearest*)(sampler);
102
  GeglBuffer *buffer = sampler->buffer;
103
  const GeglRectangle *abyss = &buffer->abyss;
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
  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};
126
            babl_process (babl_fish (gegl_babl_rgba_linear_float (), sampler->format),
127 128 129 130 131 132 133 134 135
                          color,
                          buf,
                          1);
            return;
          }

        case GEGL_ABYSS_WHITE:
          {
            gfloat color[4] = {1.0, 1.0, 1.0, 1.0};
136
            babl_process (babl_fish (gegl_babl_rgba_linear_float (),
137
                                     sampler->format),
138 139 140 141 142 143 144 145
                          color,
                          buf,
                          1);
            return;
          }

        default:
        case GEGL_ABYSS_NONE:
146
          memset (buf, 0x00, babl_format_get_bytes_per_pixel (sampler->format));
147 148 149 150
          return;
      }
    }

151 152
  gegl_buffer_lock (sampler->buffer);

153 154 155 156 157
  {
    gint tile_width  = buffer->tile_width;
    gint tile_height = buffer->tile_height;
    gint tiledy      = y + buffer->shift_y;
    gint tiledx      = x + buffer->shift_x;
158 159
    gint indice_x    = gegl_tile_indice (tiledx, tile_width);
    gint indice_y    = gegl_tile_indice (tiledy, tile_height);
160

161
    GeglTile *tile = nearest_sampler->hot_tile;
162 163 164 165 166

    if (!(tile &&
          tile->x == indice_x &&
          tile->y == indice_y))
      {
167
        g_rec_mutex_lock (&buffer->tile_storage->mutex);
168 169

        if (tile)
170 171 172 173 174
          {
            gegl_tile_read_unlock (tile);

            gegl_tile_unref (tile);
          }
175

176 177 178
        tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
                                          indice_x, indice_y,
                                          0);
179 180
        nearest_sampler->hot_tile = tile;

181 182
        gegl_tile_read_lock (tile);

183
        g_rec_mutex_unlock (&buffer->tile_storage->mutex);
184 185 186 187 188 189 190 191 192
      }

    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;

193 194 195 196
        guchar *tp;

        tp = gegl_tile_get_data (tile) +
             (offsety * tile_width + offsetx) * nearest_sampler->buffer_bpp;
197

198
        babl_process (sampler->fish, tp, buf, 1);
199 200
      }
  }
201

202
  gegl_buffer_unlock (sampler->buffer);
203 204
}

205
static void
206
gegl_sampler_nearest_get (      GeglSampler*    restrict  sampler,
207 208
                          const gdouble                   absolute_x,
                          const gdouble                   absolute_y,
209
                                GeglBufferMatrix2        *scale,
210 211
                                void*           restrict  output,
                                GeglAbyssPolicy           repeat_mode)
212
{
213
  gegl_sampler_get_pixel (sampler,
214
           int_floorf(absolute_x), int_floorf(absolute_y),
215
           output, repeat_mode);
216
}
217 218 219 220 221 222 223 224 225


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);

226
  sampler->fish = babl_fish (sampler->buffer->soft_format, sampler->format);
227
}