Example #1
0
static gboolean
gimp_cage_tool_key_press (GimpTool    *tool,
                          GdkEventKey *kevent,
                          GimpDisplay *display)
{
    GimpCageTool *ct = GIMP_CAGE_TOOL (tool);

    switch (kevent->keyval)
    {
    case GDK_KEY_BackSpace:
        if (! ct->cage_complete && ct->tool_state == CAGE_STATE_WAIT)
        {
            gimp_cage_tool_remove_last_handle (ct);
        }
        else if (ct->cage_complete && ct->tool_state == CAGE_STATE_WAIT)
        {
            gimp_cage_config_remove_selected_points(ct->config);

            /* if the cage have less than 3 handles, we reopen it */
            if (gimp_cage_config_get_n_points(ct->config) <= 2)
                ct->cage_complete = FALSE;
        }
        return TRUE;

    case GDK_KEY_Return:
    case GDK_KEY_KP_Enter:
    case GDK_KEY_ISO_Enter:
        if (ct->cage_complete == FALSE && gimp_cage_config_get_n_points (ct->config) > 2)
        {
            g_object_set (gimp_tool_get_options (tool),
                          "cage-mode", GIMP_CAGE_MODE_DEFORM,
                          NULL);
        }
        else if (ct->tool_state == DEFORM_STATE_WAIT)
        {
            gimp_tool_control_push_preserve (tool->control, TRUE);

            gimp_image_map_commit (ct->image_map);
            g_object_unref (ct->image_map);
            ct->image_map = NULL;

            gimp_tool_control_pop_preserve (tool->control);

            gimp_image_flush (gimp_display_get_image (display));

            gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
        }
        break;

    case GDK_KEY_Escape:
        gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
        return TRUE;

    default:
        break;
    }

    return FALSE;
}
Example #2
0
static gboolean
gimp_cage_tool_key_press (GimpTool    *tool,
                          GdkEventKey *kevent,
                          GimpDisplay *display)
{
  GimpCageTool *ct = GIMP_CAGE_TOOL (tool);

  switch (kevent->keyval)
    {
    case GDK_KEY_BackSpace:
      if (ct->tool_state == CAGE_STATE_WAIT)
        {
          gimp_cage_tool_remove_last_handle (ct);
        }
      else if (ct->tool_state == DEFORM_STATE_WAIT)
        {
          gimp_cage_config_remove_selected_points(ct->config);

          /* if the cage have less than 3 handles, we reopen it */
          if (gimp_cage_config_get_n_points(ct->config) <= 2)
            {
              ct->tool_state = CAGE_STATE_WAIT;
            }

          gimp_cage_tool_compute_coef (ct);
          gimp_cage_tool_render_node_update (ct);
        }
      return TRUE;

    case GDK_KEY_Return:
    case GDK_KEY_KP_Enter:
    case GDK_KEY_ISO_Enter:
      if (! gimp_cage_tool_is_complete (ct) &&
          gimp_cage_config_get_n_points (ct->config) > 2)
        {
          g_object_set (gimp_tool_get_options (tool),
                        "cage-mode", GIMP_CAGE_MODE_DEFORM,
                        NULL);
        }
      else if (ct->tool_state == DEFORM_STATE_WAIT)
        {
          gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
          gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
        }
      return TRUE;

    case GDK_KEY_Escape:
      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
      return TRUE;

    default:
      break;
    }

  return FALSE;
}
Example #3
0
static void
gimp_cage_tool_compute_coef (GimpCageTool *ct)
{
  GimpCageConfig *config = ct->config;
  GimpProgress   *progress;
  const Babl     *format;
  GeglNode       *gegl;
  GeglNode       *input;
  GeglNode       *output;
  GeglProcessor  *processor;
  GeglBuffer     *buffer;
  gdouble         value;

  progress = gimp_progress_start (GIMP_PROGRESS (ct),
                                  _("Computing Cage Coefficients"), FALSE);

  if (ct->coef)
    {
      g_object_unref (ct->coef);
      ct->coef = NULL;
    }

  format = babl_format_n (babl_type ("float"),
                          gimp_cage_config_get_n_points (config) * 2);


  gegl = gegl_node_new ();

  input = gegl_node_new_child (gegl,
                               "operation", "gimp:cage-coef-calc",
                               "config",    ct->config,
                               NULL);

  output = gegl_node_new_child (gegl,
                                "operation", "gegl:buffer-sink",
                                "buffer",    &buffer,
                                "format",    format,
                                NULL);

  gegl_node_connect_to (input, "output",
                        output, "input");

  processor = gegl_node_new_processor (output, NULL);

  while (gegl_processor_work (processor, &value))
    {
      if (progress)
        gimp_progress_set_value (progress, value);
    }

  if (progress)
    gimp_progress_end (progress);

  g_object_unref (processor);

  ct->coef = buffer;
  g_object_unref (gegl);

  ct->dirty_coef = FALSE;
}
static void
gimp_operation_cage_coef_calc_prepare (GeglOperation *operation)
{
  GimpOperationCageCoefCalc *occc   = GIMP_OPERATION_CAGE_COEF_CALC (operation);
  GimpCageConfig            *config = GIMP_CAGE_CONFIG (occc->config);

  gegl_operation_set_format (operation,
                             "output",
                             babl_format_n (babl_type ("float"),
                                            2 * gimp_cage_config_get_n_points (config)));
}
Example #5
0
static void
gimp_operation_cage_transform_prepare (GeglOperation *operation)
{
  GimpOperationCageTransform *oct    = GIMP_OPERATION_CAGE_TRANSFORM (operation);
  GimpCageConfig             *config = GIMP_CAGE_CONFIG (oct->config);

  gegl_operation_set_format (operation, "input",
                             babl_format_n (babl_type ("float"),
                                            2 * gimp_cage_config_get_n_points (config)));
  gegl_operation_set_format (operation, "output",
                             babl_format_n (babl_type ("float"), 2));
}
Example #6
0
static gint
gimp_cage_tool_is_on_edge (GimpCageTool *ct,
                           gdouble       x,
                           gdouble       y,
                           gint          handle_size)
{
  GimpCageOptions *options = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
  GimpCageConfig  *config  = ct->config;
  gint             i;
  guint            n_cage_vertices;
  GimpVector2      A, B, C, AB, BC, AC;
  gdouble          lAB, lBC, lAC, lEB, lEC;

  g_return_val_if_fail (GIMP_IS_CAGE_TOOL (ct), -1);

  n_cage_vertices = gimp_cage_config_get_n_points (config);

  if (n_cage_vertices < 2)
    return -1;

  A = gimp_cage_config_get_point_coordinate (config,
                                             options->cage_mode,
                                             n_cage_vertices-1);
  B = gimp_cage_config_get_point_coordinate (config,
                                             options->cage_mode,
                                             0);
  C.x = x;
  C.y = y;

  for (i = 0; i < n_cage_vertices; i++)
    {
      gimp_vector2_sub (&AB, &A, &B);
      gimp_vector2_sub (&BC, &B, &C);
      gimp_vector2_sub (&AC, &A, &C);

      lAB = gimp_vector2_length (&AB);
      lBC = gimp_vector2_length (&BC);
      lAC = gimp_vector2_length (&AC);
      lEB = lAB / 2 + (SQR (lBC) - SQR (lAC)) / (2 * lAB);
      lEC = sqrt (SQR (lBC) - SQR (lEB));

      if ((lEC < handle_size / 2) && (abs (SQR (lBC) - SQR (lAC)) <= SQR (lAB)))
        return i;

      A = B;
      B = gimp_cage_config_get_point_coordinate (config,
                                                 options->cage_mode,
                                                 (i+1) % n_cage_vertices);
    }

  return -1;
}
Example #7
0
static gint
gimp_cage_tool_is_on_handle (GimpCageTool *ct,
                             GimpDrawTool *draw_tool,
                             GimpDisplay  *display,
                             gdouble       x,
                             gdouble       y,
                             gint          handle_size)
{
  GimpCageOptions *options = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
  GimpCageConfig  *config  = ct->config;
  gdouble          dist    = G_MAXDOUBLE;
  gint             i;
  GimpVector2      cage_point;
  guint            n_cage_vertices;

  g_return_val_if_fail (GIMP_IS_CAGE_TOOL (ct), -1);

  n_cage_vertices = gimp_cage_config_get_n_points (config);

  if (n_cage_vertices == 0)
    return -1;

  for (i = 0; i < n_cage_vertices; i++)
    {
      cage_point = gimp_cage_config_get_point_coordinate (config,
                                                          options->cage_mode,
                                                          i);
      cage_point.x += ct->offset_x;
      cage_point.y += ct->offset_y;

      dist = gimp_draw_tool_calc_distance_square (GIMP_DRAW_TOOL (draw_tool),
                                                  display,
                                                  x, y,
                                                  cage_point.x,
                                                  cage_point.y);

      if (dist <= SQR (handle_size / 2))
        return i;
    }

  return -1;
}
Example #8
0
static GimpVector2
gimp_cage_transform_compute_destination (GimpCageConfig *config,
                                         gfloat         *coef,
                                         const Babl     *format_coef,
                                         GeglBuffer     *coef_buf,
                                         GimpVector2     coords)
{
  GimpVector2    result = {0, 0};
  gint           n_cage_vertices = gimp_cage_config_get_n_points (config);
  gint           i;
  GimpCagePoint *point;

  /* When Gegl bug #645810 will be solved, this should be a good optimisation */
  #ifdef GEGL_BUG_645810_SOLVED
    gegl_buffer_sample (coef_buf, coords.x, coords.y, 1.0, coef, format_coef, GEGL_INTERPOLATION_NEAREST);
  #else
    GeglRectangle  rect;

    rect.height = 1;
    rect.width  = 1;
    rect.x      = coords.x;
    rect.y      = coords.y;

    gegl_buffer_get (coef_buf, &rect, 1.0, format_coef, coef,
                     GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
  #endif

  for (i = 0; i < n_cage_vertices; i++)
    {
      point = &g_array_index (config->cage_points, GimpCagePoint, i);

      result.x += coef[i] * point->dest_point.x;
      result.y += coef[i] * point->dest_point.y;

      result.x += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.x;
      result.y += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.y;
    }

  return result;
}
static gboolean
gimp_operation_cage_coef_calc_process (GeglOperation       *operation,
                                       GeglBuffer          *output,
                                       const GeglRectangle *roi)
{
  GimpOperationCageCoefCalc *occc   = GIMP_OPERATION_CAGE_COEF_CALC (operation);
  GimpCageConfig            *config = GIMP_CAGE_CONFIG (occc->config);

  Babl *format = babl_format_n (babl_type ("float"), 2 * gimp_cage_config_get_n_points (config));

  GeglBufferIterator *it;
  guint               n_cage_vertices;
  GimpCagePoint      *current, *last;

  if (! config)
    return FALSE;

  n_cage_vertices   = gimp_cage_config_get_n_points (config);

  it = gegl_buffer_iterator_new (output, roi, format, GEGL_BUFFER_READWRITE);

  while (gegl_buffer_iterator_next (it))
    {
      /* iterate inside the roi */
      gint  n_pixels = it->length;
      gint  x = it->roi->x; /* initial x                   */
      gint  y = it->roi->y; /*           and y coordinates */
      gint  j;

      gfloat      *coef = it->data[0];

      while(n_pixels--)
        {
          if (gimp_cage_config_point_inside(config, x, y))
            {
              last = &(g_array_index (config->cage_points, GimpCagePoint, 0));

              for( j = 0; j < n_cage_vertices; j++)
                {
                  GimpVector2 v1,v2,a,b,p;
                  gdouble BA,SRT,L0,L1,A0,A1,A10,L10, Q,S,R, absa;

                  current = &(g_array_index (config->cage_points, GimpCagePoint, (j+1) % n_cage_vertices));
                  v1 = last->src_point;
                  v2 = current->src_point;
                  p.x = x;
                  p.y = y;
                  a.x = v2.x - v1.x;
                  a.y = v2.y - v1.y;
                  absa = gimp_vector2_length (&a);

                  b.x = v1.x - x;
                  b.y = v1.y - y;
                  Q = a.x * a.x + a.y * a.y;
                  S = b.x * b.x + b.y * b.y;
                  R = 2.0 * (a.x * b.x + a.y * b.y);
                  BA = b.x * a.y - b.y * a.x;
                  SRT = sqrt(4.0 * S * Q - R * R);

                  L0 = log(S);
                  L1 = log(S + Q + R);
                  A0 = atan2(R, SRT) / SRT;
                  A1 = atan2(2.0 * Q + R, SRT) / SRT;
                  A10 = A1 - A0;
                  L10 = L1 - L0;

                  /* edge coef */
                  coef[j + n_cage_vertices] = (-absa / (4.0 * G_PI)) * ((4.0*S-(R*R)/Q) * A10 + (R / (2.0 * Q)) * L10 + L1 - 2.0);

                  if (isnan(coef[j + n_cage_vertices]))
                    {
                      coef[j + n_cage_vertices] = 0.0;
                    }

                  /* vertice coef */
                  if (!gimp_operation_cage_coef_calc_is_on_straight (&v1, &v2, &p))
                    {
                      coef[j] += (BA / (2.0 * G_PI)) * (L10 /(2.0*Q) - A10 * (2.0 + R / Q));
                      coef[(j+1)%n_cage_vertices] -= (BA / (2.0 * G_PI)) * (L10 / (2.0 * Q) - A10 * (R / Q));
                    }

                  last = current;
                }
            }

          coef += 2 * n_cage_vertices;

          /* update x and y coordinates */
          x++;
          if (x >= (it->roi->x + it->roi->width))
            {
              x = it->roi->x;
              y++;
            }
        }
    }

  return TRUE;
}
Example #10
0
static void
gimp_cage_tool_draw (GimpDrawTool *draw_tool)
{
  GimpCageTool    *ct        = GIMP_CAGE_TOOL (draw_tool);
  GimpCageOptions *options   = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
  GimpCageConfig  *config    = ct->config;
  GimpCanvasGroup *stroke_group;
  gint             n_vertices;
  gint             i;
  GimpHandleType   handle;

  n_vertices = gimp_cage_config_get_n_points (config);

  if (n_vertices == 0)
    return;

  if (ct->tool_state == CAGE_STATE_INIT)
    return;

  stroke_group = gimp_draw_tool_add_stroke_group (draw_tool);

  gimp_draw_tool_push_group (draw_tool, stroke_group);

  /* If needed, draw line to the cursor. */
  if (! gimp_cage_tool_is_complete (ct))
    {
      GimpVector2 last_point;

      last_point = gimp_cage_config_get_point_coordinate (ct->config,
                                                          options->cage_mode,
                                                          n_vertices - 1);

      gimp_draw_tool_add_line (draw_tool,
                               last_point.x + ct->offset_x,
                               last_point.y + ct->offset_y,
                               ct->cursor_x,
                               ct->cursor_y);
    }

  gimp_draw_tool_pop_group (draw_tool);

  /* Draw the cage with handles. */
  for (i = 0; i < n_vertices; i++)
    {
      GimpVector2 point1, point2;

      point1 = gimp_cage_config_get_point_coordinate (ct->config,
                                                      options->cage_mode,
                                                      i);
      point1.x += ct->offset_x;
      point1.y += ct->offset_y;

      if (i > 0 || gimp_cage_tool_is_complete (ct))
        {
          gint index_point2;

          if (i == 0)
            index_point2 = n_vertices - 1;
          else
            index_point2 = i - 1;

          point2 = gimp_cage_config_get_point_coordinate (ct->config,
                                                          options->cage_mode,
                                                          index_point2);
          point2.x += ct->offset_x;
          point2.y += ct->offset_y;

          gimp_draw_tool_push_group (draw_tool, stroke_group);

          gimp_draw_tool_add_line (draw_tool,
                                   point1.x,
                                   point1.y,
                                   point2.x,
                                   point2.y);

          gimp_draw_tool_pop_group (draw_tool);
        }

      if (i == ct->hovering_handle)
        handle = GIMP_HANDLE_FILLED_CIRCLE;
      else
        handle = GIMP_HANDLE_CIRCLE;

      gimp_draw_tool_add_handle (draw_tool,
                                 handle,
                                 point1.x,
                                 point1.y,
                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                 GIMP_HANDLE_ANCHOR_CENTER);

      if (gimp_cage_config_point_is_selected (ct->config, i))
        {
          gimp_draw_tool_add_handle (draw_tool,
                                     GIMP_HANDLE_SQUARE,
                                     point1.x,
                                     point1.y,
                                     GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                     GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                     GIMP_HANDLE_ANCHOR_CENTER);
        }
    }

  if (ct->tool_state == DEFORM_STATE_SELECTING ||
      ct->tool_state == CAGE_STATE_SELECTING)
    {
      gimp_draw_tool_add_rectangle (draw_tool,
                                    FALSE,
                                    MIN (ct->selection_start_x, ct->cursor_x),
                                    MIN (ct->selection_start_y, ct->cursor_y),
                                    abs (ct->selection_start_x - ct->cursor_x),
                                    abs (ct->selection_start_y - ct->cursor_y));
    }
}
Example #11
0
static void
gimp_cage_tool_button_press (GimpTool            *tool,
                             const GimpCoords    *coords,
                             guint32              time,
                             GdkModifierType      state,
                             GimpButtonPressType  press_type,
                             GimpDisplay         *display)
{
  GimpCageTool    *ct        = GIMP_CAGE_TOOL (tool);
  GimpDrawTool    *draw_tool = GIMP_DRAW_TOOL (tool);
  gint             handle    = -1;
  gint             edge      = -1;

  if (display != tool->display)
    gimp_cage_tool_start (ct, display);

  gimp_tool_control_activate (tool->control);

  if (ct->config)
    {
      handle = gimp_cage_tool_is_on_handle (ct,
                                            draw_tool,
                                            display,
                                            coords->x,
                                            coords->y,
                                            GIMP_TOOL_HANDLE_SIZE_CIRCLE);
      edge = gimp_cage_tool_is_on_edge (ct,
                                        coords->x,
                                        coords->y,
                                        GIMP_TOOL_HANDLE_SIZE_CIRCLE);
    }

  ct->movement_start_x = coords->x;
  ct->movement_start_y = coords->y;

  switch (ct->tool_state)
    {
      case CAGE_STATE_INIT:
        /* No handle yet, we add the first one and swith the tool to
         * moving handle state.
         */
        gimp_cage_config_add_cage_point (ct->config,
                                         coords->x - ct->offset_x,
                                         coords->y - ct->offset_y);
        gimp_cage_config_select_point (ct->config, 0);
        ct->tool_state = CAGE_STATE_MOVE_HANDLE;
        break;

      case CAGE_STATE_WAIT:
        if (handle == -1 && edge <= 0)
          {
            /* User clicked on the background, we add a new handle
             * and move it
             */
            gimp_cage_config_add_cage_point (ct->config,
                                             coords->x - ct->offset_x,
                                             coords->y - ct->offset_y);
            gimp_cage_config_select_point (ct->config,
                                           gimp_cage_config_get_n_points (ct->config) - 1);
            ct->tool_state = CAGE_STATE_MOVE_HANDLE;
          }
        else if (handle == 0 && gimp_cage_config_get_n_points (ct->config) > 2)
          {
            /* User clicked on the first handle, we wait for
             * release for closing the cage and switching to
             * deform if possible
             */
            gimp_cage_config_select_point (ct->config, 0);
            ct->tool_state = CAGE_STATE_CLOSING;
          }
        else if (handle >= 0)
          {
            /* User clicked on a handle, so we move it */

            if (state & GDK_SHIFT_MASK)
              {
                /* Multiple selection */

                gimp_cage_config_toggle_point_selection (ct->config, handle);
              }
            else
              {
                /* New selection */

                if (! gimp_cage_config_point_is_selected (ct->config, handle))
                  {
                    gimp_cage_config_select_point (ct->config, handle);
                  }
              }

            ct->tool_state = CAGE_STATE_MOVE_HANDLE;
          }
        else if (edge > 0)
          {
            /* User clicked on an edge, we add a new handle here and select it */

            gimp_cage_config_insert_cage_point (ct->config, edge, coords->x, coords->y);
            gimp_cage_config_select_point (ct->config, edge);
            ct->tool_state = CAGE_STATE_MOVE_HANDLE;
          }
        break;

      case DEFORM_STATE_WAIT:
        if (handle == -1)
          {
            /* User clicked on the background, we start a rubber band
             * selection
             */
            ct->selection_start_x = coords->x;
            ct->selection_start_y = coords->y;
            ct->tool_state = DEFORM_STATE_SELECTING;
          }

        if (handle >= 0)
          {
            /* User clicked on a handle, so we move it */

            if (state & GDK_SHIFT_MASK)
              {
                /* Multiple selection */

                gimp_cage_config_toggle_point_selection (ct->config, handle);
              }
            else
              {
                /* New selection */

                if (! gimp_cage_config_point_is_selected (ct->config, handle))
                  {
                    gimp_cage_config_select_point (ct->config, handle);
                  }
              }

            ct->tool_state = DEFORM_STATE_MOVE_HANDLE;
          }
        break;
    }
}
Example #12
0
static gboolean
gimp_operation_cage_transform_process (GeglOperation       *operation,
                                       GeglBuffer          *in_buf,
                                       GeglBuffer          *aux_buf,
                                       GeglBuffer          *out_buf,
                                       const GeglRectangle *roi,
                                       gint                 level)
{
  GimpOperationCageTransform *oct    = GIMP_OPERATION_CAGE_TRANSFORM (operation);
  GimpCageConfig             *config = GIMP_CAGE_CONFIG (oct->config);
  GeglRectangle               cage_bb;
  gfloat                     *coords;
  gfloat                     *coef;
  const Babl                 *format_coef;
  GimpVector2                 plain_color;
  GeglBufferIterator         *it;
  gint                        x, y;
  gboolean                    output_set;
  GimpCagePoint              *point;
  guint                       n_cage_vertices;

  /* pre-fill the out buffer with no-displacement coordinate */
  it      = gegl_buffer_iterator_new (out_buf, roi, 0, NULL, GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
  cage_bb = gimp_cage_config_get_bounding_box (config);

  point = &(g_array_index (config->cage_points, GimpCagePoint, 0));
  plain_color.x = (gint) point->src_point.x;
  plain_color.y = (gint) point->src_point.y;

  n_cage_vertices   = gimp_cage_config_get_n_points (config);

  while (gegl_buffer_iterator_next (it))
    {
      /* iterate inside the roi */
      gint    n_pixels = it->length;
      gfloat *output   = it->data[0];

      x = it->roi->x; /* initial x         */
      y = it->roi->y; /* and y coordinates */

      while (n_pixels--)
        {
          output_set = FALSE;
          if (oct->fill_plain_color)
            {
              if (x > cage_bb.x &&
                  y > cage_bb.y &&
                  x < cage_bb.x + cage_bb.width &&
                  y < cage_bb.y + cage_bb.height)
                {
                  if (gimp_cage_config_point_inside (config, x, y))
                    {
                      output[0] = plain_color.x;
                      output[1] = plain_color.y;
                      output_set = TRUE;
                    }
                }
            }
          if (!output_set)
            {
              output[0] = x;
              output[1] = y;
            }

          output += 2;

          /* update x and y coordinates */
          x++;
          if (x >= (it->roi->x + it->roi->width))
            {
              x = it->roi->x;
              y++;
            }
        }
    }

  oct->progress = 0.0;
  g_object_notify (G_OBJECT (oct), "progress");

  /* pre-allocate memory outside of the loop */
  coords      = g_slice_alloc (2 * sizeof (gfloat));
  coef        = g_malloc (n_cage_vertices * 2 * sizeof (gfloat));
  format_coef = babl_format_n (babl_type ("float"), 2 * n_cage_vertices);

  /* compute, reverse and interpolate the transformation */
  for (y = cage_bb.y; y < cage_bb.y + cage_bb.height - 1; y++)
    {
      GimpVector2 p1_d, p2_d, p3_d, p4_d;
      GimpVector2 p1_s, p2_s, p3_s, p4_s;

      p1_s.y = y;
      p2_s.y = y+1;
      p3_s.y = y+1;
      p3_s.x = cage_bb.x;
      p4_s.y = y;
      p4_s.x = cage_bb.x;

      p3_d = gimp_cage_transform_compute_destination (config, coef, format_coef, aux_buf, p3_s);
      p4_d = gimp_cage_transform_compute_destination (config, coef, format_coef, aux_buf, p4_s);

      for (x = cage_bb.x; x < cage_bb.x + cage_bb.width - 1; x++)
        {
          p1_s = p4_s;
          p2_s = p3_s;
          p3_s.x = x+1;
          p4_s.x = x+1;

          p1_d = p4_d;
          p2_d = p3_d;
          p3_d = gimp_cage_transform_compute_destination (config, coef, format_coef, aux_buf, p3_s);
          p4_d = gimp_cage_transform_compute_destination (config, coef, format_coef, aux_buf, p4_s);

          if (gimp_cage_config_point_inside (config, x, y))
            {
              gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
                                                                              out_buf,
                                                                              roi,
                                                                              p1_s, p1_d,
                                                                              p2_s, p2_d,
                                                                              p3_s, p3_d,
                                                                              0,
                                                                              coords);

              gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
                                                                              out_buf,
                                                                              roi,
                                                                              p1_s, p1_d,
                                                                              p3_s, p3_d,
                                                                              p4_s, p4_d,
                                                                              0,
                                                                              coords);
            }
        }

      if ((y - cage_bb.y) % 20 == 0)
        {
          gdouble fraction = ((gdouble) (y - cage_bb.y) /
                              (gdouble) (cage_bb.height));

          /*  0.0 and 1.0 indicate progress start/end, so avoid them  */
          if (fraction > 0.0 && fraction < 1.0)
            {
              oct->progress = fraction;
              g_object_notify (G_OBJECT (oct), "progress");
            }
        }
    }

  g_free (coef);
  g_slice_free1 (2 * sizeof (gfloat), coords);

  oct->progress = 1.0;
  g_object_notify (G_OBJECT (oct), "progress");

  return TRUE;
}