Code

Ok, committed msgloan's patch to convert gbooleans to bools thus completing
[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, GtkDirectionType  direction);
52 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
54 static GtkWidgetClass *parent_class;
55 static guint wheel_signals[LAST_SIGNAL] = {0};
57 /*
58 static double
59 get_time (void)
60 {
61     GTimeVal tv;
62     g_get_current_time (&tv);
63     return tv.tv_sec + 1e-6 * tv.tv_usec;
64 }
65 */
67 GtkType
68 sp_color_wheel_get_type (void)
69 {
70     static GtkType type = 0;
71     if (!type) {
72         GtkTypeInfo info = {
73             "SPColorWheel",
74             sizeof (SPColorWheel),
75             sizeof (SPColorWheelClass),
76             (GtkClassInitFunc) sp_color_wheel_class_init,
77             (GtkObjectInitFunc) sp_color_wheel_init,
78             NULL, NULL, NULL
79         };
80         type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
81     }
82     return type;
83 }
85 static void
86 sp_color_wheel_class_init (SPColorWheelClass *klass)
87 {
88     GtkObjectClass *object_class;
89     GtkWidgetClass *widget_class;
91     object_class = (GtkObjectClass *) klass;
92     widget_class = (GtkWidgetClass *) klass;
94     parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
96     wheel_signals[CHANGED] = gtk_signal_new ("changed",
97                           (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
98                           GTK_CLASS_TYPE(object_class),
99                           GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
100                           gtk_marshal_NONE__NONE,
101                           GTK_TYPE_NONE, 0);
103     object_class->destroy = sp_color_wheel_destroy;
105     widget_class->realize = sp_color_wheel_realize;
106     widget_class->size_request = sp_color_wheel_size_request;
107     widget_class->size_allocate = sp_color_wheel_size_allocate;
109     widget_class->focus = sp_color_wheel_focus;
111     widget_class->expose_event = sp_color_wheel_expose;
112     widget_class->button_press_event = sp_color_wheel_button_press;
113     widget_class->button_release_event = sp_color_wheel_button_release;
114     widget_class->motion_notify_event = sp_color_wheel_motion_notify;
117 static void
118 sp_color_wheel_init (SPColorWheel *wheel)
120     /* We are widget with window */
121     GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
122     GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
124     wheel->dragging = FALSE;
126     wheel->_inTriangle = FALSE;
127     wheel->_triDirty = TRUE;
128     wheel->_triangle = NULL;
129     for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
130     {
131         wheel->_triPoints[i].x = 0;
132         wheel->_triPoints[i].y = 0;
133     }
134     wheel->_triImage = NULL;
135     wheel->_triBs = 0;
137     wheel->_image = NULL;
138     wheel->_bs = 0;
139     wheel->_hue = 0.0;
140     wheel->_sat = 0.9;
141     wheel->_value = 0.25;
142     wheel->_inner = 0;
143     wheel->_center = 0;
144     wheel->_spotValue = 1.0;
147 static void
148 sp_color_wheel_destroy (GtkObject *object)
150     SPColorWheel *wheel;
152     wheel = SP_COLOR_WHEEL (object);
154     if ( wheel->_image )
155     {
156         g_free( wheel->_image );
157         wheel->_image = NULL;
158         wheel->_bs = 0;
159     }
161     if ( wheel->_triImage )
162     {
163         g_free( wheel->_triImage );
164         wheel->_triImage = NULL;
165         wheel->_triBs = 0;
166     }
168     if (((GtkObjectClass *) (parent_class))->destroy)
169         (* ((GtkObjectClass *) (parent_class))->destroy) (object);
173 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
175     float rgb[3];
176     gint i;
177     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
178     g_return_if_fail (wheel != NULL);
179     g_return_if_fail (color != NULL);
181     sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
182     for ( i = 0; i < 3; i++ )
183     {
184         rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
185     }
187     sp_color_set_rgb_float (color, rgb[0], rgb[1], rgb[2]);
190 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
192     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
193     g_return_if_fail (wheel != NULL);
194     g_return_if_fail (color != NULL);
196     float hue;
197     float scratch[3];
198     float rgb[3];
200     sp_color_get_rgb_floatv (color, rgb);
201     sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
202     hue = scratch[0];
204     sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
206     gint lowInd = 0;
207     gint hiInd = 0;
208     for ( int i = 1; i < 3; i++ )
209     {
210         if ( scratch[i] < scratch[lowInd] )
211         {
212             lowInd = i;
213         }
214         if ( scratch[i] > scratch[hiInd] )
215         {
216             hiInd = i;
217         }
218     }
219     // scratch[lowInd] should always be 0
220     gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
221     gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
224     sp_color_wheel_set_hue(wheel, hue);
225     sp_color_wheel_set_sv(wheel, sat, val);
228 bool sp_color_wheel_is_adjusting( SPColorWheel *wheel )
230     g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
231     return wheel->dragging;
234 static void
235 sp_color_wheel_realize (GtkWidget *widget)
237     SPColorWheel *wheel;
238     GdkWindowAttr attributes;
239     gint attributes_mask;
241     wheel = SP_COLOR_WHEEL (widget);
243     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
245     attributes.window_type = GDK_WINDOW_CHILD;
246     attributes.x = widget->allocation.x;
247     attributes.y = widget->allocation.y;
248     attributes.width = widget->allocation.width;
249     attributes.height = widget->allocation.height;
250     attributes.wclass = GDK_INPUT_OUTPUT;
251     attributes.visual = gdk_rgb_get_visual ();
252     attributes.colormap = gdk_rgb_get_cmap ();
253     attributes.event_mask = gtk_widget_get_events (widget);
254     attributes.event_mask |= (GDK_EXPOSURE_MASK |
255                   GDK_BUTTON_PRESS_MASK |
256                   GDK_BUTTON_RELEASE_MASK |
257                   GDK_POINTER_MOTION_MASK |
258                   GDK_ENTER_NOTIFY_MASK |
259                   GDK_LEAVE_NOTIFY_MASK |
260                   GDK_FOCUS_CHANGE_MASK );
261     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
263     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
264     gdk_window_set_user_data (widget->window, widget);
266     widget->style = gtk_style_attach (widget->style, widget->window);
269 static void
270 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
272     SPColorWheel *wheel;
274     wheel = SP_COLOR_WHEEL (widget);
276     requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
277     requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
280 static void
281 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
283     SPColorWheel *wheel;
285     wheel = SP_COLOR_WHEEL (widget);
287     widget->allocation = *allocation;
289     wheel->_center = MIN(allocation->width, allocation->height)/2;
290     wheel->_inner = (3 * wheel->_center)/4;
291     if ( wheel->_image )
292     {
293         g_free( wheel->_image );
294         wheel->_image = NULL;
295         wheel->_bs = 0;
296     }
298     // Need to render the gradient before we do the triangle over
299     sp_color_wheel_render_hue_wheel(wheel);
300     sp_color_wheel_recalc_triangle(wheel);
301     sp_color_wheel_render_triangle(wheel);
303     if (GTK_WIDGET_REALIZED (widget)) {
304         /* Resize GdkWindow */
305         gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
306     }
309 static gint
310 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
312     SPColorWheel *wheel;
314     wheel = SP_COLOR_WHEEL (widget);
316     if (GTK_WIDGET_DRAWABLE (widget)) {
317         gint width, height;
318         width = widget->allocation.width;
319         height = widget->allocation.height;
320         sp_color_wheel_paint (wheel, &event->area);
321     }
323     return TRUE;
326 static gint
327 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
329     SPColorWheel *wheel;
331     wheel = SP_COLOR_WHEEL (widget);
333     if (event->button == 1) {
334         gint cx, cw;
335         cx = widget->style->xthickness;
336         cw = widget->allocation.width - 2 * cx;
337         bool grabbed = FALSE;
339         {
340             double dx = event->x - wheel->_center;
341             double dy = event->y - wheel->_center;
342             gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
343             if ( hyp <= (wheel->_center*wheel->_center) )
344             {
345                 if ( hyp >= (wheel->_inner*wheel->_inner) )
346                 {
347                     gdouble rot = atan2( dy, -dx );
348                     sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
350                     wheel->_inTriangle = FALSE;
351                     grabbed = TRUE;
352                 }
353                 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
354                 {
355                     wheel->_inTriangle = TRUE;
356                     sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
357                     grabbed = TRUE;
358                 }
359             }
360         }
362         if ( grabbed )
363         {
364             gtk_widget_queue_draw( widget );
365             gtk_widget_grab_focus( widget );
367             wheel->dragging = TRUE;
368             gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
369             gdk_pointer_grab (widget->window, FALSE,
370                               (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
371                               NULL, NULL, event->time);
372         }
373     }
375     return TRUE;
378 static gint
379 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
381     SPColorWheel *wheel;
383     wheel = SP_COLOR_WHEEL (widget);
385     if (event->button == 1) {
386         gdk_pointer_ungrab (event->time);
387         wheel->dragging = FALSE;
389         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
390     }
392     return TRUE;
395 static gint
396 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
398     SPColorWheel *wheel;
400     wheel = SP_COLOR_WHEEL (widget);
402     if (wheel->dragging) {
403         double dx = event->x - wheel->_center;
404         double dy = event->y - wheel->_center;
405         if ( !wheel->_inTriangle )
406         {
407             gdouble rot = atan2( dy, -dx );
408             sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
409         }
410         else
411         {
412             sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
413         }
415         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
416     }
418     return TRUE;
421 GtkWidget *
422 sp_color_wheel_new ()
424     SPColorWheel *wheel;
426     wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
428     return GTK_WIDGET (wheel);
431 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
433     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
435     if ( wheel->_hue != hue )
436     {
437         wheel->_hue = hue;
439         sp_color_wheel_recalc_triangle(wheel);
441         SPColor color;
442         gfloat rgb[3];
443         sp_color_wheel_get_color( wheel, &color );
444         sp_color_get_rgb_floatv (&color, rgb);
446         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
448         gtk_widget_queue_draw (GTK_WIDGET (wheel));
449     }
453 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
455     static gdouble epsilon = 1e-6;
456     bool changed = FALSE;
458     if ( ABS( wheel->_sat - sat ) > epsilon )
459     {
460         wheel->_sat = sat;
461         changed = TRUE;
462     }
463     if ( ABS( wheel->_value - value ) > epsilon )
464     {
465         wheel->_value = value;
466         changed = TRUE;
467     }
469     if ( changed )
470     {
471         SPColor color;
472         gfloat rgb[3];
473         sp_color_wheel_get_color( wheel, &color );
474         sp_color_get_rgb_floatv (&color, rgb);
476         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
478         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
479     }
480     gtk_widget_queue_draw (GTK_WIDGET (wheel));
483 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
485     if ( wheel->_triangle )
486     {
487         gdk_region_destroy( wheel->_triangle );
488         wheel->_triangle = NULL;
489     }
490     wheel->_triDirty = TRUE;
492     if ( wheel->_center > 0 && wheel->_inner > 0 )
493     {
494         gdouble dx = cos( M_PI * 2 * wheel->_hue );
495         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
497         wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
498         wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
500         dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
501         dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
502         wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
503         wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
505         dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
506         dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
507         wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
508         wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
511         wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
512                                                3,
513                                                GDK_EVEN_ODD_RULE );
514     }
517 #define VERT_SWAP( X, Y ) { \
518     gfloat tmpF; \
519  \
520     tmpF = point##X.x; \
521     point##X.x = point##Y.x; \
522     point##Y.x = tmpF; \
523  \
524     tmpF = point##X.y; \
525     point##X.y = point##Y.y; \
526     point##Y.y = tmpF; \
527  \
528     tmpF = rgb##X[0]; \
529     rgb##X[0] = rgb##Y[0]; \
530     rgb##Y[0] = tmpF; \
531  \
532     tmpF = rgb##X[1]; \
533     rgb##X[1] = rgb##Y[1]; \
534     rgb##Y[1] = tmpF; \
535  \
536     tmpF = rgb##X[2]; \
537     rgb##X[2] = rgb##Y[2]; \
538     rgb##Y[2] = tmpF; \
541 #define VERT_COPY( dst, src ) { \
542     point##dst.x = point##src.x; \
543  \
544     point##dst.y = point##src.y; \
545  \
546     rgb##dst[0] = rgb##src[0]; \
547     rgb##dst[1] = rgb##src[1]; \
548     rgb##dst[2] = rgb##src[2]; \
551 typedef struct {
552     gfloat x;
553     gfloat y;
554 } PointF;
556 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
558     if ( wheel->_image )
559     {
560         if ( wheel->_triBs < wheel->_bs )
561         {
562             g_free( wheel->_triImage );
563             wheel->_triImage = NULL;
564         }
566         if (wheel->_triDirty || !wheel->_triImage)
567         {
568             if ( !wheel->_triImage )
569             {
570                 wheel->_triBs = wheel->_bs;
571                 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
572                 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
573             }
575             memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
577             PointF pointA, pointB, pointC;
578             pointA.x = wheel->_triPoints[0].x;
579             pointA.y = wheel->_triPoints[0].y;
580             pointB.x = wheel->_triPoints[1].x;
581             pointB.y = wheel->_triPoints[1].y;
582             pointC.x = wheel->_triPoints[2].x;
583             pointC.y = wheel->_triPoints[2].y;
585             gfloat rgbA[3];
586             gfloat rgbB[3] = {0.0, 0.0, 0.0};
587             gfloat rgbC[3] = {1.0, 1.0, 1.0};
589             sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
591 // Start of Gouraud fill ============================================================
592             gint rowStride = wheel->_center * 2 * 3;
593             guchar* dst = wheel->_triImage;
595             if ( pointC.y < pointB.y )
596                 VERT_SWAP( C, B );
598             if ( pointC.y < pointA.y )
599                 VERT_SWAP( C, A );
601             if ( pointB.y < pointA.y )
602                 VERT_SWAP( B, A );
604             if ( pointA.y == pointB.y && pointB.x < pointA.x )
605                 VERT_SWAP( A, B );
607             gfloat dr, dg, db;
609             gfloat dx1,dx2,dx3;
610             gfloat dr1,dr2,dr3;
611             gfloat dg1,dg2,dg3;
612             gfloat db1,db2,db3;
615             PointF pointS;
616             PointF pointE;
617             PointF pointP;
618             gfloat rgbS[3];
619             gfloat rgbE[3];
620             gfloat rgbP[3];
623             if (pointB.y-pointA.y > 0)
624             {
625                 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
626                 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
627                 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
628                 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
629             }
630             else
631             {
632                 dx1=dr1=dg1=db1=0;
633             }
635             if (pointC.y-pointA.y > 0)
636             {
637                 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
638                 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
639                 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
640                 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
641             }
642             else
643             {
644                 dx2=dr2=dg2=db2=0;
645             }
647             if (pointC.y-pointB.y > 0)
648             {
649                 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
650                 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
651                 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
652                 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
653             }
654             else
655             {
656                 dx3=dr3=dg3=db3=0;
657             }
659             VERT_COPY(S, A);
660             VERT_COPY(E, A);
662             if ( dx1 == 0 )
663             {
664                 VERT_COPY(E,B);
665                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
666                 {
667                     if (pointE.x-pointS.x > 0)
668                     {
669                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
670                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
671                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
672                     }
673                     else
674                     {
675                         dr=dg=db=0;
676                     }
677                     VERT_COPY(P,S);
678                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
679                     dst += static_cast<gint>(pointP.x) * 3;
680                     for(;pointP.x < pointE.x;pointP.x++)
681                     {
682                         //putpixel(P);
683                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
684                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
685                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
686                         dst += 3;
687                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
688                     }
689                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
690                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
691                 }
692             }
693             else if (dx1 > dx2)
694             {
695                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
696                 {
697                     if (pointE.x-pointS.x > 0)
698                     {
699                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
700                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
701                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
702                     }
703                     else
704                     {
705                         dr=dg=db=0;
706                     }
707                     VERT_COPY(P,S);
708                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
709                     dst += static_cast<gint>(pointP.x) * 3;
710                     for(;pointP.x < pointE.x;pointP.x++)
711                     {
712                         //putpixel(P);
713                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
714                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
715                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
716                         dst += 3;
717                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
718                     }
719                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
720                     pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
721                 }
722                 VERT_COPY(E,B);
723                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
724                 {
725                     if (pointE.x-pointS.x > 0)
726                     {
727                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
728                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
729                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
730                     }
731                     else
732                     {
733                         dr=dg=db=0;
734                     }
735                     VERT_COPY(P,S);
736                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
737                     dst += static_cast<gint>(pointP.x) * 3;
738                     for(;pointP.x < pointE.x;pointP.x++)
739                     {
740                         //putpixel(P);
741                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
742                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
743                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
744                         dst += 3;
745                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
746                     }
747                     pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
748                     pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
749                 }
750             }
751             else if ( dx1 )
752             {
753                 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
754                 {
755                     if (pointE.x-pointS.x > 0)
756                     {
757                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
758                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
759                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
760                     }
761                     else
762                     {
763                         dr=dg=db=0;
764                     }
765                     VERT_COPY(P,S);
766                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
767                     dst += static_cast<gint>(pointP.x) * 3;
768                     for(;pointP.x < pointE.x;pointP.x++)
769                     {
770                         //putpixel(P);
771                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
772                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
773                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
774                         dst += 3;
775                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
776                     }
777                     pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
778                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
779                 }
780                 VERT_COPY(S,B);
781                 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
782                 {
783                     if (pointE.x-pointS.x > 0)
784                     {
785                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
786                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
787                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
788                     }
789                     else
790                     {
791                         dr=dg=db=0;
792                     }
793                     VERT_COPY(P,S);
794                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
795                     dst += static_cast<gint>(pointP.x) * 3;
796                     for(;pointP.x < pointE.x;pointP.x++)
797                     {
798                         //putpixel(P);
799                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
800                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
801                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
802                         dst += 3;
803                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
804                     }
805                     pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
806                     pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
807                 }
808             }
810 // End of Gouraud fill  ============================================================
812             wheel->_triDirty = FALSE;
813             //g_message( "Just updated triangle" );
814         }
815     }
818 static void
819 sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
821     GtkWidget *widget;
822     GdkRectangle warea, carea;
823     GdkRectangle wpaint, cpaint;
825     widget = GTK_WIDGET (wheel);
827     /* Widget area */
828     warea.x = 0;
829     warea.y = 0;
830     warea.width = widget->allocation.width;
831     warea.height = widget->allocation.height;
833     /* Color gradient area */
834     carea.x = widget->style->xthickness;
835     carea.y = widget->style->ythickness;
836     carea.width = widget->allocation.width - 2 * carea.x;
837     carea.height = widget->allocation.height - 2 * carea.y;
839     /* Actual paintable area */
840     if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
842     //g_message( "Painted as state %d", widget->state );
844     /* Paintable part of color gradient area */
845     if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
846         sp_color_wheel_render_hue_wheel (wheel);
847         sp_color_wheel_render_triangle (wheel);
848     }
850 /*
851     gtk_draw_box (widget->style,
852                   widget->window,
853                   (GtkStateType)widget->state,
854                   GTK_SHADOW_NONE,
855                   warea.x,
856                   warea.y,
857                   warea.width,
858                   warea.height);
859 */
861     gtk_style_apply_default_background( widget->style,
862                                         widget->window,
863                                         TRUE,
864                                         (GtkStateType)widget->state,
865                                         NULL,
866                                         0,
867                                         0,
868                                         warea.width,
869                                         warea.height);
872     /* Draw shadow */
873 /*
874     gtk_paint_shadow (widget->style, widget->window,
875               (GtkStateType)widget->state, GTK_SHADOW_IN,
876               NULL, widget, "colorwheel",
877               0, 0,
878               warea.width, warea.height);
879 */
882     /* Draw pixelstore */
883     if (wheel->_triImage != NULL) {
884         //gdouble start, end;
885         //start = get_time();
886         gdk_draw_rgb_image (widget->window, widget->style->black_gc,
887                             0, 0,//cpaint.x, cpaint.y,
888                             //cpaint.width, cpaint.height,
889                             wheel->_center * 2, wheel->_center * 2,
890                             GDK_RGB_DITHER_MAX,
891                             wheel->_triImage, wheel->_center * 6);
893         //end = get_time();
894         //g_message( "blits took %f", (end-start) );
895     }
897     {
898         gdouble dx = cos( M_PI * 2 * wheel->_hue );
899         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
901         gfloat rgb[3];
902         sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
904         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;
906         gint inx = wheel->_center + static_cast<gint>(dx * wheel->_inner);
907         gint iny = wheel->_center + static_cast<gint>(dy * wheel->_inner);
910         gdk_draw_line (widget->window, line_gc,
911                        inx, iny,
912                        wheel->_center + static_cast<gint>(dx * wheel->_center), wheel->_center + static_cast<gint>(dy * wheel->_center) );
915         GdkGCValues values;
917         if ( GTK_WIDGET_HAS_FOCUS(wheel) )
918         {
919             line_gc = widget->style->black_gc;
921             gdk_gc_get_values ( line_gc, &values );
923             gdk_gc_set_line_attributes ( line_gc,
924                                          3, // Line width
925                                          values.line_style, //GDK_LINE_SOLID,
926                                          values.cap_style, //GDK_CAP_BUTT,
927                                          values.join_style ); //GDK_JOIN_MITER );
929             if ( wheel->_inTriangle )
930             {
931                 gdk_draw_line (widget->window, line_gc,
932                                wheel->_triPoints[0].x, wheel->_triPoints[0].y,
933                                wheel->_triPoints[1].x, wheel->_triPoints[1].y );
935                 gdk_draw_line (widget->window, line_gc,
936                                wheel->_triPoints[1].x, wheel->_triPoints[1].y,
937                                wheel->_triPoints[2].x, wheel->_triPoints[2].y );
939                 gdk_draw_line (widget->window, line_gc,
940                                wheel->_triPoints[2].x, wheel->_triPoints[2].y,
941                                wheel->_triPoints[0].x, wheel->_triPoints[0].y );
942             }
943             else
944             {
945                 gdk_draw_arc (widget->window, line_gc,
946                               FALSE, // filled
947                               0, 0,
948                               wheel->_center * 2, wheel->_center * 2,
949                               0, 64 * 360 );
951                 gint diff = wheel->_center - wheel->_inner;
953                 gdk_draw_arc (widget->window, line_gc,
954                               FALSE, // filled
955                               diff, diff,
956                               wheel->_inner * 2, wheel->_inner * 2,
957                               0, 64 * 360 );
959             }
960             gdk_gc_set_line_attributes ( line_gc,
961                                          values.line_width, // Line width
962                                          values.line_style, //GDK_LINE_SOLID,
963                                          values.cap_style, //GDK_CAP_BUTT,
964                                          values.join_style ); //GDK_JOIN_MITER );
965         }
966 // ==========
968 //        line_gc = (p[3] < 0x80) ? widget->style->white_gc : widget->style->black_gc;
969         line_gc = (wheel->_spotValue < 0.5) ? widget->style->white_gc : widget->style->black_gc;
971         gdk_gc_get_values ( line_gc, &values );
973         gdk_gc_set_line_attributes ( line_gc,
974                                      2, // Line width
975                                      values.line_style, //GDK_LINE_SOLID,
976                                      values.cap_style, //GDK_CAP_BUTT,
977                                      values.join_style ); //GDK_JOIN_MITER );
979         gint pointX = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].x + wheel->_value*(gdouble)wheel->_triPoints[2].x)
980             + (wheel->_sat * wheel->_triPoints[0].x) );
982         gint pointY = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].y + wheel->_value*(gdouble)wheel->_triPoints[2].y)
983             + (wheel->_sat * wheel->_triPoints[0].y) );
986         gdk_gc_set_line_attributes ( line_gc,
987                                      values.line_width, // Line width
988                                      values.line_style, //GDK_LINE_SOLID,
989                                      values.cap_style, //GDK_CAP_BUTT,
990                                      values.join_style ); //GDK_JOIN_MITER );
992         gdk_draw_arc (widget->window, line_gc,
993                       FALSE, // filled
994                       pointX - 4, pointY - 4,
995                       8, 8,
996                       0, 64 * 360 );
998         gdk_draw_arc (widget->window, line_gc,
999                       FALSE, // filled
1000                       pointX - 3, pointY - 3,
1001                       6, 6,
1002                       0, 64 * 360 );
1006     }
1009 /* Colors are << 16 */
1011 static void
1012 sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
1014     guchar *dp;
1015     gint x, y;
1016     guint r, g, b;
1017     gint size = wheel->_center * 2;
1018     bool dirty = FALSE;
1020     if (wheel->_image && (wheel->_bs < (size * size) )) {
1021         g_free (wheel->_image);
1022         wheel->_image = NULL;
1023         wheel->_bs = 0;
1025         if ( wheel->_triImage )
1026         {
1027             g_free( wheel->_triImage );
1028             wheel->_triImage = NULL;
1029             wheel->_triBs = 0;
1030             wheel->_triDirty = TRUE;
1031         }
1032     }
1034     if (!wheel->_image) {
1035         wheel->_image = g_new (guchar, size * size * 3);
1036         wheel->_bs = size * size;
1037         dirty = TRUE;
1038         //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
1039     }
1041     if ( dirty )
1042     {
1043         GtkWidget* widget = GTK_WIDGET (wheel);
1044         dp = wheel->_image;
1045         r = widget->style->bg[widget->state].red >> 8;
1046         g = widget->style->bg[widget->state].green >> 8;
1047         b = widget->style->bg[widget->state].blue >> 8;
1048         //g_message( "Rendered as state %d", widget->state );
1050         gint offset = wheel->_center;
1051         gint inner = wheel->_inner * wheel->_inner;
1052         gint rad = wheel->_center * wheel->_center;
1054         for (x = 0; x < size; x++) {
1055             guchar *d = dp;
1056             for (y = 0; y < size; y++) {
1057                 gint dx = x - offset;
1058                 gint dy = y - offset;
1059                 gint hyp = (ABS(dx*dx) + ABS(dy*dy));
1060                 if ( hyp >= inner && hyp <= rad)
1061                 {
1062                     gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
1064                     gfloat rgb[3];
1065                     sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
1067                     d[0] = SP_COLOR_F_TO_U (rgb[0]);
1068                     d[1] = SP_COLOR_F_TO_U (rgb[1]);
1069                     d[2] = SP_COLOR_F_TO_U (rgb[2]);
1070                 }
1071                 else
1072                 {
1073                     /* Background value */
1074                     d[0] = r;
1075                     d[1] = g;
1076                     d[2] = b;
1077                 }
1079                 d += 3 * size;
1080             }
1081             dp += 3;
1082         }
1083     }
1086 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
1087                                      GtkDirectionType  direction)
1089     bool focusKept = FALSE;
1090     bool wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1091     SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1092     bool goingUp = FALSE;
1094     switch ( direction )
1095     {
1096     case GTK_DIR_TAB_FORWARD:
1097     case GTK_DIR_UP:
1098     case GTK_DIR_LEFT:
1099         goingUp = TRUE;
1100         break;
1102     case GTK_DIR_TAB_BACKWARD:
1103     case GTK_DIR_DOWN:
1104     case GTK_DIR_RIGHT:
1105         goingUp = FALSE;
1106         break;
1107     default:
1108         ;
1109     }
1111     if ( !wasFocused )
1112     {
1113         wheel->_inTriangle = !goingUp;
1114         gtk_widget_grab_focus (widget);
1115         focusKept = TRUE;
1116     }
1117     else if ( (!wheel->_inTriangle) == (!goingUp) )
1118     {
1119         focusKept = FALSE;
1120     }
1121     else
1122     {
1123         wheel->_inTriangle = !wheel->_inTriangle;
1124         gtk_widget_queue_draw( widget );
1125         focusKept = TRUE;
1126     }
1128     return focusKept;
1131 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
1133 // njh: dot(rot90(B-C), x) = saturation
1134 // njh: dot(B-C, x) = value
1135     NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
1136                      y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
1138     gdouble rot = (M_PI * 2 * wheel->_hue );
1140     NR::Point result = delta * NR::rotate(rot);
1142     gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
1144     gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
1145     gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
1147     wheel->_triDirty = TRUE;
1149     sp_color_wheel_set_sv( wheel, sat, value );
1153 /*
1154   Local Variables:
1155   mode:c++
1156   c-file-style:"stroustrup"
1157   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1158   indent-tabs-mode:nil
1159   fill-column:99
1160   End:
1161 */
1162 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :