Code

moving trunk for module inkscape
[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 <gtk/gtksignal.h>
17 #include "sp-color-wheel.h"
19 #include "libnr/nr-rotate-ops.h"
21 #define WHEEL_SIZE 96
23 enum {
24     CHANGED,
25     LAST_SIGNAL
26 };
28 static void sp_color_wheel_class_init (SPColorWheelClass *klass);
29 static void sp_color_wheel_init (SPColorWheel *wheel);
30 static void sp_color_wheel_destroy (GtkObject *object);
32 static void sp_color_wheel_realize (GtkWidget *widget);
33 static void sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition);
34 static void sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
36 static gint sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event);
37 static gint sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
38 static gint sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event);
39 static gint sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event);
41 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue);
42 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value );
43 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel);
45 static void sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area);
46 static void sp_color_wheel_render_hue_wheel (SPColorWheel *wheel);
47 static void sp_color_wheel_render_triangle (SPColorWheel *wheel);
50 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
51                                      GtkDirectionType  direction);
53 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
55 static GtkWidgetClass *parent_class;
56 static guint wheel_signals[LAST_SIGNAL] = {0};
58 /*
59 static double
60 get_time (void)
61 {
62     GTimeVal tv;
63     g_get_current_time (&tv);
64     return tv.tv_sec + 1e-6 * tv.tv_usec;
65 }
66 */
68 GtkType
69 sp_color_wheel_get_type (void)
70 {
71     static GtkType type = 0;
72     if (!type) {
73         GtkTypeInfo info = {
74             "SPColorWheel",
75             sizeof (SPColorWheel),
76             sizeof (SPColorWheelClass),
77             (GtkClassInitFunc) sp_color_wheel_class_init,
78             (GtkObjectInitFunc) sp_color_wheel_init,
79             NULL, NULL, NULL
80         };
81         type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
82     }
83     return type;
84 }
86 static void
87 sp_color_wheel_class_init (SPColorWheelClass *klass)
88 {
89     GtkObjectClass *object_class;
90     GtkWidgetClass *widget_class;
92     object_class = (GtkObjectClass *) klass;
93     widget_class = (GtkWidgetClass *) klass;
95     parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
97     wheel_signals[CHANGED] = gtk_signal_new ("changed",
98                           (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
99                           GTK_CLASS_TYPE(object_class),
100                           GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
101                           gtk_marshal_NONE__NONE,
102                           GTK_TYPE_NONE, 0);
104     object_class->destroy = sp_color_wheel_destroy;
106     widget_class->realize = sp_color_wheel_realize;
107     widget_class->size_request = sp_color_wheel_size_request;
108     widget_class->size_allocate = sp_color_wheel_size_allocate;
110     widget_class->focus = sp_color_wheel_focus;
112     widget_class->expose_event = sp_color_wheel_expose;
113     widget_class->button_press_event = sp_color_wheel_button_press;
114     widget_class->button_release_event = sp_color_wheel_button_release;
115     widget_class->motion_notify_event = sp_color_wheel_motion_notify;
118 static void
119 sp_color_wheel_init (SPColorWheel *wheel)
121     /* We are widget with window */
122     GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
123     GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
125     wheel->dragging = FALSE;
127     wheel->_inTriangle = FALSE;
128     wheel->_triDirty = TRUE;
129     wheel->_triangle = NULL;
130     for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
131     {
132         wheel->_triPoints[i].x = 0;
133         wheel->_triPoints[i].y = 0;
134     }
135     wheel->_triImage = NULL;
136     wheel->_triBs = 0;
138     wheel->_image = NULL;
139     wheel->_bs = 0;
140     wheel->_hue = 0.0;
141     wheel->_sat = 0.9;
142     wheel->_value = 0.25;
143     wheel->_inner = 0;
144     wheel->_center = 0;
145     wheel->_spotValue = 1.0;
148 static void
149 sp_color_wheel_destroy (GtkObject *object)
151     SPColorWheel *wheel;
153     wheel = SP_COLOR_WHEEL (object);
155     if ( wheel->_image )
156     {
157         g_free( wheel->_image );
158         wheel->_image = NULL;
159         wheel->_bs = 0;
160     }
162     if ( wheel->_triImage )
163     {
164         g_free( wheel->_triImage );
165         wheel->_triImage = NULL;
166         wheel->_triBs = 0;
167     }
169     if (((GtkObjectClass *) (parent_class))->destroy)
170         (* ((GtkObjectClass *) (parent_class))->destroy) (object);
174 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
176     float rgb[3];
177     gint i;
178     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
179     g_return_if_fail (wheel != NULL);
180     g_return_if_fail (color != NULL);
182     sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
183     for ( i = 0; i < 3; i++ )
184     {
185         rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
186     }
188     sp_color_set_rgb_float (color, rgb[0], rgb[1], rgb[2]);
191 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
193     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
194     g_return_if_fail (wheel != NULL);
195     g_return_if_fail (color != NULL);
197     float hue;
198     float scratch[3];
199     float rgb[3];
201     sp_color_get_rgb_floatv (color, rgb);
202     sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
203     hue = scratch[0];
205     sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
207     gint lowInd = 0;
208     gint hiInd = 0;
209     for ( int i = 1; i < 3; i++ )
210     {
211         if ( scratch[i] < scratch[lowInd] )
212         {
213             lowInd = i;
214         }
215         if ( scratch[i] > scratch[hiInd] )
216         {
217             hiInd = i;
218         }
219     }
220     // scratch[lowInd] should always be 0
221     gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
222     gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
225     sp_color_wheel_set_hue(wheel, hue);
226     sp_color_wheel_set_sv(wheel, sat, val);
229 gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
231     g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
232     return wheel->dragging;
235 static void
236 sp_color_wheel_realize (GtkWidget *widget)
238     SPColorWheel *wheel;
239     GdkWindowAttr attributes;
240     gint attributes_mask;
242     wheel = SP_COLOR_WHEEL (widget);
244     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
246     attributes.window_type = GDK_WINDOW_CHILD;
247     attributes.x = widget->allocation.x;
248     attributes.y = widget->allocation.y;
249     attributes.width = widget->allocation.width;
250     attributes.height = widget->allocation.height;
251     attributes.wclass = GDK_INPUT_OUTPUT;
252     attributes.visual = gdk_rgb_get_visual ();
253     attributes.colormap = gdk_rgb_get_cmap ();
254     attributes.event_mask = gtk_widget_get_events (widget);
255     attributes.event_mask |= (GDK_EXPOSURE_MASK |
256                   GDK_BUTTON_PRESS_MASK |
257                   GDK_BUTTON_RELEASE_MASK |
258                   GDK_POINTER_MOTION_MASK |
259                   GDK_ENTER_NOTIFY_MASK |
260                   GDK_LEAVE_NOTIFY_MASK |
261                   GDK_FOCUS_CHANGE_MASK );
262     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
264     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
265     gdk_window_set_user_data (widget->window, widget);
267     widget->style = gtk_style_attach (widget->style, widget->window);
270 static void
271 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
273     SPColorWheel *wheel;
275     wheel = SP_COLOR_WHEEL (widget);
277     requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
278     requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
281 static void
282 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
284     SPColorWheel *wheel;
286     wheel = SP_COLOR_WHEEL (widget);
288     widget->allocation = *allocation;
290     wheel->_center = MIN(allocation->width, allocation->height)/2;
291     wheel->_inner = (3 * wheel->_center)/4;
292     if ( wheel->_image )
293     {
294         g_free( wheel->_image );
295         wheel->_image = NULL;
296         wheel->_bs = 0;
297     }
299     // Need to render the gradient before we do the triangle over
300     sp_color_wheel_render_hue_wheel(wheel);
301     sp_color_wheel_recalc_triangle(wheel);
302     sp_color_wheel_render_triangle(wheel);
304     if (GTK_WIDGET_REALIZED (widget)) {
305         /* Resize GdkWindow */
306         gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
307     }
310 static gint
311 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
313     SPColorWheel *wheel;
315     wheel = SP_COLOR_WHEEL (widget);
317     if (GTK_WIDGET_DRAWABLE (widget)) {
318         gint width, height;
319         width = widget->allocation.width;
320         height = widget->allocation.height;
321         sp_color_wheel_paint (wheel, &event->area);
322     }
324     return TRUE;
327 static gint
328 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
330     SPColorWheel *wheel;
332     wheel = SP_COLOR_WHEEL (widget);
334     if (event->button == 1) {
335         gint cx, cw;
336         cx = widget->style->xthickness;
337         cw = widget->allocation.width - 2 * cx;
338         gboolean grabbed = FALSE;
340         {
341             double dx = event->x - wheel->_center;
342             double dy = event->y - wheel->_center;
343             gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
344             if ( hyp <= (wheel->_center*wheel->_center) )
345             {
346                 if ( hyp >= (wheel->_inner*wheel->_inner) )
347                 {
348                     gdouble rot = atan2( dy, -dx );
349                     sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
351                     wheel->_inTriangle = FALSE;
352                     grabbed = TRUE;
353                 }
354                 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
355                 {
356                     wheel->_inTriangle = TRUE;
357                     sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
358                     grabbed = TRUE;
359                 }
360             }
361         }
363         if ( grabbed )
364         {
365             gtk_widget_queue_draw( widget );
366             gtk_widget_grab_focus( widget );
368             wheel->dragging = TRUE;
369             gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
370             gdk_pointer_grab (widget->window, FALSE,
371                               (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
372                               NULL, NULL, event->time);
373         }
374     }
376     return TRUE;
379 static gint
380 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
382     SPColorWheel *wheel;
384     wheel = SP_COLOR_WHEEL (widget);
386     if (event->button == 1) {
387         gdk_pointer_ungrab (event->time);
388         wheel->dragging = FALSE;
390         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
391     }
393     return TRUE;
396 static gint
397 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
399     SPColorWheel *wheel;
401     wheel = SP_COLOR_WHEEL (widget);
403     if (wheel->dragging) {
404         double dx = event->x - wheel->_center;
405         double dy = event->y - wheel->_center;
406         if ( !wheel->_inTriangle )
407         {
408             gdouble rot = atan2( dy, -dx );
409             sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
410         }
411         else
412         {
413             sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
414         }
416         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
417     }
419     return TRUE;
422 GtkWidget *
423 sp_color_wheel_new ()
425     SPColorWheel *wheel;
427     wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
429     return GTK_WIDGET (wheel);
432 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
434     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
436     if ( wheel->_hue != hue )
437     {
438         wheel->_hue = hue;
440         sp_color_wheel_recalc_triangle(wheel);
442         SPColor color;
443         gfloat rgb[3];
444         sp_color_wheel_get_color( wheel, &color );
445         sp_color_get_rgb_floatv (&color, rgb);
447         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
449         gtk_widget_queue_draw (GTK_WIDGET (wheel));
450     }
454 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
456     static gdouble epsilon = 1e-6;
457     gboolean changed = FALSE;
459     if ( ABS( wheel->_sat - sat ) > epsilon )
460     {
461         wheel->_sat = sat;
462         changed = TRUE;
463     }
464     if ( ABS( wheel->_value - value ) > epsilon )
465     {
466         wheel->_value = value;
467         changed = TRUE;
468     }
470     if ( changed )
471     {
472         SPColor color;
473         gfloat rgb[3];
474         sp_color_wheel_get_color( wheel, &color );
475         sp_color_get_rgb_floatv (&color, rgb);
477         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
479         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
480     }
481     gtk_widget_queue_draw (GTK_WIDGET (wheel));
484 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
486     if ( wheel->_triangle )
487     {
488         gdk_region_destroy( wheel->_triangle );
489         wheel->_triangle = NULL;
490     }
491     wheel->_triDirty = TRUE;
493     if ( wheel->_center > 0 && wheel->_inner > 0 )
494     {
495         gdouble dx = cos( M_PI * 2 * wheel->_hue );
496         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
498         wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
499         wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
501         dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
502         dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
503         wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
504         wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
506         dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
507         dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
508         wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
509         wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
512         wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
513                                                3,
514                                                GDK_EVEN_ODD_RULE );
515     }
518 #define VERT_SWAP( X, Y ) { \
519     gfloat tmpF; \
520  \
521     tmpF = point##X.x; \
522     point##X.x = point##Y.x; \
523     point##Y.x = tmpF; \
524  \
525     tmpF = point##X.y; \
526     point##X.y = point##Y.y; \
527     point##Y.y = tmpF; \
528  \
529     tmpF = rgb##X[0]; \
530     rgb##X[0] = rgb##Y[0]; \
531     rgb##Y[0] = tmpF; \
532  \
533     tmpF = rgb##X[1]; \
534     rgb##X[1] = rgb##Y[1]; \
535     rgb##Y[1] = tmpF; \
536  \
537     tmpF = rgb##X[2]; \
538     rgb##X[2] = rgb##Y[2]; \
539     rgb##Y[2] = tmpF; \
542 #define VERT_COPY( dst, src ) { \
543     point##dst.x = point##src.x; \
544  \
545     point##dst.y = point##src.y; \
546  \
547     rgb##dst[0] = rgb##src[0]; \
548     rgb##dst[1] = rgb##src[1]; \
549     rgb##dst[2] = rgb##src[2]; \
552 typedef struct {
553     gfloat x;
554     gfloat y;
555 } PointF;
557 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
559     if ( wheel->_image )
560     {
561         if ( wheel->_triBs < wheel->_bs )
562         {
563             g_free( wheel->_triImage );
564             wheel->_triImage = NULL;
565         }
567         if (wheel->_triDirty || !wheel->_triImage)
568         {
569             if ( !wheel->_triImage )
570             {
571                 wheel->_triBs = wheel->_bs;
572                 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
573                 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
574             }
576             memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
578             PointF pointA, pointB, pointC;
579             pointA.x = wheel->_triPoints[0].x;
580             pointA.y = wheel->_triPoints[0].y;
581             pointB.x = wheel->_triPoints[1].x;
582             pointB.y = wheel->_triPoints[1].y;
583             pointC.x = wheel->_triPoints[2].x;
584             pointC.y = wheel->_triPoints[2].y;
586             gfloat rgbA[3];
587             gfloat rgbB[3] = {0.0, 0.0, 0.0};
588             gfloat rgbC[3] = {1.0, 1.0, 1.0};
590             sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
592 // Start of Gouraud fill ============================================================
593             gint rowStride = wheel->_center * 2 * 3;
594             guchar* dst = wheel->_triImage;
596             if ( pointC.y < pointB.y )
597                 VERT_SWAP( C, B );
599             if ( pointC.y < pointA.y )
600                 VERT_SWAP( C, A );
602             if ( pointB.y < pointA.y )
603                 VERT_SWAP( B, A );
605             if ( pointA.y == pointB.y && pointB.x < pointA.x )
606                 VERT_SWAP( A, B );
608             gfloat dr, dg, db;
610             gfloat dx1,dx2,dx3;
611             gfloat dr1,dr2,dr3;
612             gfloat dg1,dg2,dg3;
613             gfloat db1,db2,db3;
616             PointF pointS;
617             PointF pointE;
618             PointF pointP;
619             gfloat rgbS[3];
620             gfloat rgbE[3];
621             gfloat rgbP[3];
624             if (pointB.y-pointA.y > 0)
625             {
626                 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
627                 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
628                 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
629                 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
630             }
631             else
632             {
633                 dx1=dr1=dg1=db1=0;
634             }
636             if (pointC.y-pointA.y > 0)
637             {
638                 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
639                 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
640                 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
641                 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
642             }
643             else
644             {
645                 dx2=dr2=dg2=db2=0;
646             }
648             if (pointC.y-pointB.y > 0)
649             {
650                 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
651                 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
652                 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
653                 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
654             }
655             else
656             {
657                 dx3=dr3=dg3=db3=0;
658             }
660             VERT_COPY(S, A);
661             VERT_COPY(E, A);
663             if ( dx1 == 0 )
664             {
665                 VERT_COPY(E,B);
666                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
667                 {
668                     if (pointE.x-pointS.x > 0)
669                     {
670                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
671                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
672                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
673                     }
674                     else
675                     {
676                         dr=dg=db=0;
677                     }
678                     VERT_COPY(P,S);
679                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
680                     dst += static_cast<gint>(pointP.x) * 3;
681                     for(;pointP.x < pointE.x;pointP.x++)
682                     {
683                         //putpixel(P);
684                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
685                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
686                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
687                         dst += 3;
688                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
689                     }
690                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
691                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
692                 }
693             }
694             else if (dx1 > dx2)
695             {
696                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
697                 {
698                     if (pointE.x-pointS.x > 0)
699                     {
700                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
701                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
702                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
703                     }
704                     else
705                     {
706                         dr=dg=db=0;
707                     }
708                     VERT_COPY(P,S);
709                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
710                     dst += static_cast<gint>(pointP.x) * 3;
711                     for(;pointP.x < pointE.x;pointP.x++)
712                     {
713                         //putpixel(P);
714                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
715                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
716                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
717                         dst += 3;
718                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
719                     }
720                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
721                     pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
722                 }
723                 VERT_COPY(E,B);
724                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
725                 {
726                     if (pointE.x-pointS.x > 0)
727                     {
728                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
729                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
730                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
731                     }
732                     else
733                     {
734                         dr=dg=db=0;
735                     }
736                     VERT_COPY(P,S);
737                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
738                     dst += static_cast<gint>(pointP.x) * 3;
739                     for(;pointP.x < pointE.x;pointP.x++)
740                     {
741                         //putpixel(P);
742                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
743                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
744                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
745                         dst += 3;
746                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
747                     }
748                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
749                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
750                 }
751             }
752             else if ( dx1 )
753             {
754                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
755                 {
756                     if (pointE.x-pointS.x > 0)
757                     {
758                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
759                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
760                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
761                     }
762                     else
763                     {
764                         dr=dg=db=0;
765                     }
766                     VERT_COPY(P,S);
767                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
768                     dst += static_cast<gint>(pointP.x) * 3;
769                     for(;pointP.x < pointE.x;pointP.x++)
770                     {
771                         //putpixel(P);
772                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
773                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
774                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
775                         dst += 3;
776                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
777                     }
778                     pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
779                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
780                 }
781                 VERT_COPY(S,B);
782                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
783                 {
784                     if (pointE.x-pointS.x > 0)
785                     {
786                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
787                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
788                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
789                     }
790                     else
791                     {
792                         dr=dg=db=0;
793                     }
794                     VERT_COPY(P,S);
795                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
796                     dst += static_cast<gint>(pointP.x) * 3;
797                     for(;pointP.x < pointE.x;pointP.x++)
798                     {
799                         //putpixel(P);
800                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
801                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
802                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
803                         dst += 3;
804                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
805                     }
806                     pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
807                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
808                 }
809             }
811 // End of Gouraud fill  ============================================================
813             wheel->_triDirty = FALSE;
814             //g_message( "Just updated triangle" );
815         }
816     }
819 static void
820 sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
822     GtkWidget *widget;
823     GdkRectangle warea, carea;
824     GdkRectangle wpaint, cpaint;
826     widget = GTK_WIDGET (wheel);
828     /* Widget area */
829     warea.x = 0;
830     warea.y = 0;
831     warea.width = widget->allocation.width;
832     warea.height = widget->allocation.height;
834     /* Color gradient area */
835     carea.x = widget->style->xthickness;
836     carea.y = widget->style->ythickness;
837     carea.width = widget->allocation.width - 2 * carea.x;
838     carea.height = widget->allocation.height - 2 * carea.y;
840     /* Actual paintable area */
841     if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
843     //g_message( "Painted as state %d", widget->state );
845     /* Paintable part of color gradient area */
846     if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
847         sp_color_wheel_render_hue_wheel (wheel);
848         sp_color_wheel_render_triangle (wheel);
849     }
851 /*
852     gtk_draw_box (widget->style,
853                   widget->window,
854                   (GtkStateType)widget->state,
855                   GTK_SHADOW_NONE,
856                   warea.x,
857                   warea.y,
858                   warea.width,
859                   warea.height);
860 */
862     gtk_style_apply_default_background( widget->style,
863                                         widget->window,
864                                         TRUE,
865                                         (GtkStateType)widget->state,
866                                         NULL,
867                                         0,
868                                         0,
869                                         warea.width,
870                                         warea.height);
873     /* Draw shadow */
874 /*
875     gtk_paint_shadow (widget->style, widget->window,
876               (GtkStateType)widget->state, GTK_SHADOW_IN,
877               NULL, widget, "colorwheel",
878               0, 0,
879               warea.width, warea.height);
880 */
883     /* Draw pixelstore */
884     if (wheel->_triImage != NULL) {
885         //gdouble start, end;
886         //start = get_time();
887         gdk_draw_rgb_image (widget->window, widget->style->black_gc,
888                             0, 0,//cpaint.x, cpaint.y,
889                             //cpaint.width, cpaint.height,
890                             wheel->_center * 2, wheel->_center * 2,
891                             GDK_RGB_DITHER_MAX,
892                             wheel->_triImage, wheel->_center * 6);
894         //end = get_time();
895         //g_message( "blits took %f", (end-start) );
896     }
898     {
899         gdouble dx = cos( M_PI * 2 * wheel->_hue );
900         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
902         gfloat rgb[3];
903         sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
905         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;
907         gint inx = wheel->_center + static_cast<gint>(dx * wheel->_inner);
908         gint iny = wheel->_center + static_cast<gint>(dy * wheel->_inner);
911         gdk_draw_line (widget->window, line_gc,
912                        inx, iny,
913                        wheel->_center + static_cast<gint>(dx * wheel->_center), wheel->_center + static_cast<gint>(dy * wheel->_center) );
916         GdkGCValues values;
918         if ( GTK_WIDGET_HAS_FOCUS(wheel) )
919         {
920             line_gc = widget->style->black_gc;
922             gdk_gc_get_values ( line_gc, &values );
924             gdk_gc_set_line_attributes ( line_gc,
925                                          3, // Line width
926                                          values.line_style, //GDK_LINE_SOLID,
927                                          values.cap_style, //GDK_CAP_BUTT,
928                                          values.join_style ); //GDK_JOIN_MITER );
930             if ( wheel->_inTriangle )
931             {
932                 gdk_draw_line (widget->window, line_gc,
933                                wheel->_triPoints[0].x, wheel->_triPoints[0].y,
934                                wheel->_triPoints[1].x, wheel->_triPoints[1].y );
936                 gdk_draw_line (widget->window, line_gc,
937                                wheel->_triPoints[1].x, wheel->_triPoints[1].y,
938                                wheel->_triPoints[2].x, wheel->_triPoints[2].y );
940                 gdk_draw_line (widget->window, line_gc,
941                                wheel->_triPoints[2].x, wheel->_triPoints[2].y,
942                                wheel->_triPoints[0].x, wheel->_triPoints[0].y );
943             }
944             else
945             {
946                 gdk_draw_arc (widget->window, line_gc,
947                               FALSE, // filled
948                               0, 0,
949                               wheel->_center * 2, wheel->_center * 2,
950                               0, 64 * 360 );
952                 gint diff = wheel->_center - wheel->_inner;
954                 gdk_draw_arc (widget->window, line_gc,
955                               FALSE, // filled
956                               diff, diff,
957                               wheel->_inner * 2, wheel->_inner * 2,
958                               0, 64 * 360 );
960             }
961             gdk_gc_set_line_attributes ( line_gc,
962                                          values.line_width, // Line width
963                                          values.line_style, //GDK_LINE_SOLID,
964                                          values.cap_style, //GDK_CAP_BUTT,
965                                          values.join_style ); //GDK_JOIN_MITER );
966         }
967 // ==========
969 //        line_gc = (p[3] < 0x80) ? widget->style->white_gc : widget->style->black_gc;
970         line_gc = (wheel->_spotValue < 0.5) ? widget->style->white_gc : widget->style->black_gc;
972         gdk_gc_get_values ( line_gc, &values );
974         gdk_gc_set_line_attributes ( line_gc,
975                                      2, // Line width
976                                      values.line_style, //GDK_LINE_SOLID,
977                                      values.cap_style, //GDK_CAP_BUTT,
978                                      values.join_style ); //GDK_JOIN_MITER );
980         gint pointX = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].x + wheel->_value*(gdouble)wheel->_triPoints[2].x)
981             + (wheel->_sat * wheel->_triPoints[0].x) );
983         gint pointY = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].y + wheel->_value*(gdouble)wheel->_triPoints[2].y)
984             + (wheel->_sat * wheel->_triPoints[0].y) );
987         gdk_gc_set_line_attributes ( line_gc,
988                                      values.line_width, // Line width
989                                      values.line_style, //GDK_LINE_SOLID,
990                                      values.cap_style, //GDK_CAP_BUTT,
991                                      values.join_style ); //GDK_JOIN_MITER );
993         gdk_draw_arc (widget->window, line_gc,
994                       FALSE, // filled
995                       pointX - 4, pointY - 4,
996                       8, 8,
997                       0, 64 * 360 );
999         gdk_draw_arc (widget->window, line_gc,
1000                       FALSE, // filled
1001                       pointX - 3, pointY - 3,
1002                       6, 6,
1003                       0, 64 * 360 );
1007     }
1010 /* Colors are << 16 */
1012 static void
1013 sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
1015     guchar *dp;
1016     gint x, y;
1017     guint r, g, b;
1018     gint size = wheel->_center * 2;
1019     gboolean dirty = FALSE;
1021     if (wheel->_image && (wheel->_bs < (size * size) )) {
1022         g_free (wheel->_image);
1023         wheel->_image = NULL;
1024         wheel->_bs = 0;
1026         if ( wheel->_triImage )
1027         {
1028             g_free( wheel->_triImage );
1029             wheel->_triImage = NULL;
1030             wheel->_triBs = 0;
1031             wheel->_triDirty = TRUE;
1032         }
1033     }
1035     if (!wheel->_image) {
1036         wheel->_image = g_new (guchar, size * size * 3);
1037         wheel->_bs = size * size;
1038         dirty = TRUE;
1039         //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
1040     }
1042     if ( dirty )
1043     {
1044         GtkWidget* widget = GTK_WIDGET (wheel);
1045         dp = wheel->_image;
1046         r = widget->style->bg[widget->state].red >> 8;
1047         g = widget->style->bg[widget->state].green >> 8;
1048         b = widget->style->bg[widget->state].blue >> 8;
1049         //g_message( "Rendered as state %d", widget->state );
1051         gint offset = wheel->_center;
1052         gint inner = wheel->_inner * wheel->_inner;
1053         gint rad = wheel->_center * wheel->_center;
1055         for (x = 0; x < size; x++) {
1056             guchar *d = dp;
1057             for (y = 0; y < size; y++) {
1058                 gint dx = x - offset;
1059                 gint dy = y - offset;
1060                 gint hyp = (ABS(dx*dx) + ABS(dy*dy));
1061                 if ( hyp >= inner && hyp <= rad)
1062                 {
1063                     gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
1065                     gfloat rgb[3];
1066                     sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
1068                     d[0] = SP_COLOR_F_TO_U (rgb[0]);
1069                     d[1] = SP_COLOR_F_TO_U (rgb[1]);
1070                     d[2] = SP_COLOR_F_TO_U (rgb[2]);
1071                 }
1072                 else
1073                 {
1074                     /* Background value */
1075                     d[0] = r;
1076                     d[1] = g;
1077                     d[2] = b;
1078                 }
1080                 d += 3 * size;
1081             }
1082             dp += 3;
1083         }
1084     }
1087 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
1088                                      GtkDirectionType  direction)
1090     gboolean focusKept = FALSE;
1091     gboolean wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1092     SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1093     gboolean goingUp = FALSE;
1095     switch ( direction )
1096     {
1097     case GTK_DIR_TAB_FORWARD:
1098     case GTK_DIR_UP:
1099     case GTK_DIR_LEFT:
1100         goingUp = TRUE;
1101         break;
1103     case GTK_DIR_TAB_BACKWARD:
1104     case GTK_DIR_DOWN:
1105     case GTK_DIR_RIGHT:
1106         goingUp = FALSE;
1107         break;
1108     default:
1109         ;
1110     }
1112     if ( !wasFocused )
1113     {
1114         wheel->_inTriangle = !goingUp;
1115         gtk_widget_grab_focus (widget);
1116         focusKept = TRUE;
1117     }
1118     else if ( (!wheel->_inTriangle) == (!goingUp) )
1119     {
1120         focusKept = FALSE;
1121     }
1122     else
1123     {
1124         wheel->_inTriangle = !wheel->_inTriangle;
1125         gtk_widget_queue_draw( widget );
1126         focusKept = TRUE;
1127     }
1129     return focusKept;
1132 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
1134 // njh: dot(rot90(B-C), x) = saturation
1135 // njh: dot(B-C, x) = value
1136     NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
1137                      y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
1139     gdouble rot = (M_PI * 2 * wheel->_hue );
1141     NR::Point result = delta * NR::rotate(rot);
1143     gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
1145     gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
1146     gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
1148     wheel->_triDirty = TRUE;
1150     sp_color_wheel_set_sv( wheel, sat, value );
1154 /*
1155   Local Variables:
1156   mode:c++
1157   c-file-style:"stroustrup"
1158   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1159   indent-tabs-mode:nil
1160   fill-column:99
1161   End:
1162 */
1163 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :