[gimp] app: use GEGL for transform-tools preview



commit 7f56393138d6da3d42c6b62cbfbf78f8101c2670
Author: Ell <ell_se yahoo com>
Date:   Mon Jan 15 12:17:13 2018 -0500

    app: use GEGL for transform-tools preview
    
    Before you get too exceited -- no, this commit doesn't integrate
    transform previews into the image graph :)  We still use a
    separate canvas-item overlay, just like before, but instead of
    using an impromptu implementation to render the preview, we use
    gegl:transform.  We properly adjust the matrix passed to the op
    according to the display scale, so that we still render only as
    much as needed.
    
    This is both notably faster than the current code, and, for
    perspective transforms, more accurate.

 app/display/gimpcanvastransformpreview.c | 1014 ++++++------------------------
 1 files changed, 205 insertions(+), 809 deletions(-)
---
diff --git a/app/display/gimpcanvastransformpreview.c b/app/display/gimpcanvastransformpreview.c
index f8a262a..7786c05 100644
--- a/app/display/gimpcanvastransformpreview.c
+++ b/app/display/gimpcanvastransformpreview.c
@@ -35,19 +35,13 @@
 #include "core/gimp-transform-utils.h"
 #include "core/gimp-utils.h"
 
+#include "gegl/gimp-gegl-nodes.h"
+
 #include "gimpcanvas.h"
 #include "gimpcanvastransformpreview.h"
 #include "gimpdisplayshell.h"
 
 
-#define INT_MULT(a,b,t)    ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
-#define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \
-                           ((((t) >> 7) + (t)) >> 16))
-
-#define MAX_SUB_COLS       6 /* number of columns and  */
-#define MAX_SUB_ROWS       6 /* rows to use in perspective preview subdivision */
-
-
 enum
 {
   PROP_0,
@@ -65,11 +59,23 @@ typedef struct _GimpCanvasTransformPreviewPrivate GimpCanvasTransformPreviewPriv
 
 struct _GimpCanvasTransformPreviewPrivate
 {
-  GimpDrawable      *drawable;
-  GimpMatrix3        transform;
-  gdouble            x1, y1;
-  gdouble            x2, y2;
-  gdouble            opacity;
+  GimpDrawable *drawable;
+  GimpMatrix3   transform;
+  gdouble       x1, y1;
+  gdouble       x2, y2;
+  gdouble       opacity;
+
+  GeglNode     *node;
+  GeglNode     *source_node;
+  GeglNode     *convert_format_node;
+  GeglNode     *mask_source_node;
+  GeglNode     *opacity_node;
+  GeglNode     *cache_node;
+  GeglNode     *transform_node;
+  GimpDrawable *node_drawable;
+  GimpDrawable *node_mask;
+  gdouble       node_opacity;
+  GimpMatrix3   node_matrix;
 };
 
 #define GET_PRIVATE(transform_preview) \
@@ -80,6 +86,7 @@ struct _GimpCanvasTransformPreviewPrivate
 
 /*  local function prototypes  */
 
+static void             gimp_canvas_transform_preview_dispose      (GObject        *object);
 static void             gimp_canvas_transform_preview_set_property (GObject        *object,
                                                                     guint           property_id,
                                                                     const GValue   *value,
@@ -93,63 +100,7 @@ static void             gimp_canvas_transform_preview_draw         (GimpCanvasIt
                                                                     cairo_t        *cr);
 static cairo_region_t * gimp_canvas_transform_preview_get_extents  (GimpCanvasItem *item);
 
-static void   gimp_canvas_transform_preview_draw_quad         (GimpDrawable    *texture,
-                                                               cairo_t         *cr,
-                                                               GimpChannel     *mask,
-                                                               gint             mask_offx,
-                                                               gint             mask_offy,
-                                                               gint            *x,
-                                                               gint            *y,
-                                                               gfloat          *u,
-                                                               gfloat          *v,
-                                                               guchar           opacity);
-static void   gimp_canvas_transform_preview_draw_tri          (GimpDrawable    *texture,
-                                                               cairo_t         *cr,
-                                                               cairo_surface_t *area,
-                                                               gint             area_offx,
-                                                               gint             area_offy,
-                                                               GimpChannel     *mask,
-                                                               gint             mask_offx,
-                                                               gint             mask_offy,
-                                                               gint            *x,
-                                                               gint            *y,
-                                                               gfloat          *u,
-                                                               gfloat          *v,
-                                                               guchar           opacity);
-static void   gimp_canvas_transform_preview_draw_tri_row      (GimpDrawable    *texture,
-                                                               cairo_t         *cr,
-                                                               cairo_surface_t *area,
-                                                               gint             area_offx,
-                                                               gint             area_offy,
-                                                               gint             x1,
-                                                               gfloat           u1,
-                                                               gfloat           v1,
-                                                               gint             x2,
-                                                               gfloat           u2,
-                                                               gfloat           v2,
-                                                               gint             y,
-                                                               guchar           opacity);
-static void   gimp_canvas_transform_preview_draw_tri_row_mask (GimpDrawable    *texture,
-                                                               cairo_t         *cr,
-                                                               cairo_surface_t *area,
-                                                               gint             area_offx,
-                                                               gint             area_offy,
-                                                               GimpChannel     *mask,
-                                                               gint             mask_offx,
-                                                               gint             mask_offy,
-                                                               gint             x1,
-                                                               gfloat           u1,
-                                                               gfloat           v1,
-                                                               gint             x2,
-                                                               gfloat           u2,
-                                                               gfloat           v2,
-                                                               gint             y,
-                                                               guchar           opacity);
-static void   gimp_canvas_transform_preview_trace_tri_edge    (gint            *dest,
-                                                               gint             x1,
-                                                               gint             y1,
-                                                               gint             x2,
-                                                               gint             y2);
+static void             gimp_canvas_transform_preview_sync_node    (GimpCanvasItem *item);
 
 
 G_DEFINE_TYPE (GimpCanvasTransformPreview, gimp_canvas_transform_preview,
@@ -158,12 +109,16 @@ G_DEFINE_TYPE (GimpCanvasTransformPreview, gimp_canvas_transform_preview,
 #define parent_class gimp_canvas_transform_preview_parent_class
 
 
+/* private functions */
+
+
 static void
 gimp_canvas_transform_preview_class_init (GimpCanvasTransformPreviewClass *klass)
 {
   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
   GimpCanvasItemClass *item_class   = GIMP_CANVAS_ITEM_CLASS (klass);
 
+  object_class->dispose      = gimp_canvas_transform_preview_dispose;
   object_class->set_property = gimp_canvas_transform_preview_set_property;
   object_class->get_property = gimp_canvas_transform_preview_get_property;
 
@@ -229,6 +184,16 @@ gimp_canvas_transform_preview_init (GimpCanvasTransformPreview *transform_previe
 }
 
 static void
+gimp_canvas_transform_preview_dispose (GObject *object)
+{
+  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
+
+  g_clear_object (&private->node);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
 gimp_canvas_transform_preview_set_property (GObject      *object,
                                             guint         property_id,
                                             const GValue *value,
@@ -389,156 +354,64 @@ gimp_canvas_transform_preview_draw (GimpCanvasItem *item,
                                     cairo_t        *cr)
 {
   GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
-  GimpChannel                       *mask;
-  gint                               mask_x1, mask_y1;
-  gint                               mask_x2, mask_y2;
-  gint                               mask_offx, mask_offy;
-  gint                               columns, rows;
-  gint                               j, k, sub;
-
-   /* x and y get filled with the screen coordinates of each corner of
-    * each quadrilateral subdivision of the transformed area. u and v
-    * are the corresponding points in the mask
-    */
-  gfloat                             du, dv, dx, dy;
-  gint                               x[MAX_SUB_COLS * MAX_SUB_ROWS][4];
-  gint                               y[MAX_SUB_COLS * MAX_SUB_ROWS][4];
-  gfloat                             u[MAX_SUB_COLS * MAX_SUB_ROWS][4];
-  gfloat                             v[MAX_SUB_COLS * MAX_SUB_ROWS][4];
-  guchar                             opacity;
-
-  opacity = private->opacity * 255.999;
+  GimpDisplayShell                  *shell   = gimp_canvas_item_get_shell (item);
+  cairo_rectangle_int_t              extents;
+  gdouble                            clip_x1, clip_y1;
+  gdouble                            clip_x2, clip_y2;
+  GeglRectangle                      bounds;
+  cairo_surface_t                   *surface;
+  guchar                            *surface_data;
+  gint                               surface_stride;
 
   /* only draw convex polygons */
-  if (! gimp_canvas_transform_preview_transform (item, NULL))
+  if (! gimp_canvas_transform_preview_transform (item, &extents))
     return;
 
-  mask      = NULL;
-  mask_offx = 0;
-  mask_offy = 0;
-
-  if (gimp_item_mask_bounds (GIMP_ITEM (private->drawable),
-                             &mask_x1, &mask_y1,
-                             &mask_x2, &mask_y2))
-    {
-      GimpImage *image = gimp_item_get_image (GIMP_ITEM (private->drawable));
-
-      mask = gimp_image_get_mask (image);
-
-      gimp_item_get_offset (GIMP_ITEM (private->drawable),
-                            &mask_offx, &mask_offy);
-    }
-
-  if (! gimp_matrix3_is_affine (&private->transform))
-    {
-      /* approximate perspective transform by subdivision
-       *
-       * increase number of columns/rows to increase precision
-       */
-      columns = MAX_SUB_COLS;
-      rows    = MAX_SUB_ROWS;
-    }
-  else
-    {
-      /*  for affine transforms subdivision has no effect
-       */
-      columns = 1;
-      rows    = 1;
-    }
+  cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
 
-  dx = (private->x2 - private->x1) / ((gfloat) columns);
-  dy = (private->y2 - private->y1) / ((gfloat) rows);
-
-  du = (mask_x2 - mask_x1) / ((gfloat) columns);
-  dv = (mask_y2 - mask_y1) / ((gfloat) rows);
-
-#define CALC_VERTEX(col, row, sub, index)                       \
-  {                                                             \
-    gdouble tx1, ty1;                                           \
-    gdouble tx2, ty2;                                           \
-                                                                \
-    tx2 = private->x1 + (dx * (col + (index & 1)));             \
-    ty2 = private->y1 + (dy * (row + (index >> 1)));            \
-                                                                \
-    gimp_matrix3_transform_point (&private->transform,          \
-                                  tx2, ty2,                     \
-                                  &tx1, &ty1);                  \
-                                                                \
-    gimp_canvas_item_transform_xy_f (item,                      \
-                                     tx1, ty1,                  \
-                                     &tx2, &ty2);               \
-    x[sub][index] = (gint) tx2;                                 \
-    y[sub][index] = (gint) ty2;                                 \
-                                                                \
-    u[sub][index] = mask_x1 + (du * (col + (index & 1)));       \
-    v[sub][index] = mask_y1 + (dv * (row + (index >> 1)));      \
-  }
-
-#define COPY_VERTEX(subdest, idest, subsrc, isrc) \
-  x[subdest][idest] = x[subsrc][isrc];            \
-  y[subdest][idest] = y[subsrc][isrc];            \
-  u[subdest][idest] = u[subsrc][isrc];            \
-  v[subdest][idest] = v[subsrc][isrc];
-
-  /*
-   * upper left corner subdivision: calculate all vertices
-   */
-
-  for (j = 0; j < 4; j++)
+  clip_x1 = floor (clip_x1);
+  clip_y1 = floor (clip_y1);
+  clip_x2 = ceil  (clip_x2);
+  clip_y2 = ceil  (clip_y2);
+
+  if (! gegl_rectangle_intersect (&bounds,
+                                  GEGL_RECTANGLE (extents.x,
+                                                  extents.y,
+                                                  extents.width,
+                                                  extents.height),
+                                  GEGL_RECTANGLE (clip_x1,
+                                                  clip_y1,
+                                                  clip_x2 - clip_x1,
+                                                  clip_y2 - clip_y1)))
     {
-      CALC_VERTEX (0, 0, 0, j);
+      return;
     }
 
-  /*
-   * top row subdivisions: calculate only right side vertices
-   */
-
-  for (j = 1; j < columns; j++)
-    {
-      COPY_VERTEX (j, 0, j - 1, 1);
-      CALC_VERTEX (j, 0, j, 1);
-      COPY_VERTEX (j, 2, j - 1, 3);
-      CALC_VERTEX (j, 0, j, 3);
-    }
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        bounds.width, bounds.height);
 
-  /*
-   * left column subdivisions: calculate only bottom side vertices
-   */
+  g_return_if_fail (surface != NULL);
 
-  for (j = 1, sub = columns; j < rows; j++, sub += columns)
-    {
-      COPY_VERTEX (sub, 0, sub - columns, 2);
-      COPY_VERTEX (sub, 1, sub - columns, 3);
-      CALC_VERTEX (0, j, sub, 2);
-      CALC_VERTEX (0, j, sub, 3);
-    }
+  surface_data   = cairo_image_surface_get_data (surface);
+  surface_stride = cairo_image_surface_get_stride (surface);
 
-  /*
-   * the rest: calculate only the bottom-right vertex
-   */
+  gimp_canvas_transform_preview_sync_node (item);
 
-  for (j = 1, sub = columns; j < rows; j++)
-    {
-      sub++;
+  gegl_node_blit (private->transform_node, 1.0,
+                  GEGL_RECTANGLE (bounds.x + shell->offset_x,
+                                  bounds.y + shell->offset_y,
+                                  bounds.width,
+                                  bounds.height),
+                  babl_format ("cairo-ARGB32"), surface_data, surface_stride,
+                  GEGL_BLIT_CACHE);
 
-      for (k = 1; k < columns; k++, sub++)
-        {
-          COPY_VERTEX (sub, 0, sub - 1, 1);
-          COPY_VERTEX (sub, 1, sub - columns, 3);
-          COPY_VERTEX (sub, 2, sub - 1, 3);
-          CALC_VERTEX (k, j, sub, 3);
-        }
-    }
+  cairo_surface_mark_dirty (surface);
 
-#undef CALC_VERTEX
-#undef COPY_VERTEX
+  cairo_set_source_surface (cr, surface, bounds.x, bounds.y);
+  cairo_rectangle (cr, bounds.x, bounds.y, bounds.width, bounds.height);
+  cairo_fill (cr);
 
-  k = columns * rows;
-  for (j = 0; j < k; j++)
-    gimp_canvas_transform_preview_draw_quad (private->drawable, cr,
-                                             mask, mask_offx, mask_offy,
-                                             x[j], y[j], u[j], v[j],
-                                             opacity);
+  cairo_surface_destroy (surface);
 }
 
 static cairo_region_t *
@@ -552,649 +425,172 @@ gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item)
   return NULL;
 }
 
-GimpCanvasItem *
-gimp_canvas_transform_preview_new (GimpDisplayShell  *shell,
-                                   GimpDrawable      *drawable,
-                                   const GimpMatrix3 *transform,
-                                   gdouble            x1,
-                                   gdouble            y1,
-                                   gdouble            x2,
-                                   gdouble            y2)
-{
-  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
-  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
-  g_return_val_if_fail (transform != NULL, NULL);
-
-  return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW,
-                       "shell",     shell,
-                       "drawable",  drawable,
-                       "transform", transform,
-                       "x1",        x1,
-                       "y1",        y1,
-                       "x2",        x2,
-                       "y2",        y2,
-                       NULL);
-}
-
-
-/*  private functions  */
-
-/**
- * gimp_canvas_transform_preview_draw_quad:
- * @texture:   the #GimpDrawable to be previewed
- * @cr:        the #cairo_t to draw to
- * @mask:      a #GimpChannel
- * @opacity:   the opacity of the preview
- *
- * Take a quadrilateral, divide it into two triangles, and draw those
- * with gimp_canvas_transform_preview_draw_tri().
- **/
 static void
-gimp_canvas_transform_preview_draw_quad (GimpDrawable *texture,
-                                         cairo_t      *cr,
-                                         GimpChannel  *mask,
-                                         gint          mask_offx,
-                                         gint          mask_offy,
-                                         gint         *x,
-                                         gint         *y,
-                                         gfloat       *u,
-                                         gfloat       *v,
-                                         guchar        opacity)
+gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
 {
-  gint    x2[3], y2[3];
-  gfloat  u2[3], v2[3];
-  gint    minx, maxx, miny, maxy; /* screen bounds of the quad        */
-  gdouble clip_x1, clip_y1, clip_x2, clip_y2;
-  gint    c;
-
-  x2[0] = x[3];  y2[0] = y[3];  u2[0] = u[3];  v2[0] = v[3];
-  x2[1] = x[2];  y2[1] = y[2];  u2[1] = u[2];  v2[1] = v[2];
-  x2[2] = x[1];  y2[2] = y[1];  u2[2] = u[1];  v2[2] = v[1];
-
-   /* Allocate a box around the quad to compute preview data into
-    * and fill it with the original window contents.
-    */
-
-  cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
-
-  /* find bounds of quad in order to only grab as much of dest as needed */
+  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
+  GimpDisplayShell                  *shell   = gimp_canvas_item_get_shell (item);
+  GimpImage                         *image   = gimp_canvas_item_get_image (item);
+  gint                               offset_x, offset_y;
+  GimpMatrix3                        matrix;
+  gboolean                           has_mask;
 
-  minx = maxx = x[0];
-  miny = maxy = y[0];
-  for (c = 1; c < 4; c++)
+  if (! private->node)
     {
-      if (x[c] < minx) minx = x[c];
-      else if (x[c] > maxx) maxx = x[c];
-
-      if (y[c] < miny) miny = y[c];
-      else if (y[c] > maxy) maxy = y[c];
+      private->node = gegl_node_new ();
+
+      private->source_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gimp:buffer-source-validate",
+                             NULL);
+
+      private->convert_format_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gegl:convert-format",
+                             NULL);
+
+      private->mask_source_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gimp:buffer-source-validate",
+                             NULL);
+
+      private->opacity_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gegl:opacity",
+                             NULL);
+
+      private->cache_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gegl:cache",
+                             NULL);
+
+      private->transform_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gegl:transform",
+                             "sampler",   GIMP_INTERPOLATION_NONE,
+                             NULL);
+
+      gegl_node_link_many (private->source_node,
+                           private->convert_format_node,
+                           private->transform_node,
+                           NULL);
+
+      private->node_drawable = NULL;
+      private->node_mask     = NULL;
+      private->node_opacity  = 1.0;
+      gimp_matrix3_identity (&private->node_matrix);
     }
-  if (minx < clip_x1) minx = clip_x1;
-  if (miny < clip_y1) miny = clip_y1;
-  if (maxx > clip_x2) maxx = clip_x2;
-  if (maxy > clip_y2) maxy = clip_y2;
-
-  if (minx <= maxx && miny <= maxy)
-    {
-      cairo_surface_t *area;
 
-      area = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                         maxx - minx + 1,
-                                         maxy - miny + 1);
+  gimp_item_get_offset (GIMP_ITEM (private->drawable), &offset_x, &offset_y);
 
-      g_return_if_fail (area != NULL);
+  gimp_matrix3_identity (&matrix);
+  gimp_matrix3_translate (&matrix, offset_x, offset_y);
+  gimp_matrix3_mult (&private->transform, &matrix);
+  gimp_matrix3_scale (&matrix, shell->scale_x, shell->scale_y);
 
-      gimp_canvas_transform_preview_draw_tri (texture, cr, area, minx, miny,
-                                              mask, mask_offx, mask_offy,
-                                              x, y, u, v, opacity);
-      gimp_canvas_transform_preview_draw_tri (texture, cr, area, minx, miny,
-                                              mask, mask_offx, mask_offy,
-                                              x2, y2, u2, v2, opacity);
+  has_mask = gimp_item_mask_bounds (GIMP_ITEM (private->drawable),
+                                    NULL, NULL, NULL, NULL);
 
-      cairo_surface_destroy (area);
+  if (private->drawable != private->node_drawable)
+    {
+      private->node_drawable = private->drawable;
+
+      gegl_node_set (private->source_node,
+                     "buffer", gimp_drawable_get_buffer (private->drawable),
+                     NULL);
+      gegl_node_set (private->convert_format_node,
+                     "format", gimp_drawable_get_format_with_alpha (private->drawable),
+                     NULL);
     }
-}
-
-/**
- * gimp_canvas_transform_preview_draw_tri:
- * @texture:   the thing being transformed
- * @cr:        the #cairo_t to draw to
- * @area:      has prefetched pixel data of dest
- * @area_offx: x coordinate of area in dest
- * @area_offy: y coordinate of area in dest
- * @x:         Array of the three x coords of triangle
- * @y:         Array of the three y coords of triangle
- *
- * This draws a triangle onto dest by breaking it down into pixel
- * rows, and then calling gimp_canvas_transform_preview_draw_tri_row()
- * and gimp_canvas_transform_preview_draw_tri_row_mask() to do the
- * actual pixel changing.
- **/
-static void
-gimp_canvas_transform_preview_draw_tri (GimpDrawable    *texture,
-                                        cairo_t         *cr,
-                                        cairo_surface_t *area,
-                                        gint             area_offx,
-                                        gint             area_offy,
-                                        GimpChannel     *mask,
-                                        gint             mask_offx,
-                                        gint             mask_offy,
-                                        gint            *x,
-                                        gint            *y,
-                                        gfloat          *u, /* texture coords */
-                                        gfloat          *v, /* 0.0 ... tex width, height */
-                                        guchar           opacity)
-{
-  gdouble      clip_x1, clip_y1, clip_x2, clip_y2;
-  gint         j, k;
-  gint         ry;
-  gint        *l_edge, *r_edge;    /* arrays holding x-coords of edge pixels */
-  gint        *left,   *right;     /* temp pointers into l_edge and r_edge  */
-  gfloat       dul, dvl, dur, dvr; /* left and right texture coord deltas  */
-  gfloat       u_l, v_l, u_r, v_r; /* left and right texture coord pairs  */
-
-  g_return_if_fail (GIMP_IS_DRAWABLE (texture));
-  g_return_if_fail (area != NULL);
-
-  g_return_if_fail (x != NULL && y != NULL && u != NULL && v != NULL);
-
-  cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
 
-  /* sort vertices in order of y-coordinate */
-
-  for (j = 0; j < 2; j++)
-    for (k = j + 1; k < 3; k++)
-      if (y[k] < y[j])
+  if ((has_mask           || private->opacity      != 1.0) !=
+      (private->node_mask || private->node_opacity != 1.0))
+    {
+      if (has_mask || private->opacity != 1.0)
         {
-          gint tmp;
-          gfloat ftmp;
+          gegl_node_disconnect (private->convert_format_node, "input");
 
-          tmp  = y[k];  y[k] = y[j];  y[j] = tmp;
-          tmp  = x[k];  x[k] = x[j];  x[j] = tmp;
-          ftmp = u[k];  u[k] = u[j];  u[j] = ftmp;
-          ftmp = v[k];  v[k] = v[j];  v[j] = ftmp;
+          gegl_node_link_many (private->source_node,
+                               private->opacity_node,
+                               private->cache_node,
+                               private->transform_node,
+                               NULL);
         }
-
-  if (y[2] == y[0])
-    return;
-
-  l_edge = g_new (gint, y[2] - y[0]);
-  r_edge = g_new (gint, y[2] - y[0]);
-
-  /* draw the triangle */
-
-  gimp_canvas_transform_preview_trace_tri_edge (l_edge, x[0], y[0], x[2], y[2]);
-
-  left = l_edge;
-  dul  = (u[2] - u[0]) / (y[2] - y[0]);
-  dvl  = (v[2] - v[0]) / (y[2] - y[0]);
-  u_l  = u[0];
-  v_l  = v[0];
-
-  if (y[0] != y[1])
-    {
-      gimp_canvas_transform_preview_trace_tri_edge (r_edge, x[0], y[0], x[1], y[1]);
-
-      right = r_edge;
-      dur   = (u[1] - u[0]) / (y[1] - y[0]);
-      dvr   = (v[1] - v[0]) / (y[1] - y[0]);
-      u_r   = u[0];
-      v_r   = v[0];
-
-      if (mask)
-        for (ry = y[0]; ry < y[1]; ry++)
-          {
-            if (ry >= clip_y1 && ry < clip_y2)
-              gimp_canvas_transform_preview_draw_tri_row_mask (texture, cr,
-                                                               area, area_offx, area_offy,
-                                                               mask, mask_offx, mask_offy,
-                                                               *left, u_l, v_l,
-                                                               *right, u_r, v_r,
-                                                               ry,
-                                                               opacity);
-            left ++;      right ++;
-            u_l += dul;   v_l += dvl;
-            u_r += dur;   v_r += dvr;
-          }
-      else
-        for (ry = y[0]; ry < y[1]; ry++)
-          {
-            if (ry >= clip_y1 && ry < clip_y2)
-              gimp_canvas_transform_preview_draw_tri_row (texture, cr,
-                                                          area, area_offx, area_offy,
-                                                          *left, u_l, v_l,
-                                                          *right, u_r, v_r,
-                                                          ry,
-                                                          opacity);
-            left ++;      right ++;
-            u_l += dul;   v_l += dvl;
-            u_r += dur;   v_r += dvr;
-          }
-    }
-
-  if (y[1] != y[2])
-    {
-      gimp_canvas_transform_preview_trace_tri_edge (r_edge, x[1], y[1], x[2], y[2]);
-
-      right = r_edge;
-      dur   = (u[2] - u[1]) / (y[2] - y[1]);
-      dvr   = (v[2] - v[1]) / (y[2] - y[1]);
-      u_r   = u[1];
-      v_r   = v[1];
-
-      if (mask)
-        for (ry = y[1]; ry < y[2]; ry++)
-          {
-            if (ry >= clip_y1 && ry < clip_y2)
-              gimp_canvas_transform_preview_draw_tri_row_mask (texture, cr,
-                                                               area, area_offx, area_offy,
-                                                               mask, mask_offx, mask_offy,
-                                                               *left,  u_l, v_l,
-                                                               *right, u_r, v_r,
-                                                               ry,
-                                                               opacity);
-            left ++;      right ++;
-            u_l += dul;   v_l += dvl;
-            u_r += dur;   v_r += dvr;
-          }
       else
-        for (ry = y[1]; ry < y[2]; ry++)
-          {
-            if (ry >= clip_y1 && ry < clip_y2)
-              gimp_canvas_transform_preview_draw_tri_row (texture, cr,
-                                                          area, area_offx, area_offy,
-                                                          *left,  u_l, v_l,
-                                                          *right, u_r, v_r,
-                                                          ry,
-                                                          opacity);
-            left ++;      right ++;
-            u_l += dul;   v_l += dvl;
-            u_r += dur;   v_r += dvr;
-          }
-    }
-
-  g_free (l_edge);
-  g_free (r_edge);
-}
-
-/**
- * gimp_canvas_transform_preview_draw_tri_row:
- * @texture: the thing being transformed
- * @cr:      the #cairo_t to draw to
- * @area:    has prefetched pixel data of dest
- *
- * Called from gimp_canvas_transform_preview_draw_tri(), this draws a
- * single row of a triangle onto dest when there is not a mask. The
- * run (x1,y) to (x2,y) in dest corresponds to the run (u1,v1) to
- * (u2,v2) in texture.
- **/
-static void
-gimp_canvas_transform_preview_draw_tri_row (GimpDrawable    *texture,
-                                            cairo_t         *cr,
-                                            cairo_surface_t *area,
-                                            gint             area_offx,
-                                            gint             area_offy,
-                                            gint             x1,
-                                            gfloat           u1,
-                                            gfloat           v1,
-                                            gint             x2,
-                                            gfloat           u2,
-                                            gfloat           v2,
-                                            gint             y,
-                                            guchar           opacity)
-{
-  GeglBuffer *buffer;
-  const Babl *format;
-  guchar     *buf;
-  guchar     *b;
-  guchar     *pptr;      /* points into the pixels of a row of area */
-  gint        bpp;
-  gfloat      u, v;
-  gfloat      du, dv;
-  gint        dx;
-  gint        samples;
-
-  if (x2 == x1)
-    return;
-
-  g_return_if_fail (GIMP_IS_DRAWABLE (texture));
-  g_return_if_fail (area != NULL);
-  g_return_if_fail (cairo_image_surface_get_format (area) == CAIRO_FORMAT_ARGB32);
-
-  /* make sure the pixel run goes in the positive direction */
-  if (x1 > x2)
-    {
-      gint   tmp;
-      gfloat ftmp;
-
-      tmp  = x2;  x2 = x1;  x1 = tmp;
-      ftmp = u2;  u2 = u1;  u1 = ftmp;
-      ftmp = v2;  v2 = v1;  v1 = ftmp;
-    }
-
-  u = u1;
-  v = v1;
-  du = (u2 - u1) / (x2 - x1);
-  dv = (v2 - v1) / (x2 - x1);
-
-  /* don't calculate unseen pixels */
-  if (x1 < area_offx)
-    {
-      u += du * (area_offx - x1);
-      v += dv * (area_offx - x1);
-      x1 = area_offx;
-    }
-  else if (x1 > area_offx + cairo_image_surface_get_width (area) - 1)
-    {
-      return;
-    }
-
-  if (x2 < area_offx)
-    {
-      return;
-    }
-  else if (x2 > area_offx + cairo_image_surface_get_width (area) - 1)
-    {
-      x2 = area_offx + cairo_image_surface_get_width (area) - 1;
-    }
-
-  dx = x2 - x1;
-  if (! dx)
-    return;
-
-  cairo_surface_flush (area);
-  pptr = (cairo_image_surface_get_data (area)
-          + (y - area_offy) * cairo_image_surface_get_stride (area)
-          + (x1 - area_offx) * 4);
-
-  buffer = gimp_drawable_get_buffer (texture);
-
-  format = gegl_buffer_get_format (buffer);
-  bpp    = babl_format_get_bytes_per_pixel (format);
-  buf    = g_alloca (bpp * dx);
-
-  samples = dx;
-  b       = buf;
-
-  while (samples--)
-    {
-      gegl_buffer_sample (buffer, (gint) u, (gint) v, NULL, b,
-                          format,
-                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
-
-      b += bpp;
+        {
+          gegl_node_disconnect (private->opacity_node, "input");
 
-      u += du;
-      v += dv;
+          gegl_node_link_many (private->source_node,
+                               private->convert_format_node,
+                               private->transform_node,
+                               NULL);
+        }
     }
 
-  if (opacity < 255)
+  if (has_mask)
     {
-      guchar *u8_buffer = g_alloca (4 * dx);
-      guchar *u8_b;
+      GimpDrawable *mask = GIMP_DRAWABLE (gimp_image_get_mask (image));
 
-      babl_process (babl_fish (format,
-                               babl_format ("R'G'B'A u8")),
-                    buf, u8_buffer, dx);
-
-      samples = dx;
-      u8_b    = u8_buffer;
-
-      while (samples--)
+      if (mask != private->node_mask)
         {
-          register gulong tmp;
+          private->node_mask = mask;
 
-          u8_b[3] = INT_MULT (opacity, u8_b[3], tmp);
+          gegl_node_set (private->mask_source_node,
+                         "buffer", gimp_drawable_get_buffer (mask),
+                         NULL);
 
-          u8_b += 4;
+          gegl_node_connect_to (private->mask_source_node, "output",
+                                private->opacity_node,     "aux");
         }
-
-      babl_process (babl_fish (babl_format ("R'G'B'A u8"),
-                               babl_format ("cairo-ARGB32")),
-                    u8_buffer, pptr, dx);
     }
-  else
+  else if (private->node_mask)
     {
-      babl_process (babl_fish (format,
-                               babl_format ("cairo-ARGB32")),
-                    buf, pptr, dx);
-    }
-
-  cairo_surface_mark_dirty (area);
-
-  cairo_set_source_surface (cr, area, area_offx, area_offy);
-  cairo_rectangle (cr, x1, y, x2 - x1, 1);
-  cairo_fill (cr);
-}
-
-/**
- * gimp_canvas_transform_preview_draw_tri_row_mask:
- *
- * Called from gimp_canvas_transform_preview_draw_tri(), this draws a
- * single row of a triangle onto dest, when there is a mask.
- **/
-static void
-gimp_canvas_transform_preview_draw_tri_row_mask (GimpDrawable    *texture,
-                                                 cairo_t         *cr,
-                                                 cairo_surface_t *area,
-                                                 gint             area_offx,
-                                                 gint             area_offy,
-                                                 GimpChannel     *mask,
-                                                 gint             mask_offx,
-                                                 gint             mask_offy,
-                                                 gint             x1,
-                                                 gfloat           u1,
-                                                 gfloat           v1,
-                                                 gint             x2,
-                                                 gfloat           u2,
-                                                 gfloat           v2,
-                                                 gint             y,
-                                                 guchar           opacity)
-{
-  GeglBuffer *buffer;
-  GeglBuffer *mask_buffer;
-  const Babl *format;
-  const Babl *mask_format;
-  guchar     *buf;
-  guchar     *mask_buf;
-  guchar     *b;
-  guchar     *mask_b;
-  guchar     *pptr;              /* points into the pixels of area        */
-  gint        bpp;
-  gint        mask_bpp;
-  gfloat      u, v;
-  gfloat      mu, mv;
-  gfloat      du, dv;
-  gint        dx;
-  gint        samples;
-
-  if (x2 == x1)
-    return;
-
-  g_return_if_fail (GIMP_IS_DRAWABLE (texture));
-  g_return_if_fail (GIMP_IS_CHANNEL (mask));
-  g_return_if_fail (area != NULL);
-  g_return_if_fail (cairo_image_surface_get_format (area) == CAIRO_FORMAT_ARGB32);
+      private->node_mask = NULL;
 
-  /* make sure the pixel run goes in the positive direction */
-  if (x1 > x2)
-    {
-      gint   tmp;
-      gfloat ftmp;
-
-      tmp  = x2;  x2 = x1;  x1 = tmp;
-      ftmp = u2;  u2 = u1;  u1 = ftmp;
-      ftmp = v2;  v2 = v1;  v1 = ftmp;
+      gegl_node_disconnect (private->opacity_node, "aux");
     }
 
-  u = u1;
-  v = v1;
-  du = (u2 - u1) / (x2 - x1);
-  dv = (v2 - v1) / (x2 - x1);
-
-  /* don't calculate unseen pixels */
-  if (x1 < area_offx)
+  if (private->opacity != private->node_opacity)
     {
-      u += du * (area_offx - x1);
-      v += dv * (area_offx - x1);
-      x1 = area_offx;
-    }
-  else if (x1 > area_offx + cairo_image_surface_get_width (area) - 1)
-    {
-      return;
-    }
+      private->node_opacity = private->opacity;
 
-  if (x2 < area_offx)
-    {
-      return;
+      gegl_node_set (private->opacity_node,
+                     "value", private->opacity,
+                     NULL);
     }
-  else if (x2 > area_offx + cairo_image_surface_get_width (area) - 1)
-    {
-      x2 = area_offx + cairo_image_surface_get_width (area) - 1;
-    }
-
-  dx = x2 - x1;
-  if (! dx)
-    return;
-
-  mu = u + mask_offx;
-  mv = v + mask_offy;
-
-  cairo_surface_flush (area);
-  pptr = (cairo_image_surface_get_data (area)
-          + (y - area_offy) * cairo_image_surface_get_stride (area)
-          + (x1 - area_offx) * 4);
-
-  buffer      = gimp_drawable_get_buffer (texture);
-  mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
 
-  format      = gegl_buffer_get_format (buffer);
-  mask_format = gegl_buffer_get_format (mask_buffer);
-  bpp         = babl_format_get_bytes_per_pixel (format);
-  mask_bpp    = babl_format_get_bytes_per_pixel (mask_format);
-  buf         = g_alloca (bpp * dx);
-  mask_buf    = g_alloca (mask_bpp * dx);
-
-  samples = dx;
-  b       = buf;
-  mask_b  = mask_buf;
-
-  while (samples--)
+  if (memcmp (&matrix, &private->node_matrix, sizeof (matrix)))
     {
-      gegl_buffer_sample (buffer, (gint) u, (gint) v, NULL, b,
-                          format,
-                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
-      gegl_buffer_sample (mask_buffer, (gint) mu, (gint) mv, NULL, mask_b,
-                          mask_format,
-                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
-
-      b      += bpp;
-      mask_b += mask_bpp;
-
-      u += du;
-      v += dv;
-      mu += du;
-      mv += dv;
-    }
-
-  {
-    guchar *u8_buffer      = g_alloca (4 * dx);
-    guchar *u8_mask_buffer = g_alloca (dx);
-    guchar *u8_b;
-    guchar *u8_mask_b;
-
-    babl_process (babl_fish (format,
-                             babl_format ("R'G'B'A u8")),
-                  buf, u8_buffer, dx);
-    babl_process (babl_fish (mask_format,
-                             babl_format ("Y u8")),
-                  mask_buf, u8_mask_buffer, dx);
-
-    samples   = dx;
-    u8_b      = u8_buffer;
-    u8_mask_b = u8_mask_buffer;
-
-    while (samples--)
-      {
-        register gulong tmp;
-
-        u8_b[3] = INT_MULT3 (opacity, *u8_mask_b, u8_b[3], tmp);
-
-        u8_b      += 4;
-        u8_mask_b += 1;
-      }
+      private->node_matrix = matrix;
 
-    babl_process (babl_fish (babl_format ("R'G'B'A u8"),
-                             babl_format ("cairo-ARGB32")),
-                  u8_buffer, pptr, dx);
-  }
-
-  cairo_surface_mark_dirty (area);
-
-  cairo_set_source_surface (cr, area, area_offx, area_offy);
-  cairo_rectangle (cr, x1, y, x2 - x1, 1);
-  cairo_fill (cr);
+      gimp_gegl_node_set_matrix (private->transform_node, &matrix);
+    }
 }
 
-/**
- * gimp_canvas_transform_preview_trace_tri_edge:
- * @dest: x coordinates are placed in this array
- *
- * Find the x coordinates for a line that runs from (x1,y1) to
- * (x2,y2), corresponding to the y coordinates y1 to y2-1. So dest
- * must be large enough to hold y2-y1 values.
- **/
-static void
-gimp_canvas_transform_preview_trace_tri_edge (gint *dest,
-                                              gint  x1,
-                                              gint  y1,
-                                              gint  x2,
-                                              gint  y2)
-{
-  const gint  dx   = x2 - x1;
-  const gint  dy   = y2 - y1;
-  gint        b    = dy;
-  gint       *dptr = dest;
-  gint        errorterm;
 
-  if (dy <= 0)
-    return;
-
-  g_return_if_fail (dest != NULL);
+/* public functions */
 
-  if (dx >= 0)
-    {
-      errorterm = 0;
 
-      while (b--)
-        {
-          *dptr++ = x1;
-
-          errorterm += dx;
-
-          while (errorterm > dy)
-            {
-              x1++;
-              errorterm -= dy;
-            }
-        }
-    }
-  else
-    {
-      errorterm = dy;
-
-      while (b--)
-        {
-          while (errorterm > dy)
-            {
-              x1--;
-              errorterm -= dy;
-            }
-
-          /* dx is negative here, so this is effectively an addition: */
-          errorterm -= dx;
+GimpCanvasItem *
+gimp_canvas_transform_preview_new (GimpDisplayShell  *shell,
+                                   GimpDrawable      *drawable,
+                                   const GimpMatrix3 *transform,
+                                   gdouble            x1,
+                                   gdouble            y1,
+                                   gdouble            x2,
+                                   gdouble            y2)
+{
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
+  g_return_val_if_fail (transform != NULL, NULL);
 
-          *dptr++ = x1;
-        }
-    }
+  return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW,
+                       "shell",     shell,
+                       "drawable",  drawable,
+                       "transform", transform,
+                       "x1",        x1,
+                       "y1",        y1,
+                       "x2",        x2,
+                       "y2",        y2,
+                       NULL);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]