Logo Search packages:      
Sourcecode: inkscape version File versions

gradient-position.cpp

#define __SP_GRADIENT_POSITION_C__

/*
 * Gradient positioning widget
 *
 * Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *
 * Copyright (C) 2001-2002 Lauris Kaplinski
 * Copyright (C) 2001 Ximian, Inc.
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <string.h>
#include <libnr/nr-matrix.h>
#include <libnr/nr-matrix-ops.h>
#include <libnr/nr-pixops.h>
#include <libnr/nr-pixblock.h>
#include <gtk/gtksignal.h>
#include <glibmm/i18n.h>
#include "../display/nr-plain-stuff.h"
#include "../display/nr-plain-stuff-gdk.h"
#include "../macros.h"
#include "../prefs-utils.h"
#include "gradient-position.h"
#include "sp-gradient-fns.h"
#include "../nodepath.h" // fixme! this is only included for the Radial class, if it's moved elsewhere, update this 

#include <libnr/nr-point-fns.h>

#define RADIUS 4

enum {
    GRABBED,
    DRAGGED,
    RELEASED,
    CHANGED,
    LAST_SIGNAL
};

static void sp_gradient_position_class_init (SPGradientPositionClass *klass);
static void sp_gradient_position_init (SPGradientPosition *position);
static void sp_gradient_position_destroy (GtkObject *object);

static void sp_gradient_position_realize (GtkWidget *widget);
static void sp_gradient_position_unrealize (GtkWidget *widget);
static void sp_gradient_position_size_request (GtkWidget *widget, GtkRequisition *requisition);
static void sp_gradient_position_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
static gint sp_gradient_position_expose (GtkWidget *widget, GdkEventExpose *event);

static gint sp_gradient_position_button_press (GtkWidget *widget, GdkEventButton *event);
static gint sp_gradient_position_button_release (GtkWidget *widget, GdkEventButton *event);
static gint sp_gradient_position_motion_notify (GtkWidget *widget, GdkEventMotion *event);

static void sp_gradient_position_gradient_release (SPGradient *gr, SPGradientPosition *im);
static void sp_gradient_position_gradient_modified (SPGradient *gr, guint flags, SPGradientPosition *im);
static void sp_gradient_position_update (SPGradientPosition *img);

static void sp_gradient_position_paint (GtkWidget *widget, GdkRectangle *area);
static void sp_gradient_position_free_image_data (SPGradientPosition *pos);

void spgp_clip_line (long *c, long x0, long y0, long x1, long y1);
void spgp_draw_line (unsigned char *px, int w, int h, int rs, int x0, int y0, int x1, int y1, unsigned long c0, unsigned long c1);
void spgp_draw_rect (unsigned char *px, int w, int h, int rs, int x0, int y0, int x1, int y1, unsigned long c0, unsigned long c1);

static GtkWidgetClass *parent_class;
static guint position_signals[LAST_SIGNAL] = {0};

GtkType
sp_gradient_position_get_type (void)
{
    static GtkType type = 0;
    if (!type) {
      GtkTypeInfo info = {
          "SPGradientPosition",
          sizeof (SPGradientPosition),
          sizeof (SPGradientPositionClass),
          (GtkClassInitFunc) sp_gradient_position_class_init,
          (GtkObjectInitFunc) sp_gradient_position_init,
          NULL, NULL, NULL
      };
      type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
    }
    return type;
}

static void
sp_gradient_position_class_init (SPGradientPositionClass *klass)
{
    GtkObjectClass *object_class = (GtkObjectClass *) klass;
    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;

    parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);

    position_signals[GRABBED] = gtk_signal_new ("grabbed",
                                    (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
                                    GTK_CLASS_TYPE(object_class),
                                    GTK_SIGNAL_OFFSET (SPGradientPositionClass, grabbed),
                                    gtk_marshal_NONE__NONE,
                                    GTK_TYPE_NONE, 0);
    position_signals[DRAGGED] = gtk_signal_new ("dragged",
                                    (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
                                    GTK_CLASS_TYPE(object_class),
                                    GTK_SIGNAL_OFFSET (SPGradientPositionClass, dragged),
                                    gtk_marshal_NONE__NONE,
                                    GTK_TYPE_NONE, 0);
    position_signals[RELEASED] = gtk_signal_new ("released",
                                     (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
                                     GTK_CLASS_TYPE(object_class),
                                     GTK_SIGNAL_OFFSET (SPGradientPositionClass, released),
                                     gtk_marshal_NONE__NONE,
                                     GTK_TYPE_NONE, 0);
    position_signals[CHANGED] = gtk_signal_new ("changed",
                                    (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
                                    GTK_CLASS_TYPE(object_class),
                                    GTK_SIGNAL_OFFSET (SPGradientPositionClass, changed),
                                    gtk_marshal_NONE__NONE,
                                    GTK_TYPE_NONE, 0);

    object_class->destroy = sp_gradient_position_destroy;

    widget_class->realize = sp_gradient_position_realize;
    widget_class->unrealize = sp_gradient_position_unrealize;
    widget_class->size_request = sp_gradient_position_size_request;
    widget_class->size_allocate = sp_gradient_position_size_allocate;
/*    widget_class->draw = sp_gradient_position_draw; */
    widget_class->expose_event = sp_gradient_position_expose;
    widget_class->button_press_event = sp_gradient_position_button_press;
    widget_class->button_release_event = sp_gradient_position_button_release;
    widget_class->motion_notify_event = sp_gradient_position_motion_notify;
}

static void
sp_gradient_position_init (SPGradientPosition *pos)
{
    /* We are widget with window */
    GTK_WIDGET_UNSET_FLAGS (pos, GTK_NO_WINDOW);

    pos->dragging = FALSE;
    pos->position = 0;

    pos->need_update = TRUE;

    pos->gradient = NULL;
    pos->bbox.x0 = pos->bbox.y0 = pos->bbox.x1 = pos->bbox.y1 = 0.0;
    pos->spread = NR_GRADIENT_SPREAD_PAD;

    nr_matrix_set_identity (&pos->gs2d);

    pos->gc = NULL;
    pos->px = NULL;
}

static void
sp_gradient_position_destroy (GtkObject *object)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (object);

    sp_gradient_position_free_image_data (pos);

    if (pos->gradient) {
      sp_signal_disconnect_by_data (pos->gradient, pos);
      pos->gradient = NULL;
    }

    if (((GtkObjectClass *) (parent_class))->destroy)
      (* ((GtkObjectClass *) (parent_class))->destroy) (object);
}

static void
sp_gradient_position_realize (GtkWidget *widget)
{
    GdkWindowAttr attributes;
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);

    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

    attributes.window_type = GDK_WINDOW_CHILD;
    attributes.x = widget->allocation.x;
    attributes.y = widget->allocation.y;
    attributes.width = widget->allocation.width;
    attributes.height = widget->allocation.height;
    attributes.wclass = GDK_INPUT_OUTPUT;
    attributes.visual = gdk_rgb_get_visual ();
    attributes.colormap = gdk_rgb_get_cmap ();
    attributes.event_mask = gtk_widget_get_events (widget);
    attributes.event_mask |= (GDK_EXPOSURE_MASK |
                        GDK_BUTTON_PRESS_MASK |
                        GDK_BUTTON_RELEASE_MASK |
                        GDK_POINTER_MOTION_MASK |
                        GDK_ENTER_NOTIFY_MASK |
                        GDK_LEAVE_NOTIFY_MASK);
    gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
    gdk_window_set_user_data (widget->window, widget);

    pos->need_update = TRUE;
}

static void
sp_gradient_position_unrealize (GtkWidget *widget)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);

    if (((GtkWidgetClass *) parent_class)->unrealize)
      (* ((GtkWidgetClass *) parent_class)->unrealize) (widget);

    sp_gradient_position_free_image_data (pos);

    pos->need_update = TRUE;
}

static void
sp_gradient_position_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
    requisition->width = 128;
    requisition->height = 128;
}

static void
sp_gradient_position_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);

    widget->allocation = *allocation;

    if (GTK_WIDGET_REALIZED (widget)) {
      sp_gradient_position_free_image_data (pos);
      pos->need_update = TRUE;
      gdk_window_move_resize (widget->window,
                        widget->allocation.x, widget->allocation.y,
                        widget->allocation.width, widget->allocation.height);
    }
}

static gint
sp_gradient_position_expose (GtkWidget *widget, GdkEventExpose *event)
{
    if (GTK_WIDGET_DRAWABLE (widget)) {
      sp_gradient_position_paint (widget, &event->area);
    }

    return TRUE;
}

static gint
sp_gradient_position_button_press (GtkWidget *widget, GdkEventButton *event)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);

    if (!pos->gradient) return FALSE;

    NR::Point const mouse(event->x, event->y);
    NR::Point const mouse_gs( mouse * NR::Matrix(pos->w2gs) );

    if (pos->mode == SP_GRADIENT_POSITION_MODE_LINEAR) {
      /* Linear mode */
      if (event->button == 1) {
          pos->changed = FALSE;
          g_signal_emit (G_OBJECT (pos), position_signals[GRABBED], 0);
          double d = NR::L2( mouse - ( pos->linear.start * NR::Matrix(pos->gs2w) ) );
          if (d < RADIUS) {
            pos->dragging = 1;
            pos->linear.start = mouse_gs;
          }
          d = NR::L2( mouse - ( pos->linear.end * NR::Matrix(pos->gs2w) ) );
          if (d < RADIUS) {
            pos->dragging = 2;
            pos->linear.end = mouse_gs;
          }
          
          if(!pos->dragging) {
/* If we haven't found a knot then we do the start + end drag thing */
            pos->linear.start = mouse_gs;
            pos->dragging = 2;
          }
          
      }
    } else {
      /* Radial mode */
      if (event->button == 1) {
          pos->changed = FALSE;
          g_signal_emit (G_OBJECT (pos), position_signals[GRABBED], 0);
          double d = NR::L2( mouse - ( pos->radial.f * NR::Matrix(pos->gs2w) ) );
          //g_print("d = %f\n", d);
          if (d < RADIUS) {
            pos->dragging = 2;
            pos->radial.f = mouse_gs;
          }
          
          // Preference is given to moving the center, unless the
          // shift key is down
          d = NR::L2( mouse - ( pos->radial.center * NR::Matrix(pos->gs2w) ) );
          //g_print("d = %f\n", d);
          if ((d < RADIUS) && !(event->state & GDK_SHIFT_MASK)) {
            pos->dragging = 1;
            NR::Point delta = pos->radial.f - pos->radial.center;
            pos->radial.center = mouse_gs;
            pos->radial.f = mouse_gs + delta;
          }
          d = fabs(NR::L2(mouse_gs - pos->radial.center) - pos->radial.r);
          //g_print("d = %f, r = %f\n", d, pos->radial.r);
          if (d < 0.1) {
            /* the user has grabbed the outside of the circle */
            pos->dragging = 3;
            pos->radial.r = NR::L2 (mouse_gs - pos->radial.center);
          }
          
          if(!pos->dragging) {
/* If we haven't found a knot then we do the center and focus drag thing */
            pos->radial.f = pos->radial.center = mouse_gs;
            pos->dragging = 3;
          }
      }
    }
    if(pos->dragging) {
      g_signal_emit (G_OBJECT (pos), position_signals[DRAGGED], 0);
      pos->changed = TRUE;
      pos->need_update = TRUE;
      if (GTK_WIDGET_DRAWABLE (pos))
          gtk_widget_queue_draw (GTK_WIDGET (pos));
      
      gdk_pointer_grab (widget->window, FALSE,
                    (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
                    NULL, NULL, event->time);
    }

    return FALSE;
}

static gint
sp_gradient_position_button_release (GtkWidget *widget, GdkEventButton *event)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);

    if ((event->button == 1) && pos->dragging) {
      g_signal_emit (G_OBJECT (pos), position_signals[RELEASED], 0);
      if (pos->changed) {
          g_signal_emit (G_OBJECT (pos), position_signals[CHANGED], 0);
          pos->changed = FALSE;
      }
      gdk_pointer_ungrab (event->time);
      pos->dragging = FALSE;
    }

    return FALSE;
}

static gint
sp_gradient_position_motion_notify (GtkWidget *widget, GdkEventMotion *event)
{
    SPGradientPosition *pos = SP_GRADIENT_POSITION (widget);
    int snaps = prefs_get_int_attribute ("options.rotationsnapsperpi", "value", 12);

    NR::Point const mouse_gs = NR::Point(event->x, event->y) * NR::Matrix(pos->w2gs);
    
    if (pos->mode == SP_GRADIENT_POSITION_MODE_LINEAR) {
      switch(pos->dragging) {
      case 1:
            if (event->state & GDK_CONTROL_MASK) {
                  Radial mouse (mouse_gs - pos->linear.end);
                  Radial old (pos->linear.start - pos->linear.end);

                  double a_snapped, a_ortho;
                  // the closest PI/snaps angle, starting from zero
                  a_snapped = floor (mouse.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
                  // the closest PI/2 angle, starting from original angle (i.e. snapping to original, its opposite and perpendiculars)
                  a_ortho = old.a + floor ((mouse.a - old.a)/(M_PI/2) + 0.5) * (M_PI/2);

                  // snap to the closest
                  if (fabs (a_snapped - mouse.a) < fabs (a_ortho - mouse.a))
                        mouse.a = a_snapped;
                  else 
                        mouse.a = a_ortho;

                  pos->linear.start = pos->linear.end + NR::Point (mouse);
            } else {
                  pos->linear.start = mouse_gs;
            }
          break;
      case 2:
            if (event->state & GDK_CONTROL_MASK) {
                  Radial mouse (mouse_gs - pos->linear.start);
                  Radial old (pos->linear.end - pos->linear.start);

                  double a_snapped, a_ortho;
                  // the closest PI/snaps angle, starting from zero
                  a_snapped = floor (mouse.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
                  // the closest PI/2 angle, starting from original angle (i.e. snapping to original, its opposite and perpendiculars)
                  a_ortho = old.a + floor ((mouse.a - old.a)/(M_PI/2) + 0.5) * (M_PI/2);

                  // snap to the closest
                  if (fabs (a_snapped - mouse.a) < fabs (a_ortho - mouse.a))
                        mouse.a = a_snapped;
                  else 
                        mouse.a = a_ortho;

                  pos->linear.end = pos->linear.start + NR::Point (mouse);
            } else {
                  pos->linear.end = mouse_gs;
            }
          break;
      default:
          break;
      }
    } else {
      switch(pos->dragging) {
      case 1: {
          NR::Point delta = pos->radial.f - pos->radial.center;
          pos->radial.center = mouse_gs;
          pos->radial.f = mouse_gs + delta;
          break;
      }
      case 2:
          pos->radial.f = mouse_gs;
          break;
      case 3:
          pos->radial.r = NR::L2 (mouse_gs - pos->radial.center);
          break;
      }
    }
    if(pos->dragging) {
      g_signal_emit (G_OBJECT (pos), position_signals[DRAGGED], 0);
      pos->changed = TRUE;
      pos->need_update = TRUE;
      if (GTK_WIDGET_DRAWABLE (pos))
          gtk_widget_queue_draw (GTK_WIDGET (pos));
    }
    return FALSE;
}

GtkWidget *
sp_gradient_position_new (SPGradient *gradient)
{
    SPGradientPosition *position = (SPGradientPosition*)gtk_type_new (SP_TYPE_GRADIENT_POSITION);

    sp_gradient_position_set_gradient (position, gradient);

    return (GtkWidget *) position;
}

void
sp_gradient_position_set_gradient (SPGradientPosition *pos, SPGradient *gradient)
{
    if (pos->gradient) {
      sp_signal_disconnect_by_data (pos->gradient, pos);
    }

    pos->gradient = gradient;

    if (gradient) {
      g_signal_connect (G_OBJECT (gradient), "release", G_CALLBACK (sp_gradient_position_gradient_release), pos);
      g_signal_connect (G_OBJECT (gradient), "modified", G_CALLBACK (sp_gradient_position_gradient_modified), pos);
    }

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) {
      gtk_widget_queue_draw (GTK_WIDGET (pos));
    }
}

static void
sp_gradient_position_gradient_release (SPGradient *gradient, SPGradientPosition *pos)
{
    sp_gradient_position_set_gradient (pos, NULL);
}

static void
sp_gradient_position_gradient_modified (SPGradient *gradient, guint flags, SPGradientPosition *pos)
{
    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) {
      gtk_widget_queue_draw (GTK_WIDGET (pos));
    }
}

void
sp_gradient_position_set_mode (SPGradientPosition *pos, guint mode)
{
    if (pos->mode != mode) {
      pos->mode = mode;
      if (pos->mode == SP_GRADIENT_POSITION_MODE_LINEAR) {
          pos->linear.start = NR::Point(0, 0.5);
          pos->linear.end = NR::Point(1, 0.5);
      } else {
          pos->radial.center = NR::Point(0.5, 0.5);
          pos->radial.f = NR::Point(0.5, 0.5);
          pos->radial.r = 0.5;
      }
      pos->need_update = TRUE;
      if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
    }
}

void
sp_gradient_position_set_bbox (SPGradientPosition *pos, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
{
    g_return_if_fail (x1 >= x0);
    g_return_if_fail (y1 >= y0);

    pos->bbox.x0 = x0;
    pos->bbox.y0 = y0;
    pos->bbox.x1 = x1;
    pos->bbox.y1 = y1;

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
}

void
sp_gradient_position_set_gs2d_matrix(SPGradientPosition *pos, NR::Matrix const &gs2d)
{
    pos->gs2d = gs2d;

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) {
        gtk_widget_queue_draw(GTK_WIDGET(pos));
    }
}

void
sp_gradient_position_set_gs2d_matrix_f(SPGradientPosition *pos, NRMatrix const *gs2d)
{
    pos->gs2d = *gs2d;

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
}

void
sp_gradient_position_get_gs2d_matrix_f (SPGradientPosition *pos, NRMatrix *gs2d)
{
    *gs2d = pos->gs2d;
}

void
sp_gradient_position_set_linear_position (SPGradientPosition *pos, float x1, float y1, float x2, float y2)
{
    pos->linear.start = NR::Point(x1, y1);
    pos->linear.end = NR::Point(x2, y2);

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
}

void
sp_gradient_position_set_radial_position (SPGradientPosition *pos, float cx, float cy, float fx, float fy, float r)
{
    pos->radial.center = NR::Point(cx, cy);
    pos->radial.f = NR::Point(fx, fy);
    pos->radial.r = r;

    pos->need_update = TRUE;
    if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
}

void
sp_gradient_position_set_spread (SPGradientPosition *pos, unsigned int spread)
{
    if (spread != pos->spread) {
      pos->spread = spread;
      pos->need_update = TRUE;
      if (GTK_WIDGET_DRAWABLE (pos)) gtk_widget_queue_draw (GTK_WIDGET (pos));
    }
}

void
sp_gradient_position_get_linear_position_floatv (SPGradientPosition *gp, float *pos)
{
    pos[0] = gp->linear.start[0];
    pos[1] = gp->linear.start[1];
    pos[2] = gp->linear.end[0];
    pos[3] = gp->linear.end[1];
}

void
sp_gradient_position_get_radial_position_floatv (SPGradientPosition *gp, float *pos)
{
    pos[0] = gp->radial.center[0];
    pos[1] = gp->radial.center[1];
    pos[2] = gp->radial.f[0];
    pos[3] = gp->radial.f[1];
    pos[4] = gp->radial.r;
}

static void
sp_gradient_position_update (SPGradientPosition *pos)
{
    GtkWidget *widget;
    int width, height;
    gdouble xs, ys;

    widget = GTK_WIDGET (pos);
    width = widget->allocation.width;
    height = widget->allocation.height;

    pos->need_update = FALSE;

    /* Create image data */
    if (!pos->px) pos->px = gdk_pixmap_new (widget->window, 64, 64, -1);
    if (!pos->gc) pos->gc = gdk_gc_new (widget->window);

    /* Calculate bbox */
            if (pos->bbox.x1 > pos->bbox.x0)
                  xs = width / (pos->bbox.x1 - pos->bbox.x0);
            else 
                  xs = 1e18;
            if (pos->bbox.y1 > pos->bbox.y0)
                  ys = height / (pos->bbox.y1 - pos->bbox.y0);
            else 
                  ys = 1e18;

    if (xs > ys) {
      pos->vbox.x0 = (short) floor (width * (1 - ys / xs) / 2.0);
      pos->vbox.y0 = 0;
    } else if (xs < ys) {
      pos->vbox.x0 = 0;
      pos->vbox.y0 = (short) floor (height * (1 - xs / ys) / 2.0);
    } else {
      pos->vbox.x0 = 0;
      pos->vbox.y0 = 0;
    }
    pos->vbox.x1 = widget->allocation.width - pos->vbox.x0;
    pos->vbox.y1 = widget->allocation.height - pos->vbox.y0;

    /* Calculate w2d */
            if (pos->bbox.x1 > pos->bbox.x0)
                  pos->w2d.c[0] = (pos->bbox.x1 - pos->bbox.x0) / (pos->vbox.x1 - pos->vbox.x0);
            else 
                  pos->w2d.c[0] = 1e18;
    pos->w2d.c[1] = 0.0;
    pos->w2d.c[2] = 0.0;
            if (pos->bbox.y1 > pos->bbox.y0)
                  pos->w2d.c[3] = (pos->bbox.y1 - pos->bbox.y0) / (pos->vbox.y1 - pos->vbox.y0);
            else 
                  pos->w2d.c[3] = 1e18;
    pos->w2d.c[4] = pos->bbox.x0 - (pos->vbox.x0 * pos->w2d.c[0]);
    pos->w2d.c[5] = pos->bbox.y0 - (pos->vbox.y0 * pos->w2d.c[3]);
    /* Calculate d2w */
    nr_matrix_invert (&pos->d2w, &pos->w2d);
    /* Calculate wbox */
    pos->wbox.x0 = pos->w2d.c[4];
    pos->wbox.x0 = pos->w2d.c[5];
    pos->wbox.x1 = pos->wbox.x0 + pos->w2d.c[0] * width;
    pos->wbox.y1 = pos->wbox.y0 + pos->w2d.c[1] * height;
    /* w2gs and gs2w */
    nr_matrix_multiply (&pos->gs2w, &pos->gs2d, &pos->d2w);
    nr_matrix_invert (&pos->w2gs, &pos->gs2w);

    if (!pos->cv) pos->cv = g_new (guchar, 4 * NR_GRADIENT_VECTOR_LENGTH);
    sp_gradient_render_vector_line_rgba (pos->gradient, pos->cv, NR_GRADIENT_VECTOR_LENGTH, 0 , NR_GRADIENT_VECTOR_LENGTH);

    if (pos->mode == SP_GRADIENT_POSITION_MODE_LINEAR) {
      nr_lgradient_renderer_setup (&pos->renderer.lgr, pos->cv, pos->spread, &pos->gs2w,
                             pos->linear.start[0], pos->linear.start[1],
                             pos->linear.end[0], pos->linear.end[1]);
    } else {
      nr_rgradient_renderer_setup (&pos->renderer.rgr, pos->cv, pos->spread, &pos->gs2w,
                             pos->radial.center[0], pos->radial.center[1],
                             pos->radial.f[0], pos->radial.f[1],
                             pos->radial.r);
    }
}

static void
sp_gradient_position_free_image_data (SPGradientPosition *pos)
{
    if (pos->px) {
      gdk_pixmap_unref (pos->px);
      pos->px = NULL;
    }
    if (pos->gc) {
      gdk_gc_unref (pos->gc);
      pos->gc = NULL;
    }
    if (pos->cv) {
      g_free (pos->cv);
      pos->cv = NULL;
    }
}

static void
sp_gradient_position_paint (GtkWidget *widget, GdkRectangle *area)
{
    SPGradientPosition *gp;
    gint x, y;

    gp = SP_GRADIENT_POSITION (widget);

    if (!gp->gradient ||
      (gp->bbox.x0 > gp->bbox.x1) ||
      (gp->bbox.y0 > gp->bbox.y1) ||
      (widget->allocation.width < 1) ||
      (widget->allocation.height < 1)) {
      /* Draw empty thing */
      if (!gp->gc) gp->gc = gdk_gc_new (widget->window);
      nr_gdk_draw_gray_garbage (widget->window, gp->gc, area->x, area->y, area->width, area->height);
      return;
    }

    if (gp->need_update) {
      sp_gradient_position_update (gp);
    }

    if (gp->mode == SP_GRADIENT_POSITION_MODE_LINEAR) {
      float x1, y1, x2, y2, cx, cy, dx, dy;
      long wx1, wy1, wx2, wy2, c[4];

      x1 = gp->linear.start[0];
      y1 = gp->linear.start[1];
      x2 = gp->linear.end[0];
      y2 = gp->linear.end[1];

      wx1 = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, x1, y1) + 0.5);
      wy1 = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, x1, y1) + 0.5);
      wx2 = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, x2, y2) + 0.5);
      wy2 = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, x2, y2) + 0.5);

      cx = (x1 + x2) / 2.0 + y1 - y2;
      cy = (y1 + y2) / 2.0 + x2 - x1;
      c[0] = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, cx, cy) + 0.5);
      c[1] = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, cx, cy) + 0.5);
      dx = (x1 + x2) / 2.0 - y1 + y2;
      dy = (y1 + y2) / 2.0 - x2 + x1;
      c[2] = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, dx, dy) + 0.5);
      c[3] = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, dx, dy) + 0.5);

      spgp_clip_line (c, area->x + 4, area->y + 4, area->x + area->width - 4, area->y + area->height - 4);

      for (y = area->y; y < area->y + area->height; y += 64) {
          for (x = area->x; x < area->x + area->width; x += 64) {
            int w, h;
            NRPixBlock pb;

            w = MIN (x + 64, area->x + area->width) - x;
            h = MIN (y + 64, area->y + area->height) - y;

            /* Set up pixblock */
            nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8, x, y, x + w, y + h, FALSE);
            /* Draw checkerboard */
            nr_render_checkerboard_rgb (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, x, y);
            pb.empty = FALSE;

            /* Render gradient */
            nr_render ((NRRenderer *) &gp->renderer.lgr, &pb, NULL);

            /* Draw controls */
            spgp_draw_line (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, wx1 - x, wy1 - y, wx2 - x, wy2 - y,
                                                            //                      0xfff7f77f, 0x7f7f7f7f);
                                                                                    0x000000ff, 0x000000ff);
            spgp_draw_line (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        c[0] - x, c[1] - y,
                        c[2] - x, c[3] - y,
                                                            //0x7f7fffff, 0x7f7f7fff);
                        0xff0000ff, 0xff0000ff);

            spgp_draw_rect (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        wx1 - RADIUS - x, wy1 - RADIUS - y,
                        wx1 + RADIUS - x, wy1 + RADIUS - y,
                                                            //                      0xff7f7f7f, 0x7f7f7f7f);
                        0x0000007f, 0x000000ff);
            spgp_draw_rect (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        wx2 - RADIUS - x, wy2 - RADIUS - y,
                        wx2 + RADIUS - x, wy2 + RADIUS - y,
                        0x0000007f, 0x000000ff);

            /* Draw pixmap */
            gdk_gc_set_function (gp->gc, GDK_COPY);
            gdk_draw_rgb_image (gp->px, gp->gc, 0, 0, w, h, GDK_RGB_DITHER_MAX, NR_PIXBLOCK_PX (&pb), pb.rs);

            /* Draw bbox */
            gdk_gc_set_function (gp->gc, GDK_INVERT);
            gdk_draw_rectangle (gp->px, gp->gc, FALSE,
                            gp->vbox.x0 - x, gp->vbox.y0 - y,
                            gp->vbox.x1 - gp->vbox.x0 - 1, gp->vbox.y1 - gp->vbox.y0 - 1);
            /* Copy to window */
            gdk_gc_set_function (gp->gc, GDK_COPY);
            gdk_draw_pixmap (widget->window, gp->gc, gp->px, 0, 0, x, y, w, h);

            /* Release pixblock */
            nr_pixblock_release (&pb);
          }
      }
    } else {
      float cx, cy, fx, fy, r;
      long wcx, wcy, wdx, wdy, wfx, wfy, c[4];

      cx = gp->radial.center[0];
      cy = gp->radial.center[1];
      fx = gp->radial.f[0];
      fy = gp->radial.f[1];
      r = gp->radial.r;

      wcx = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, cx, cy) + 0.5);
      wcy = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, cx, cy) + 0.5);
      wdx = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, cx + r, cy) + 0.5);
      wdy = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, cx + r, cy) + 0.5);
      wfx = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, fx, fy) + 0.5);
      wfy = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, fx, fy) + 0.5);

      c[0] = wcx;
      c[1] = wcy;
      c[2] = (short) (NR_MATRIX_DF_TRANSFORM_X (&gp->gs2w, cx, cy + r) + 0.5);
      c[3] = (short) (NR_MATRIX_DF_TRANSFORM_Y (&gp->gs2w, cx, cy + r) + 0.5);

      spgp_clip_line (c, area->x + 4, area->y + 4, area->x + area->width - 4, area->y + area->height - 4);

      for (y = area->y; y < area->y + area->height; y += 64) {
          for (x = area->x; x < area->x + area->width; x += 64) {
            int w, h;
            NRPixBlock pb;

            w = MIN (x + 64, area->x + area->width) - x;
            h = MIN (y + 64, area->y + area->height) - y;

            /* Set up pixblock */
            nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8, x, y, x + w, y + h, FALSE);
            /* Draw checkerboard */
            nr_render_checkerboard_rgb (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, x, y);
            pb.empty = FALSE;

            /* Render gradient */
            nr_render ((NRRenderer *) &gp->renderer.rgr, &pb, NULL);

            /* Draw controls */
            spgp_draw_line (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, wcx - x, wcy - y, wdx - x, wdy - y,
                        0x000000ff, 0x000000ff);
            spgp_draw_line (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        c[0] - x, c[1] - y,
                        c[2] - x, c[3] - y,
                        0x000000ff, 0x000000ff);

            // center
            spgp_draw_rect (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        wcx - RADIUS - x, wcy - RADIUS - y,
                        wcx + RADIUS - x, wcy + RADIUS - y,
                        0x0000007f, 0x000000ff);
            // radius 
            spgp_draw_rect (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        wdx - RADIUS - x, wcy - RADIUS - y,
                        wdx + RADIUS - x, wcy + RADIUS - y,
                        0x0000007f, 0x000000ff);
            // focus
            spgp_draw_rect (NR_PIXBLOCK_PX (&pb), w, h, pb.rs,
                        wfx - RADIUS - x, wfy - RADIUS - y,
                        wfx + RADIUS - x, wfy + RADIUS - y,
                        0x0000007f, 0x000000ff);

            /* Draw pixmap */
            gdk_gc_set_function (gp->gc, GDK_COPY);
            gdk_draw_rgb_image (gp->px, gp->gc, 0, 0, w, h, GDK_RGB_DITHER_MAX, NR_PIXBLOCK_PX (&pb), pb.rs);

            /* Draw bbox */
            gdk_gc_set_function (gp->gc, GDK_INVERT);
            gdk_draw_rectangle (gp->px, gp->gc, FALSE,
                            gp->vbox.x0 - x, gp->vbox.y0 - y,
                            gp->vbox.x1 - gp->vbox.x0 - 1, gp->vbox.y1 - gp->vbox.y0 - 1);
            /* Copy to window */
            gdk_gc_set_function (gp->gc, GDK_COPY);
            gdk_draw_pixmap (widget->window, gp->gc, gp->px, 0, 0, x, y, w, h);

            /* Release pixblock */
            nr_pixblock_release (&pb);
          }
      }
    }
}

/* fixme: We may want to adjust it to integers (Lauris) */

void
spgp_clip_line (long *c, long x0, long y0, long x1, long y1)
{
    float px, py, vx, vy;
    float s, e, t0, t1;

    px = c[0];
    py = c[1];
    vx = c[2] - c[0];
    vy = c[3] - c[1];

    s = -NR_HUGE;
    e = NR_HUGE;

    if (!NR_DF_TEST_CLOSE (vx, 0.0, NR_EPSILON)) {
      t0 = (x0 - px) / vx;
      t1 = (x1 - px) / vx;
      s = MAX (s, MIN (t0, t1));
      e = MIN (e, MAX (t0, t1));
    }
    if (!NR_DF_TEST_CLOSE (vy, 0.0, NR_EPSILON)) {
      t0 = (y0 - py) / vy;
      t1 = (y1 - py) / vy;
      s = MAX (s, MIN (t0, t1));
      e = MIN (e, MAX (t0, t1));
    }

    c[0] = (long)(px + s * vx);
    c[1] = (long)(py + s * vy);
    c[2] = (long)(px + e * vx);
    c[3] = (long)(py + e * vy);
}

void
spgp_draw_line (guchar *px, int w, int h, int rs, int x0, int y0, int x1, int y1, unsigned long c0, unsigned long c1)
{
    long deltax, deltay, xinc1, xinc2, yinc1, yinc2;
    long den, num, numadd, numpixels;
    long x, y, curpixel;
    guchar c[8];

    if ((x0 < 0) && (x1 < 0)) return;
    if ((x0 >= w) && (x1 >= w)) return;
    if ((y0 < 0) && (y1 < 0)) return;
    if ((y0 >= h) && (y1 >= h)) return;

    if (x1 >= x0) {
      deltax = x1 - x0;
      xinc1 = 1;
      xinc2 = 1;
    } else {
      deltax = x0 - x1;
      xinc1 = -1;
      xinc2 = -1;
    }

    if (y1 >= y0) {
      deltay = y1 - y0;
      yinc1 = 1;
      yinc2 = 1;
    } else {
      deltay = y0 - y1;
      yinc1 = -1;
      yinc2 = -1;
    }

    if (deltax >= deltay) {
      xinc1 = 0;
      yinc2 = 0;
      den = deltax;
      num = deltax / 2;
      numadd = deltay;
      numpixels = deltax;
    } else {
      xinc2 = 0;
      yinc1 = 0;
      den = deltay;
      num = deltay / 2;
      numadd = deltax;
      numpixels = deltay;
    }

    c[0] = NR_RGBA32_R (c0);
    c[1] = NR_RGBA32_G (c0);
    c[2] = NR_RGBA32_B (c0);
    c[3] = NR_RGBA32_A (c0);
    c[4] = NR_RGBA32_R (c1);
    c[5] = NR_RGBA32_G (c1);
    c[6] = NR_RGBA32_B (c1);
    c[7] = NR_RGBA32_A (c1);

    x = x0;
    y = y0;

    for (curpixel = 0; curpixel <= numpixels; curpixel++) {
      if ((x >= 0) && (y >= 0) && (x < w) && (y < h)) {
          guchar *d, *s;
          d = px + y * rs + 3 * x;
          s = &c[4 * (curpixel & 0x1)];
          d[0] = INK_COMPOSE (s[0], s[3], d[0]);
          d[1] = INK_COMPOSE (s[1], s[3], d[1]);
          d[2] = INK_COMPOSE (s[2], s[3], d[2]);
      }
      num += numadd;
      if (num >= den) {
          num -= den;
          x += xinc1;
          y += yinc1;
      }
      x += xinc2;
      y += yinc2;
    }
}

void
spgp_draw_rect (guchar *px, int w, int h, int rs, int x0, int y0, int x1, int y1, unsigned long c0, unsigned long c1)
{
    int sx, sy, ex, ey, x, y;
    guchar s[4];
    guchar f[4];
    guchar *p;

    if (x0 >= w) return;
    if (y0 >= h) return;
    if (x1 < 0) return;
    if (y1 < 0) return;
 
    s[0] = NR_RGBA32_R (c0);
    s[1] = NR_RGBA32_G (c0);
    s[2] = NR_RGBA32_B (c0);
    s[3] = NR_RGBA32_A (c0);
    f[0] = NR_RGBA32_R (c1);
    f[1] = NR_RGBA32_G (c1);
    f[2] = NR_RGBA32_B (c1);
    f[3] = NR_RGBA32_A (c1);

    sx = MAX (x0, 0);
    sy = MAX (y0, 0);
    ex = MIN (x1, w - 1);
    ey = MIN (y1, h - 1);

    for (y = sy; y <= ey; y++) {
      p = px + y * rs + sx * 3;
      if ((y == y0) || (y == y1)) {
          for (x = sx; x <= ex; x++) {
            p[0] = INK_COMPOSE (s[0], s[3], p[0]);
            p[1] = INK_COMPOSE (s[0], s[3], p[0]);
            p[2] = INK_COMPOSE (s[0], s[3], p[0]);
            p += 3;
          }
      } else {
          for (x = sx; x <= ex; x++) {
            if ((x == x0) || (x == x1)) {
                p[0] = INK_COMPOSE (s[0], s[3], p[0]);
                p[1] = INK_COMPOSE (s[0], s[3], p[0]);
                p[2] = INK_COMPOSE (s[0], s[3], p[0]);
                p += 3;
            } else {
                p[0] = INK_COMPOSE (f[0], f[3], p[0]);
                p[1] = INK_COMPOSE (f[0], f[3], p[0]);
                p[2] = INK_COMPOSE (f[0], f[3], p[0]);
                p += 3;
            }
          }
      }
    }
}


/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :

Generated by  Doxygen 1.6.0   Back to index