Code

c7d1acb3e99150ba3b04daae5e918d7bbd34cc52
[inkscape.git] / src / widgets / sp-color-wheel.cpp
1 #define __SP_COLOR_WHEEL_C__
3 /*
4  * A wheel color widget
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *
10  * Copyright (C) 2001-2002 Lauris Kaplinski
11  * Copyright (C) 2003-2004 Authors
12  *
13  * This code is in public domain
14  */
16 #include <cstring>
17 #include <string>
19 #include <gtk/gtksignal.h>
20 #include "sp-color-wheel.h"
22 #include "libnr/nr-rotate-ops.h"
24 #define WHEEL_SIZE 96
26 enum {
27     CHANGED,
28     LAST_SIGNAL
29 };
31 #define noDUMP_CHANGE_INFO
32 #define FOO_NAME(x) g_type_name( G_TYPE_FROM_INSTANCE(x) )
34 static void sp_color_wheel_class_init (SPColorWheelClass *klass);
35 static void sp_color_wheel_init (SPColorWheel *wheel);
36 static void sp_color_wheel_destroy (GtkObject *object);
38 static void sp_color_wheel_realize (GtkWidget *widget);
39 static void sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition);
40 static void sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
42 static gint sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event);
43 static gint sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
44 static gint sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event);
45 static gint sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event);
47 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue);
48 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value );
49 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel);
51 static void sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area);
52 static void sp_color_wheel_render_hue_wheel (SPColorWheel *wheel);
53 static void sp_color_wheel_render_triangle (SPColorWheel *wheel);
56 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
57                                      GtkDirectionType  direction);
59 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
61 static GtkWidgetClass *parent_class;
62 static guint wheel_signals[LAST_SIGNAL] = {0};
64 /*
65 static double
66 get_time (void)
67 {
68     GTimeVal tv;
69     g_get_current_time (&tv);
70     return tv.tv_sec + 1e-6 * tv.tv_usec;
71 }
72 */
74 GtkType
75 sp_color_wheel_get_type (void)
76 {
77     static GtkType type = 0;
78     if (!type) {
79         GtkTypeInfo info = {
80             "SPColorWheel",
81             sizeof (SPColorWheel),
82             sizeof (SPColorWheelClass),
83             (GtkClassInitFunc) sp_color_wheel_class_init,
84             (GtkObjectInitFunc) sp_color_wheel_init,
85             NULL, NULL, NULL
86         };
87         type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
88     }
89     return type;
90 }
92 static void
93 sp_color_wheel_class_init (SPColorWheelClass *klass)
94 {
95     GtkObjectClass *object_class;
96     GtkWidgetClass *widget_class;
98     object_class = (GtkObjectClass *) klass;
99     widget_class = (GtkWidgetClass *) klass;
101     parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
103     wheel_signals[CHANGED] = gtk_signal_new ("changed",
104                           (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
105                           GTK_CLASS_TYPE(object_class),
106                           GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
107                           gtk_marshal_NONE__NONE,
108                           GTK_TYPE_NONE, 0);
110     object_class->destroy = sp_color_wheel_destroy;
112     widget_class->realize = sp_color_wheel_realize;
113     widget_class->size_request = sp_color_wheel_size_request;
114     widget_class->size_allocate = sp_color_wheel_size_allocate;
116     widget_class->focus = sp_color_wheel_focus;
118     widget_class->expose_event = sp_color_wheel_expose;
119     widget_class->button_press_event = sp_color_wheel_button_press;
120     widget_class->button_release_event = sp_color_wheel_button_release;
121     widget_class->motion_notify_event = sp_color_wheel_motion_notify;
124 static void
125 sp_color_wheel_init (SPColorWheel *wheel)
127     /* We are widget with window */
128     GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
129     GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
131     wheel->dragging = FALSE;
133     wheel->_inTriangle = FALSE;
134     wheel->_triDirty = TRUE;
135     wheel->_triangle = NULL;
136     for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
137     {
138         wheel->_triPoints[i].x = 0;
139         wheel->_triPoints[i].y = 0;
140     }
141     wheel->_triImage = NULL;
142     wheel->_triBs = 0;
144     wheel->_image = NULL;
145     wheel->_bs = 0;
146     wheel->_hue = 0.0;
147     wheel->_sat = 0.9;
148     wheel->_value = 0.25;
149     wheel->_inner = 0;
150     wheel->_center = 0;
151     wheel->_spotValue = 1.0;
154 static void
155 sp_color_wheel_destroy (GtkObject *object)
157     SPColorWheel *wheel;
159     wheel = SP_COLOR_WHEEL (object);
161     if ( wheel->_image )
162     {
163         g_free( wheel->_image );
164         wheel->_image = NULL;
165         wheel->_bs = 0;
166     }
168     if ( wheel->_triImage )
169     {
170         g_free( wheel->_triImage );
171         wheel->_triImage = NULL;
172         wheel->_triBs = 0;
173     }
175     if (((GtkObjectClass *) (parent_class))->destroy)
176         (* ((GtkObjectClass *) (parent_class))->destroy) (object);
180 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
182     float rgb[3];
183     gint i;
184     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
185     g_return_if_fail (wheel != NULL);
186     g_return_if_fail (color != NULL);
188     sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
189     for ( i = 0; i < 3; i++ )
190     {
191         rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
192     }
194     color->set( rgb[0], rgb[1], rgb[2] );
197 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
199 #ifdef DUMP_CHANGE_INFO
200     g_message("sp_color_wheel_set_color( wheel=%p, %f, %f, %f)", wheel, color->v.c[0], color->v.c[1], color->v.c[2] );
201 #endif
202     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
203     g_return_if_fail (wheel != NULL);
204     g_return_if_fail (color != NULL);
206     float hue;
207     float scratch[3];
208     float rgb[3];
210     sp_color_get_rgb_floatv (color, rgb);
211     sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
212     hue = scratch[0];
214     sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
216     gint lowInd = 0;
217     gint hiInd = 0;
218     for ( int i = 1; i < 3; i++ )
219     {
220         if ( scratch[i] < scratch[lowInd] )
221         {
222             lowInd = i;
223         }
224         if ( scratch[i] > scratch[hiInd] )
225         {
226             hiInd = i;
227         }
228     }
229     // scratch[lowInd] should always be 0
230     gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
231     gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
234     sp_color_wheel_set_hue(wheel, hue);
235     sp_color_wheel_set_sv(wheel, sat, val);
238 gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
240     g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
241     return wheel->dragging;
244 static void
245 sp_color_wheel_realize (GtkWidget *widget)
247     SPColorWheel *wheel;
248     GdkWindowAttr attributes;
249     gint attributes_mask;
251     wheel = SP_COLOR_WHEEL (widget);
253     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
255     attributes.window_type = GDK_WINDOW_CHILD;
256     attributes.x = widget->allocation.x;
257     attributes.y = widget->allocation.y;
258     attributes.width = widget->allocation.width;
259     attributes.height = widget->allocation.height;
260     attributes.wclass = GDK_INPUT_OUTPUT;
261     attributes.visual = gdk_rgb_get_visual ();
262     attributes.colormap = gdk_rgb_get_cmap ();
263     attributes.event_mask = gtk_widget_get_events (widget);
264     attributes.event_mask |= (GDK_EXPOSURE_MASK |
265                   GDK_BUTTON_PRESS_MASK |
266                   GDK_BUTTON_RELEASE_MASK |
267                   GDK_POINTER_MOTION_MASK |
268                   GDK_ENTER_NOTIFY_MASK |
269                   GDK_LEAVE_NOTIFY_MASK |
270                   GDK_FOCUS_CHANGE_MASK );
271     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
273     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
274     gdk_window_set_user_data (widget->window, widget);
276     widget->style = gtk_style_attach (widget->style, widget->window);
279 static void
280 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
282     SPColorWheel *wheel;
284     wheel = SP_COLOR_WHEEL (widget);
286     requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
287     requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
290 static void
291 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
293     SPColorWheel *wheel;
295     wheel = SP_COLOR_WHEEL (widget);
297     widget->allocation = *allocation;
299     wheel->_center = MIN(allocation->width, allocation->height)/2;
300     wheel->_inner = (3 * wheel->_center)/4;
301     if ( wheel->_image )
302     {
303         g_free( wheel->_image );
304         wheel->_image = NULL;
305         wheel->_bs = 0;
306     }
308     // Need to render the gradient before we do the triangle over
309     sp_color_wheel_render_hue_wheel(wheel);
310     sp_color_wheel_recalc_triangle(wheel);
311     sp_color_wheel_render_triangle(wheel);
313     if (GTK_WIDGET_REALIZED (widget)) {
314         /* Resize GdkWindow */
315         gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
316     }
319 static gint
320 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
322     SPColorWheel *wheel;
324     wheel = SP_COLOR_WHEEL (widget);
326     if (GTK_WIDGET_DRAWABLE (widget)) {
327         gint width, height;
328         width = widget->allocation.width;
329         height = widget->allocation.height;
330         sp_color_wheel_paint (wheel, &event->area);
331     }
333     return TRUE;
336 static gint
337 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
339     SPColorWheel *wheel;
341     wheel = SP_COLOR_WHEEL (widget);
343     if (event->button == 1) {
344         gint cx, cw;
345         cx = widget->style->xthickness;
346         cw = widget->allocation.width - 2 * cx;
347         gboolean grabbed = FALSE;
349         {
350             double dx = event->x - wheel->_center;
351             double dy = event->y - wheel->_center;
352             gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
353             if ( hyp <= (wheel->_center*wheel->_center) )
354             {
355                 if ( hyp >= (wheel->_inner*wheel->_inner) )
356                 {
357                     gdouble rot = atan2( dy, -dx );
358                     sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
360                     wheel->_inTriangle = FALSE;
361                     grabbed = TRUE;
362                 }
363                 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
364                 {
365                     wheel->_inTriangle = TRUE;
366                     sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
367                     grabbed = TRUE;
368                 }
369             }
370         }
372         if ( grabbed )
373         {
374             gtk_widget_queue_draw( widget );
375             gtk_widget_grab_focus( widget );
377             wheel->dragging = TRUE;
378 #ifdef DUMP_CHANGE_INFO
379             {
380                 SPColor color;
381                 sp_color_wheel_get_color( wheel, &color );
382                 g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
383                            "CHANGED",
384                            color.toRGBA32( 0 ), FOO_NAME(wheel));
385             }
386 #endif
387             gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
388             gdk_pointer_grab (widget->window, FALSE,
389                               (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
390                               NULL, NULL, event->time);
391         }
392     }
394     return TRUE;
397 static gint
398 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
400     SPColorWheel *wheel;
402     wheel = SP_COLOR_WHEEL (widget);
404     if (event->button == 1) {
405         gdk_pointer_ungrab (event->time);
406         wheel->dragging = FALSE;
408 #ifdef DUMP_CHANGE_INFO
409         {
410             SPColor color;
411             sp_color_wheel_get_color( wheel, &color );
412             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
413                        "CHANGED",
414                        color.toRGBA32( 0 ), FOO_NAME(wheel));
415         }
416 #endif
417         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
418     }
420     return TRUE;
423 static gint
424 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
426     SPColorWheel *wheel;
428     wheel = SP_COLOR_WHEEL (widget);
430     if (wheel->dragging) {
431         double dx = event->x - wheel->_center;
432         double dy = event->y - wheel->_center;
433         if ( !wheel->_inTriangle )
434         {
435             gdouble rot = atan2( dy, -dx );
436             sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
437         }
438         else
439         {
440             sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
441         }
443 #ifdef DUMP_CHANGE_INFO
444         {
445             SPColor color;
446             sp_color_wheel_get_color( wheel, &color );
447             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
448                        "CHANGED",
449                        color.toRGBA32( 0 ), FOO_NAME(wheel));
450         }
451 #endif
452         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
453     }
455     return TRUE;
458 GtkWidget *
459 sp_color_wheel_new ()
461     SPColorWheel *wheel;
463     wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
465     return GTK_WIDGET (wheel);
468 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
470     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
472     if ( wheel->_hue != hue )
473     {
474         wheel->_hue = hue;
476         sp_color_wheel_recalc_triangle(wheel);
478         SPColor color;
479         gfloat rgb[3];
480         sp_color_wheel_get_color( wheel, &color );
481         sp_color_get_rgb_floatv (&color, rgb);
483         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
485         gtk_widget_queue_draw (GTK_WIDGET (wheel));
486     }
490 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
492     static gdouble epsilon = 1e-6;
493     gboolean changed = FALSE;
495     if ( ABS( wheel->_sat - sat ) > epsilon )
496     {
497         wheel->_sat = sat;
498         changed = TRUE;
499     }
500     if ( ABS( wheel->_value - value ) > epsilon )
501     {
502         wheel->_value = value;
503         changed = TRUE;
504     }
506     if ( changed )
507     {
508         SPColor color;
509         gfloat rgb[3];
510         sp_color_wheel_get_color( wheel, &color );
511         sp_color_get_rgb_floatv (&color, rgb);
513         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
515 #ifdef DUMP_CHANGE_INFO
516         {
517             SPColor color;
518             sp_color_wheel_get_color( wheel, &color );
519             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
520                        "CHANGED",
521                        color.toRGBA32( 0 ), FOO_NAME(wheel));
522         }
523 #endif
524         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
525     }
526     gtk_widget_queue_draw (GTK_WIDGET (wheel));
529 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
531     if ( wheel->_triangle )
532     {
533         gdk_region_destroy( wheel->_triangle );
534         wheel->_triangle = NULL;
535     }
536     wheel->_triDirty = TRUE;
538     if ( wheel->_center > 0 && wheel->_inner > 0 )
539     {
540         gdouble dx = cos( M_PI * 2 * wheel->_hue );
541         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
543         wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
544         wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
546         dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
547         dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
548         wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
549         wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
551         dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
552         dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
553         wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
554         wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
557         wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
558                                                3,
559                                                GDK_EVEN_ODD_RULE );
560     }
563 #define VERT_SWAP( X, Y ) { \
564     gfloat tmpF; \
565  \
566     tmpF = point##X.x; \
567     point##X.x = point##Y.x; \
568     point##Y.x = tmpF; \
569  \
570     tmpF = point##X.y; \
571     point##X.y = point##Y.y; \
572     point##Y.y = tmpF; \
573  \
574     tmpF = rgb##X[0]; \
575     rgb##X[0] = rgb##Y[0]; \
576     rgb##Y[0] = tmpF; \
577  \
578     tmpF = rgb##X[1]; \
579     rgb##X[1] = rgb##Y[1]; \
580     rgb##Y[1] = tmpF; \
581  \
582     tmpF = rgb##X[2]; \
583     rgb##X[2] = rgb##Y[2]; \
584     rgb##Y[2] = tmpF; \
587 #define VERT_COPY( dst, src ) { \
588     point##dst.x = point##src.x; \
589  \
590     point##dst.y = point##src.y; \
591  \
592     rgb##dst[0] = rgb##src[0]; \
593     rgb##dst[1] = rgb##src[1]; \
594     rgb##dst[2] = rgb##src[2]; \
597 typedef struct {
598     gfloat x;
599     gfloat y;
600 } PointF;
602 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
604     if ( wheel->_image )
605     {
606         if ( wheel->_triBs < wheel->_bs )
607         {
608             g_free( wheel->_triImage );
609             wheel->_triImage = NULL;
610         }
612         if (wheel->_triDirty || !wheel->_triImage)
613         {
614             if ( !wheel->_triImage )
615             {
616                 wheel->_triBs = wheel->_bs;
617                 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
618                 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
619             }
621             memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
623             PointF pointA, pointB, pointC;
624             pointA.x = wheel->_triPoints[0].x;
625             pointA.y = wheel->_triPoints[0].y;
626             pointB.x = wheel->_triPoints[1].x;
627             pointB.y = wheel->_triPoints[1].y;
628             pointC.x = wheel->_triPoints[2].x;
629             pointC.y = wheel->_triPoints[2].y;
631             gfloat rgbA[3];
632             gfloat rgbB[3] = {0.0, 0.0, 0.0};
633             gfloat rgbC[3] = {1.0, 1.0, 1.0};
635             sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
637 // Start of Gouraud fill ============================================================
638             gint rowStride = wheel->_center * 2 * 3;
639             guchar* dst = wheel->_triImage;
641             if ( pointC.y < pointB.y )
642                 VERT_SWAP( C, B );
644             if ( pointC.y < pointA.y )
645                 VERT_SWAP( C, A );
647             if ( pointB.y < pointA.y )
648                 VERT_SWAP( B, A );
650             if ( pointA.y == pointB.y && pointB.x < pointA.x )
651                 VERT_SWAP( A, B );
653             gfloat dr, dg, db;
655             gfloat dx1,dx2,dx3;
656             gfloat dr1,dr2,dr3;
657             gfloat dg1,dg2,dg3;
658             gfloat db1,db2,db3;
661             PointF pointS;
662             PointF pointE;
663             PointF pointP;
664             gfloat rgbS[3];
665             gfloat rgbE[3];
666             gfloat rgbP[3];
669             if (pointB.y-pointA.y > 0)
670             {
671                 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
672                 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
673                 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
674                 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
675             }
676             else
677             {
678                 dx1=dr1=dg1=db1=0;
679             }
681             if (pointC.y-pointA.y > 0)
682             {
683                 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
684                 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
685                 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
686                 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
687             }
688             else
689             {
690                 dx2=dr2=dg2=db2=0;
691             }
693             if (pointC.y-pointB.y > 0)
694             {
695                 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
696                 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
697                 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
698                 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
699             }
700             else
701             {
702                 dx3=dr3=dg3=db3=0;
703             }
705             VERT_COPY(S, A);
706             VERT_COPY(E, A);
708             if ( dx1 == 0 )
709             {
710                 VERT_COPY(E,B);
711                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
712                 {
713                     if (pointE.x-pointS.x > 0)
714                     {
715                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
716                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
717                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
718                     }
719                     else
720                     {
721                         dr=dg=db=0;
722                     }
723                     VERT_COPY(P,S);
724                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
725                     dst += static_cast<gint>(pointP.x) * 3;
726                     for(;pointP.x < pointE.x;pointP.x++)
727                     {
728                         //putpixel(P);
729                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
730                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
731                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
732                         dst += 3;
733                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
734                     }
735                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
736                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
737                 }
738             }
739             else if (dx1 > dx2)
740             {
741                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
742                 {
743                     if (pointE.x-pointS.x > 0)
744                     {
745                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
746                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
747                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
748                     }
749                     else
750                     {
751                         dr=dg=db=0;
752                     }
753                     VERT_COPY(P,S);
754                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
755                     dst += static_cast<gint>(pointP.x) * 3;
756                     for(;pointP.x < pointE.x;pointP.x++)
757                     {
758                         //putpixel(P);
759                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
760                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
761                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
762                         dst += 3;
763                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
764                     }
765                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
766                     pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
767                 }
768                 VERT_COPY(E,B);
769                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
770                 {
771                     if (pointE.x-pointS.x > 0)
772                     {
773                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
774                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
775                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
776                     }
777                     else
778                     {
779                         dr=dg=db=0;
780                     }
781                     VERT_COPY(P,S);
782                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
783                     dst += static_cast<gint>(pointP.x) * 3;
784                     for(;pointP.x < pointE.x;pointP.x++)
785                     {
786                         //putpixel(P);
787                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
788                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
789                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
790                         dst += 3;
791                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
792                     }
793                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
794                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
795                 }
796             }
797             else if ( dx1 )
798             {
799                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
800                 {
801                     if (pointE.x-pointS.x > 0)
802                     {
803                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
804                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
805                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
806                     }
807                     else
808                     {
809                         dr=dg=db=0;
810                     }
811                     VERT_COPY(P,S);
812                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
813                     dst += static_cast<gint>(pointP.x) * 3;
814                     for(;pointP.x < pointE.x;pointP.x++)
815                     {
816                         //putpixel(P);
817                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
818                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
819                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
820                         dst += 3;
821                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
822                     }
823                     pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
824                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
825                 }
826                 VERT_COPY(S,B);
827                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
828                 {
829                     if (pointE.x-pointS.x > 0)
830                     {
831                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
832                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
833                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
834                     }
835                     else
836                     {
837                         dr=dg=db=0;
838                     }
839                     VERT_COPY(P,S);
840                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
841                     dst += static_cast<gint>(pointP.x) * 3;
842                     for(;pointP.x < pointE.x;pointP.x++)
843                     {
844                         //putpixel(P);
845                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
846                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
847                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
848                         dst += 3;
849                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
850                     }
851                     pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
852                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
853                 }
854             }
856 // End of Gouraud fill  ============================================================
858             wheel->_triDirty = FALSE;
859             //g_message( "Just updated triangle" );
860         }
861     }
864 static void
865 sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
867     GtkWidget *widget;
868     GdkRectangle warea, carea;
869     GdkRectangle wpaint, cpaint;
871     widget = GTK_WIDGET (wheel);
873     /* Widget area */
874     warea.x = 0;
875     warea.y = 0;
876     warea.width = widget->allocation.width;
877     warea.height = widget->allocation.height;
879     /* Color gradient area */
880     carea.x = widget->style->xthickness;
881     carea.y = widget->style->ythickness;
882     carea.width = widget->allocation.width - 2 * carea.x;
883     carea.height = widget->allocation.height - 2 * carea.y;
885     /* Actual paintable area */
886     if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
888     //g_message( "Painted as state %d", widget->state );
890     /* Paintable part of color gradient area */
891     if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
892         sp_color_wheel_render_hue_wheel (wheel);
893         sp_color_wheel_render_triangle (wheel);
894     }
896 /*
897     gtk_draw_box (widget->style,
898                   widget->window,
899                   (GtkStateType)widget->state,
900                   GTK_SHADOW_NONE,
901                   warea.x,
902                   warea.y,
903                   warea.width,
904                   warea.height);
905 */
907     gtk_style_apply_default_background( widget->style,
908                                         widget->window,
909                                         TRUE,
910                                         (GtkStateType)widget->state,
911                                         NULL,
912                                         0,
913                                         0,
914                                         warea.width,
915                                         warea.height);
918     /* Draw shadow */
919 /*
920     gtk_paint_shadow (widget->style, widget->window,
921               (GtkStateType)widget->state, GTK_SHADOW_IN,
922               NULL, widget, "colorwheel",
923               0, 0,
924               warea.width, warea.height);
925 */
928     /* Draw pixelstore */
929     if (wheel->_triImage != NULL) {
930         //gdouble start, end;
931         //start = get_time();
932         gdk_draw_rgb_image (widget->window, widget->style->black_gc,
933                             0, 0,//cpaint.x, cpaint.y,
934                             //cpaint.width, cpaint.height,
935                             wheel->_center * 2, wheel->_center * 2,
936                             GDK_RGB_DITHER_MAX,
937                             wheel->_triImage, wheel->_center * 6);
939         //end = get_time();
940         //g_message( "blits took %f", (end-start) );
941     }
943     {
944         gdouble dx = cos( M_PI * 2 * wheel->_hue );
945         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
947         gfloat rgb[3];
948         sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
950         GdkGC *line_gc = (( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) ) < 0.5) ? widget->style->white_gc : widget->style->black_gc;
952         gint inx = wheel->_center + static_cast<gint>(dx * wheel->_inner);
953         gint iny = wheel->_center + static_cast<gint>(dy * wheel->_inner);
956         gdk_draw_line (widget->window, line_gc,
957                        inx, iny,
958                        wheel->_center + static_cast<gint>(dx * wheel->_center), wheel->_center + static_cast<gint>(dy * wheel->_center) );
961         GdkGCValues values;
963         if ( GTK_WIDGET_HAS_FOCUS(wheel) )
964         {
965             line_gc = widget->style->black_gc;
967             gdk_gc_get_values ( line_gc, &values );
969             gdk_gc_set_line_attributes ( line_gc,
970                                          3, // Line width
971                                          values.line_style, //GDK_LINE_SOLID,
972                                          values.cap_style, //GDK_CAP_BUTT,
973                                          values.join_style ); //GDK_JOIN_MITER );
975             if ( wheel->_inTriangle )
976             {
977                 gdk_draw_line (widget->window, line_gc,
978                                wheel->_triPoints[0].x, wheel->_triPoints[0].y,
979                                wheel->_triPoints[1].x, wheel->_triPoints[1].y );
981                 gdk_draw_line (widget->window, line_gc,
982                                wheel->_triPoints[1].x, wheel->_triPoints[1].y,
983                                wheel->_triPoints[2].x, wheel->_triPoints[2].y );
985                 gdk_draw_line (widget->window, line_gc,
986                                wheel->_triPoints[2].x, wheel->_triPoints[2].y,
987                                wheel->_triPoints[0].x, wheel->_triPoints[0].y );
988             }
989             else
990             {
991                 gdk_draw_arc (widget->window, line_gc,
992                               FALSE, // filled
993                               0, 0,
994                               wheel->_center * 2, wheel->_center * 2,
995                               0, 64 * 360 );
997                 gint diff = wheel->_center - wheel->_inner;
999                 gdk_draw_arc (widget->window, line_gc,
1000                               FALSE, // filled
1001                               diff, diff,
1002                               wheel->_inner * 2, wheel->_inner * 2,
1003                               0, 64 * 360 );
1005             }
1006             gdk_gc_set_line_attributes ( line_gc,
1007                                          values.line_width, // Line width
1008                                          values.line_style, //GDK_LINE_SOLID,
1009                                          values.cap_style, //GDK_CAP_BUTT,
1010                                          values.join_style ); //GDK_JOIN_MITER );
1011         }
1012 // ==========
1014 //        line_gc = (p[3] < 0x80) ? widget->style->white_gc : widget->style->black_gc;
1015         line_gc = (wheel->_spotValue < 0.5) ? widget->style->white_gc : widget->style->black_gc;
1017         gdk_gc_get_values ( line_gc, &values );
1019         gdk_gc_set_line_attributes ( line_gc,
1020                                      2, // Line width
1021                                      values.line_style, //GDK_LINE_SOLID,
1022                                      values.cap_style, //GDK_CAP_BUTT,
1023                                      values.join_style ); //GDK_JOIN_MITER );
1025         gint pointX = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].x + wheel->_value*(gdouble)wheel->_triPoints[2].x)
1026             + (wheel->_sat * wheel->_triPoints[0].x) );
1028         gint pointY = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].y + wheel->_value*(gdouble)wheel->_triPoints[2].y)
1029             + (wheel->_sat * wheel->_triPoints[0].y) );
1032         gdk_gc_set_line_attributes ( line_gc,
1033                                      values.line_width, // Line width
1034                                      values.line_style, //GDK_LINE_SOLID,
1035                                      values.cap_style, //GDK_CAP_BUTT,
1036                                      values.join_style ); //GDK_JOIN_MITER );
1038         gdk_draw_arc (widget->window, line_gc,
1039                       FALSE, // filled
1040                       pointX - 4, pointY - 4,
1041                       8, 8,
1042                       0, 64 * 360 );
1044         gdk_draw_arc (widget->window, line_gc,
1045                       FALSE, // filled
1046                       pointX - 3, pointY - 3,
1047                       6, 6,
1048                       0, 64 * 360 );
1052     }
1055 /* Colors are << 16 */
1057 static void
1058 sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
1060     guchar *dp;
1061     gint x, y;
1062     guint r, g, b;
1063     gint size = wheel->_center * 2;
1064     gboolean dirty = FALSE;
1066     if (wheel->_image && (wheel->_bs < (size * size) )) {
1067         g_free (wheel->_image);
1068         wheel->_image = NULL;
1069         wheel->_bs = 0;
1071         if ( wheel->_triImage )
1072         {
1073             g_free( wheel->_triImage );
1074             wheel->_triImage = NULL;
1075             wheel->_triBs = 0;
1076             wheel->_triDirty = TRUE;
1077         }
1078     }
1080     if (!wheel->_image) {
1081         wheel->_image = g_new (guchar, size * size * 3);
1082         wheel->_bs = size * size;
1083         dirty = TRUE;
1084         //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
1085     }
1087     if ( dirty )
1088     {
1089         GtkWidget* widget = GTK_WIDGET (wheel);
1090         dp = wheel->_image;
1091         r = widget->style->bg[widget->state].red >> 8;
1092         g = widget->style->bg[widget->state].green >> 8;
1093         b = widget->style->bg[widget->state].blue >> 8;
1094         //g_message( "Rendered as state %d", widget->state );
1096         gint offset = wheel->_center;
1097         gint inner = wheel->_inner * wheel->_inner;
1098         gint rad = wheel->_center * wheel->_center;
1100         for (x = 0; x < size; x++) {
1101             guchar *d = dp;
1102             for (y = 0; y < size; y++) {
1103                 gint dx = x - offset;
1104                 gint dy = y - offset;
1105                 gint hyp = (ABS(dx*dx) + ABS(dy*dy));
1106                 if ( hyp >= inner && hyp <= rad)
1107                 {
1108                     gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
1110                     gfloat rgb[3];
1111                     sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
1113                     d[0] = SP_COLOR_F_TO_U (rgb[0]);
1114                     d[1] = SP_COLOR_F_TO_U (rgb[1]);
1115                     d[2] = SP_COLOR_F_TO_U (rgb[2]);
1116                 }
1117                 else
1118                 {
1119                     /* Background value */
1120                     d[0] = r;
1121                     d[1] = g;
1122                     d[2] = b;
1123                 }
1125                 d += 3 * size;
1126             }
1127             dp += 3;
1128         }
1129     }
1132 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
1133                                      GtkDirectionType  direction)
1135     gboolean focusKept = FALSE;
1136     gboolean wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1137     SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1138     gboolean goingUp = FALSE;
1140     switch ( direction )
1141     {
1142     case GTK_DIR_TAB_FORWARD:
1143     case GTK_DIR_UP:
1144     case GTK_DIR_LEFT:
1145         goingUp = TRUE;
1146         break;
1148     case GTK_DIR_TAB_BACKWARD:
1149     case GTK_DIR_DOWN:
1150     case GTK_DIR_RIGHT:
1151         goingUp = FALSE;
1152         break;
1153     default:
1154         ;
1155     }
1157     if ( !wasFocused )
1158     {
1159         wheel->_inTriangle = !goingUp;
1160         gtk_widget_grab_focus (widget);
1161         focusKept = TRUE;
1162     }
1163     else if ( (!wheel->_inTriangle) == (!goingUp) )
1164     {
1165         focusKept = FALSE;
1166     }
1167     else
1168     {
1169         wheel->_inTriangle = !wheel->_inTriangle;
1170         gtk_widget_queue_draw( widget );
1171         focusKept = TRUE;
1172     }
1174     return focusKept;
1177 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
1179 // njh: dot(rot90(B-C), x) = saturation
1180 // njh: dot(B-C, x) = value
1181     NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
1182                      y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
1184     gdouble rot = (M_PI * 2 * wheel->_hue );
1186     NR::Point result = delta * NR::rotate(rot);
1188     gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
1190     gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
1191     gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
1193     wheel->_triDirty = TRUE;
1195     sp_color_wheel_set_sv( wheel, sat, value );
1199 /*
1200   Local Variables:
1201   mode:c++
1202   c-file-style:"stroustrup"
1203   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1204   indent-tabs-mode:nil
1205   fill-column:99
1206   End:
1207 */
1208 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :