Blame gegl/buffer/gegl-cache.c

Packit Service 2781ba
/* This file is part of GEGL editor -- a gtk frontend for GEGL
Packit Service 2781ba
 *
Packit Service 2781ba
 * This program is free software; you can redistribute it and/or modify
Packit Service 2781ba
 * it under the terms of the GNU General Public License as published by
Packit Service 2781ba
 * the Free Software Foundation; either version 3 of the License, or
Packit Service 2781ba
 * (at your option) any later version.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This program is distributed in the hope that it will be useful,
Packit Service 2781ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2781ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 2781ba
 * GNU General Public License for more details.
Packit Service 2781ba
 *
Packit Service 2781ba
 * You should have received a copy of the GNU General Public License
Packit Service 2781ba
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit Service 2781ba
 *
Packit Service 2781ba
 * Copyright (C) 2003, 2004, 2006 Øyvind Kolås
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
Packit Service 2781ba
#include <string.h>
Packit Service 2781ba
Packit Service 2781ba
#include <glib-object.h>
Packit Service 2781ba
Packit Service 2781ba
#include <babl/babl.h>
Packit Service 2781ba
Packit Service 2781ba
#include "gegl.h"
Packit Service 2781ba
#include "gegl-types-internal.h"
Packit Service 2781ba
#include "gegl-utils.h"
Packit Service 2781ba
Packit Service 2781ba
#include "graph/gegl-node.h"
Packit Service 2781ba
Packit Service 2781ba
#include "gegl-cache.h"
Packit Service 2781ba
#include "gegl-region.h"
Packit Service 2781ba
Packit Service 2781ba
enum
Packit Service 2781ba
{
Packit Service 2781ba
  PROP_0,
Packit Service 2781ba
  PROP_X,
Packit Service 2781ba
  PROP_Y,
Packit Service 2781ba
  PROP_WIDTH,
Packit Service 2781ba
  PROP_HEIGHT,
Packit Service 2781ba
  PROP_NODE
Packit Service 2781ba
};
Packit Service 2781ba
Packit Service 2781ba
enum
Packit Service 2781ba
{
Packit Service 2781ba
  INVALIDATED,
Packit Service 2781ba
  COMPUTED,
Packit Service 2781ba
  LAST_SIGNAL
Packit Service 2781ba
};
Packit Service 2781ba
Packit Service 2781ba
static void            finalize     (GObject      *object);
Packit Service 2781ba
static void            dispose      (GObject      *object);
Packit Service 2781ba
static void            set_property (GObject      *object,
Packit Service 2781ba
                                     guint         prop_id,
Packit Service 2781ba
                                     const GValue *value,
Packit Service 2781ba
                                     GParamSpec   *pspec);
Packit Service 2781ba
static void            get_property (GObject      *object,
Packit Service 2781ba
                                     guint         prop_id,
Packit Service 2781ba
                                     GValue       *value,
Packit Service 2781ba
                                     GParamSpec   *pspec);
Packit Service 2781ba
Packit Service 2781ba
G_DEFINE_TYPE (GeglCache, gegl_cache, GEGL_TYPE_BUFFER)
Packit Service 2781ba
Packit Service 2781ba
guint gegl_cache_signals[LAST_SIGNAL] = { 0 };
Packit Service 2781ba
Packit Service 2781ba
static GObject *
Packit Service 2781ba
gegl_cache_constructor (GType                  type,
Packit Service 2781ba
                        guint                  n_params,
Packit Service 2781ba
                        GObjectConstructParam *params)
Packit Service 2781ba
{
Packit Service 2781ba
  GObject   *object;
Packit Service 2781ba
  GeglCache *self;
Packit Service 2781ba
Packit Service 2781ba
  object = G_OBJECT_CLASS (gegl_cache_parent_class)->constructor (type,
Packit Service 2781ba
                                                                  n_params,
Packit Service 2781ba
                                                                  params);
Packit Service 2781ba
  self = GEGL_CACHE (object);
Packit Service 2781ba
Packit Service 2781ba
  self->valid_region = gegl_region_new ();
Packit Service 2781ba
  self->format       = GEGL_BUFFER (self)->format;
Packit Service 2781ba
Packit Service 2781ba
  return object;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/* expand invalidated regions to be align with coordinates divisible by 8 in both
Packit Service 2781ba
 * directions. This improves improves the performance of GeglProcessor when it
Packit Service 2781ba
 * iterates the resulting dirtied rectangles in the GeglRegion.
Packit Service 2781ba
 */
Packit Service 2781ba
static GeglRectangle gegl_rectangle_expand (const GeglRectangle *rectangle)
Packit Service 2781ba
{
Packit Service 2781ba
  gint align = 8;
Packit Service 2781ba
  GeglRectangle expanded = *rectangle;
Packit Service 2781ba
  gint xdiff;
Packit Service 2781ba
  gint ydiff;
Packit Service 2781ba
Packit Service 2781ba
  if (gegl_rectangle_is_infinite_plane (rectangle))
Packit Service 2781ba
    return *rectangle;
Packit Service 2781ba
Packit Service 2781ba
  xdiff = expanded.x % align;
Packit Service 2781ba
  if (xdiff < 0)
Packit Service 2781ba
    xdiff = align + xdiff;
Packit Service 2781ba
  expanded.width += xdiff;
Packit Service 2781ba
  expanded.x -= xdiff;
Packit Service 2781ba
  xdiff = align -(expanded.width % align);
Packit Service 2781ba
  expanded.width += xdiff;
Packit Service 2781ba
Packit Service 2781ba
  ydiff = expanded.y % align;
Packit Service 2781ba
  if (ydiff < 0)
Packit Service 2781ba
    ydiff = align + ydiff;
Packit Service 2781ba
  expanded.height += ydiff;
Packit Service 2781ba
  expanded.y -= ydiff;
Packit Service 2781ba
  ydiff = align -(expanded.height % align);
Packit Service 2781ba
  expanded.height += ydiff;
Packit Service 2781ba
Packit Service 2781ba
  return expanded;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_cache_class_init (GeglCacheClass *klass)
Packit Service 2781ba
{
Packit Service 2781ba
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit Service 2781ba
Packit Service 2781ba
  gobject_class->constructor  = gegl_cache_constructor;
Packit Service 2781ba
  gobject_class->finalize     = finalize;
Packit Service 2781ba
  gobject_class->dispose      = dispose;
Packit Service 2781ba
  gobject_class->set_property = set_property;
Packit Service 2781ba
  gobject_class->get_property = get_property;
Packit Service 2781ba
Packit Service 2781ba
  g_object_class_install_property (gobject_class, PROP_NODE,
Packit Service 2781ba
                                   g_param_spec_object ("node",
Packit Service 2781ba
                                                        "GeglNode",
Packit Service 2781ba
                                                        "The GeglNode to cache results for",
Packit Service 2781ba
                                                        GEGL_TYPE_NODE,
Packit Service 2781ba
                                                        G_PARAM_WRITABLE |
Packit Service 2781ba
                                                        G_PARAM_CONSTRUCT_ONLY));
Packit Service 2781ba
Packit Service 2781ba
  /* overriding pspecs for properties in parent class */
Packit Service 2781ba
  g_object_class_install_property (gobject_class, PROP_X,
Packit Service 2781ba
                                   g_param_spec_int ("x", "x",
Packit Service 2781ba
                                                     "local origin's offset relative to source origin",
Packit Service 2781ba
                                                     G_MININT / 2, G_MAXINT / 2, -4096,
Packit Service 2781ba
                                                     G_PARAM_READWRITE |
Packit Service 2781ba
                                                     G_PARAM_CONSTRUCT_ONLY));
Packit Service 2781ba
Packit Service 2781ba
  g_object_class_install_property (gobject_class, PROP_Y,
Packit Service 2781ba
                                   g_param_spec_int ("y", "y",
Packit Service 2781ba
                                                     "local origin's offset relative to source origin",
Packit Service 2781ba
                                                     G_MININT / 2, G_MAXINT / 2, -4096,
Packit Service 2781ba
                                                     G_PARAM_READWRITE |
Packit Service 2781ba
                                                     G_PARAM_CONSTRUCT_ONLY));
Packit Service 2781ba
Packit Service 2781ba
  g_object_class_install_property (gobject_class, PROP_WIDTH,
Packit Service 2781ba
                                   g_param_spec_int ("width", "width",
Packit Service 2781ba
                                                     "pixel width of buffer",
Packit Service 2781ba
                                                     -1, G_MAXINT, 10240 * 4,
Packit Service 2781ba
                                                     G_PARAM_READWRITE |
Packit Service 2781ba
                                                     G_PARAM_CONSTRUCT_ONLY));
Packit Service 2781ba
Packit Service 2781ba
  g_object_class_install_property (gobject_class, PROP_HEIGHT,
Packit Service 2781ba
                                   g_param_spec_int ("height", "height",
Packit Service 2781ba
                                                     "pixel height of buffer",
Packit Service 2781ba
                                                     -1, G_MAXINT, 10240 * 4,
Packit Service 2781ba
                                                     G_PARAM_READWRITE |
Packit Service 2781ba
                                                     G_PARAM_CONSTRUCT_ONLY));
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
  gegl_cache_signals[COMPUTED] =
Packit Service 2781ba
    g_signal_new ("computed",
Packit Service 2781ba
                  G_TYPE_FROM_CLASS (klass),
Packit Service 2781ba
                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
Packit Service 2781ba
                  0,
Packit Service 2781ba
                  NULL, NULL,
Packit Service 2781ba
                  g_cclosure_marshal_VOID__BOXED,
Packit Service 2781ba
                  G_TYPE_NONE, 1,
Packit Service 2781ba
                  GEGL_TYPE_RECTANGLE);
Packit Service 2781ba
Packit Service 2781ba
  gegl_cache_signals[INVALIDATED] =
Packit Service 2781ba
    g_signal_new ("invalidated",
Packit Service 2781ba
                  G_TYPE_FROM_CLASS (klass),
Packit Service 2781ba
                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
Packit Service 2781ba
                  0,
Packit Service 2781ba
                  NULL, NULL,
Packit Service 2781ba
                  g_cclosure_marshal_VOID__BOXED,
Packit Service 2781ba
                  G_TYPE_NONE, 1,
Packit Service 2781ba
                  GEGL_TYPE_RECTANGLE);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_cache_init (GeglCache *self)
Packit Service 2781ba
{
Packit Service 2781ba
  self->node = NULL;
Packit Service 2781ba
  self->mutex = g_mutex_new ();
Packit Service 2781ba
Packit Service 2781ba
  /* thus providing a default value for GeglCache, that overrides the NULL
Packit Service 2781ba
   * from GeglBuffer */
Packit Service 2781ba
  GEGL_BUFFER (self)->format = (gpointer) babl_format ("R'G'B'A u8");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
dispose (GObject *gobject)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglCache *self = GEGL_CACHE (gobject);
Packit Service 2781ba
Packit Service 2781ba
  while (g_idle_remove_by_data (gobject)) ;
Packit Service 2781ba
Packit Service 2781ba
  /* Check with GEGL_IS_NODE since sometimes the node is destroyed
Packit Service 2781ba
   * before we get here
Packit Service 2781ba
   */
Packit Service 2781ba
  if (GEGL_IS_NODE (self->node))
Packit Service 2781ba
    {
Packit Service 2781ba
      gint handler = g_signal_handler_find (self->node, G_SIGNAL_MATCH_DATA,
Packit Service 2781ba
                                            g_signal_lookup ("invalidated",
Packit Service 2781ba
                                                             GEGL_TYPE_NODE),
Packit Service 2781ba
                                            0, NULL, NULL, self);
Packit Service 2781ba
      if (handler)
Packit Service 2781ba
        {
Packit Service 2781ba
          g_signal_handler_disconnect (self->node, handler);
Packit Service 2781ba
        }
Packit Service 2781ba
      self->node = NULL;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  G_OBJECT_CLASS (gegl_cache_parent_class)->dispose (gobject);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
finalize (GObject *gobject)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglCache *self = GEGL_CACHE (gobject);
Packit Service 2781ba
Packit Service 2781ba
  g_mutex_free (self->mutex);
Packit Service 2781ba
  if (self->valid_region)
Packit Service 2781ba
    gegl_region_destroy (self->valid_region);
Packit Service 2781ba
  G_OBJECT_CLASS (gegl_cache_parent_class)->finalize (gobject);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
node_invalidated (GeglNode            *source,
Packit Service 2781ba
                  const GeglRectangle *rect,
Packit Service 2781ba
                  gpointer             data)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglCache *cache = GEGL_CACHE (data);
Packit Service 2781ba
  GeglRectangle expanded = gegl_rectangle_expand (rect);
Packit Service 2781ba
Packit Service 2781ba
  {
Packit Service 2781ba
    GeglRegion *region;
Packit Service 2781ba
    region = gegl_region_rectangle (&expanded);
Packit Service 2781ba
    gegl_region_subtract (cache->valid_region, region);
Packit Service 2781ba
    gegl_region_destroy (region);
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  g_mutex_lock (cache->mutex);
Packit Service 2781ba
  g_signal_emit_by_name (cache, "invalidated", &expanded, NULL);
Packit Service 2781ba
  g_mutex_unlock (cache->mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
set_property (GObject      *gobject,
Packit Service 2781ba
              guint         property_id,
Packit Service 2781ba
              const GValue *value,
Packit Service 2781ba
              GParamSpec   *pspec)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglCache *self = GEGL_CACHE (gobject);
Packit Service 2781ba
Packit Service 2781ba
  switch (property_id)
Packit Service 2781ba
    {
Packit Service 2781ba
      case PROP_NODE:
Packit Service 2781ba
        g_mutex_lock (self->mutex);
Packit Service 2781ba
        if (self->node)
Packit Service 2781ba
          {
Packit Service 2781ba
            gulong handler;
Packit Service 2781ba
            handler = g_signal_handler_find (self->node, G_SIGNAL_MATCH_DATA,
Packit Service 2781ba
                                             g_signal_lookup ("invalidated",
Packit Service 2781ba
                                                              GEGL_TYPE_NODE),
Packit Service 2781ba
                                             0, NULL, NULL, self);
Packit Service 2781ba
            if (handler)
Packit Service 2781ba
              {
Packit Service 2781ba
                g_signal_handler_disconnect (self->node, handler);
Packit Service 2781ba
              }
Packit Service 2781ba
          }
Packit Service 2781ba
        /* just getting the node, the cache holds no reference on the node,
Packit Service 2781ba
         * it is the node that holds reference on the cache
Packit Service 2781ba
         */
Packit Service 2781ba
        self->node = GEGL_NODE (g_value_get_object (value));
Packit Service 2781ba
        g_signal_connect (G_OBJECT (self->node), "invalidated",
Packit Service 2781ba
                          G_CALLBACK (node_invalidated), self);
Packit Service 2781ba
        g_mutex_unlock (self->mutex);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_X:
Packit Service 2781ba
        g_object_set_property (gobject, "GeglBuffer::x", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_Y:
Packit Service 2781ba
        g_object_set_property (gobject, "GeglBuffer::y", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_WIDTH:
Packit Service 2781ba
        g_object_set_property (gobject, "GeglBuffer::width", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_HEIGHT:
Packit Service 2781ba
        g_object_set_property (gobject, "GeglBuffer::height", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      default:
Packit Service 2781ba
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
Packit Service 2781ba
        break;
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
get_property (GObject    *gobject,
Packit Service 2781ba
              guint       property_id,
Packit Service 2781ba
              GValue     *value,
Packit Service 2781ba
              GParamSpec *pspec)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglCache *self = GEGL_CACHE (gobject);
Packit Service 2781ba
Packit Service 2781ba
  switch (property_id)
Packit Service 2781ba
    {
Packit Service 2781ba
      case PROP_NODE:
Packit Service 2781ba
        g_value_set_object (value, self->node);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
        /* For the rest, upchaining to the property implementation in GeglBuffer */
Packit Service 2781ba
      case PROP_X:
Packit Service 2781ba
        g_object_get_property (gobject, "GeglBuffer::x", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_Y:
Packit Service 2781ba
        g_object_get_property (gobject, "GeglBuffer::y", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_WIDTH:
Packit Service 2781ba
        g_object_get_property (gobject, "GeglBuffer::width", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      case PROP_HEIGHT:
Packit Service 2781ba
        g_object_get_property (gobject, "GeglBuffer::height", value);
Packit Service 2781ba
        break;
Packit Service 2781ba
Packit Service 2781ba
      default:
Packit Service 2781ba
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
Packit Service 2781ba
        break;
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
#if 0
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_buffer_clear (GeglBuffer    *buffer,
Packit Service 2781ba
                   GeglRectangle *rectangle)
Packit Service 2781ba
{
Packit Service 2781ba
  gint pixels = rectangle->width * rectangle->height;
Packit Service 2781ba
  guchar *buf = g_malloc (pixels * 4);
Packit Service 2781ba
  gint i;
Packit Service 2781ba
Packit Service 2781ba
  for (i=0;i
Packit Service 2781ba
    {
Packit Service 2781ba
      buf[i*4+0]=25;
Packit Service 2781ba
      buf[i*4+1]=0;
Packit Service 2781ba
      buf[i*4+2]=25;
Packit Service 2781ba
      buf[i*4+3]=40;
Packit Service 2781ba
    }
Packit Service 2781ba
  gegl_buffer_set (buffer, rectangle, babl_format ("RGBA u8"), buf);
Packit Service 2781ba
  g_free (buf);
Packit Service 2781ba
}
Packit Service 2781ba
#endif
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_cache_invalidate (GeglCache           *self,
Packit Service 2781ba
                       const GeglRectangle *roi)
Packit Service 2781ba
{
Packit Service 2781ba
#if 0
Packit Service 2781ba
  if (roi)
Packit Service 2781ba
    {
Packit Service 2781ba
      gegl_buffer_clear (GEGL_BUFFER (self), roi);
Packit Service 2781ba
    }
Packit Service 2781ba
  else
Packit Service 2781ba
    {
Packit Service 2781ba
      g_warning ("XXX: full invalidation of GeglCache NYI\n");
Packit Service 2781ba
    }
Packit Service 2781ba
#endif
Packit Service 2781ba
Packit Service 2781ba
  g_mutex_lock (self->mutex);
Packit Service 2781ba
Packit Service 2781ba
  if (roi)
Packit Service 2781ba
    {
Packit Service 2781ba
      GeglRectangle expanded = gegl_rectangle_expand (roi);
Packit Service 2781ba
Packit Service 2781ba
      GeglRegion *temp_region;
Packit Service 2781ba
      temp_region = gegl_region_rectangle (&expanded);
Packit Service 2781ba
      gegl_region_subtract (self->valid_region, temp_region);
Packit Service 2781ba
      gegl_region_destroy (temp_region);
Packit Service 2781ba
      g_signal_emit (self, gegl_cache_signals[INVALIDATED], 0,
Packit Service 2781ba
                     roi, NULL);
Packit Service 2781ba
    }
Packit Service 2781ba
  else
Packit Service 2781ba
    {
Packit Service 2781ba
      GeglRectangle rect = { 0, 0, 0, 0 }; /* should probably be the extent of the cache */
Packit Service 2781ba
      if (self->valid_region)
Packit Service 2781ba
        gegl_region_destroy (self->valid_region);
Packit Service 2781ba
      self->valid_region = gegl_region_new ();
Packit Service 2781ba
      g_signal_emit (self, gegl_cache_signals[INVALIDATED], 0,
Packit Service 2781ba
                     &rect, NULL);
Packit Service 2781ba
    }
Packit Service 2781ba
  g_mutex_unlock (self->mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_cache_computed (GeglCache           *self,
Packit Service 2781ba
                     const GeglRectangle *rect)
Packit Service 2781ba
{
Packit Service 2781ba
  g_return_if_fail (GEGL_IS_CACHE (self));
Packit Service 2781ba
  g_return_if_fail (rect != NULL);
Packit Service 2781ba
Packit Service 2781ba
  g_mutex_lock (self->mutex);
Packit Service 2781ba
  gegl_region_union_with_rect (self->valid_region, rect);
Packit Service 2781ba
  g_signal_emit (self, gegl_cache_signals[COMPUTED], 0, rect, NULL);
Packit Service 2781ba
  g_mutex_unlock (self->mutex);
Packit Service 2781ba
}