Code

Stop background rendering once crash handler is triggered.
[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  *   John Bintz <jcoswell@coswellproductions.org>
10  *
11  * Copyright (C) 2001-2002 Lauris Kaplinski
12  * Copyright (C) 2003-2004 Authors
13  *
14  * This code is in public domain
15  */
17 #include <cstring>
18 #include <string>
20 #include <gtk/gtksignal.h>
21 #include "sp-color-wheel.h"
23 #include "libnr/nr-rotate-ops.h"
24 #include <2geom/transforms.h>
26 #define WHEEL_SIZE 96
28 enum {
29     CHANGED,
30     LAST_SIGNAL
31 };
33 #define noDUMP_CHANGE_INFO
34 #define FOO_NAME(x) g_type_name( G_TYPE_FROM_INSTANCE(x) )
36 static void sp_color_wheel_class_init (SPColorWheelClass *klass);
37 static void sp_color_wheel_init (SPColorWheel *wheel);
38 static void sp_color_wheel_destroy (GtkObject *object);
40 static void sp_color_wheel_realize (GtkWidget *widget);
41 static void sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition);
42 static void sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
44 static gint sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event);
45 static gint sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
46 static gint sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event);
47 static gint sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event);
49 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue);
50 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value );
51 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel);
53 static void sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area);
54 static void sp_color_wheel_render_hue_wheel (SPColorWheel *wheel);
55 static void sp_color_wheel_render_triangle (SPColorWheel *wheel);
58 static gboolean sp_color_wheel_focus(GtkWidget        *widget,
59                                      GtkDirectionType  direction);
61 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
63 static GtkWidgetClass *parent_class;
64 static guint wheel_signals[LAST_SIGNAL] = {0};
66 /*
67 static double
68 get_time (void)
69 {
70     GTimeVal tv;
71     g_get_current_time (&tv);
72     return tv.tv_sec + 1e-6 * tv.tv_usec;
73 }
74 */
76 GType sp_color_wheel_get_type(void)
77 {
78     static GType type = 0;
79     if (!type) {
80         GTypeInfo info = {
81             sizeof(SPColorWheelClass),
82             0, // base_init
83             0, // base_finalize
84             (GClassInitFunc)sp_color_wheel_class_init,
85             0, // class_finalize
86             0, // class_data
87             sizeof(SPColorWheel),
88             0, // n_preallocs
89             (GInstanceInitFunc)sp_color_wheel_init,
90             0 // value_table
91         };
92         type = g_type_register_static(GTK_TYPE_WIDGET, "SPColorWheel", &info, static_cast<GTypeFlags>(0));
93     }
94     return type;
95 }
97 static void
98 sp_color_wheel_class_init (SPColorWheelClass *klass)
99 {
100     GtkObjectClass *object_class;
101     GtkWidgetClass *widget_class;
103     object_class = (GtkObjectClass *) klass;
104     widget_class = (GtkWidgetClass *) klass;
106     parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
108     wheel_signals[CHANGED] = gtk_signal_new ("changed",
109                           (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
110                           GTK_CLASS_TYPE(object_class),
111                           GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
112                           gtk_marshal_NONE__NONE,
113                           GTK_TYPE_NONE, 0);
115     object_class->destroy = sp_color_wheel_destroy;
117     widget_class->realize = sp_color_wheel_realize;
118     widget_class->size_request = sp_color_wheel_size_request;
119     widget_class->size_allocate = sp_color_wheel_size_allocate;
121     widget_class->focus = sp_color_wheel_focus;
123     widget_class->expose_event = sp_color_wheel_expose;
124     widget_class->button_press_event = sp_color_wheel_button_press;
125     widget_class->button_release_event = sp_color_wheel_button_release;
126     widget_class->motion_notify_event = sp_color_wheel_motion_notify;
129 static void
130 sp_color_wheel_init (SPColorWheel *wheel)
132     /* We are widget with window */
133     GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
134     GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
136     wheel->dragging = FALSE;
138     wheel->_inTriangle = FALSE;
139     wheel->_triDirty = TRUE;
140     wheel->_triangle = NULL;
141     for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
142     {
143         wheel->_triPoints[i].x = 0;
144         wheel->_triPoints[i].y = 0;
145     }
146     wheel->_triImage = NULL;
147     wheel->_triBs = 0;
149     wheel->_image = NULL;
150     wheel->_bs = 0;
151     wheel->_hue = 0.0;
152     wheel->_sat = 0.9;
153     wheel->_value = 0.25;
154     wheel->_inner = 0;
155     wheel->_center = 0;
156     wheel->_spotValue = 1.0;
159 static void
160 sp_color_wheel_destroy (GtkObject *object)
162     SPColorWheel *wheel;
164     wheel = SP_COLOR_WHEEL (object);
166     if ( wheel->_image )
167     {
168         g_free( wheel->_image );
169         wheel->_image = NULL;
170         wheel->_bs = 0;
171     }
173     if ( wheel->_triImage )
174     {
175         g_free( wheel->_triImage );
176         wheel->_triImage = NULL;
177         wheel->_triBs = 0;
178     }
180     if (((GtkObjectClass *) (parent_class))->destroy)
181         (* ((GtkObjectClass *) (parent_class))->destroy) (object);
185 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
187     float rgb[3];
188     gint i;
189     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
190     g_return_if_fail (wheel != NULL);
191     g_return_if_fail (color != NULL);
193     sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
194     for ( i = 0; i < 3; i++ )
195     {
196         rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
197     }
199     color->set( rgb[0], rgb[1], rgb[2] );
202 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
204 #ifdef DUMP_CHANGE_INFO
205     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] );
206 #endif
207     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
208     g_return_if_fail (wheel != NULL);
209     g_return_if_fail (color != NULL);
211     float hue;
212     float scratch[3];
213     float rgb[3];
215     sp_color_get_rgb_floatv (color, rgb);
216     sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
217     hue = scratch[0];
219     sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
221     gint lowInd = 0;
222     gint hiInd = 0;
223     for ( int i = 1; i < 3; i++ )
224     {
225         if ( scratch[i] < scratch[lowInd] )
226         {
227             lowInd = i;
228         }
229         if ( scratch[i] > scratch[hiInd] )
230         {
231             hiInd = i;
232         }
233     }
234     // scratch[lowInd] should always be 0
235     gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
236     gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
239     sp_color_wheel_set_hue(wheel, hue);
240     sp_color_wheel_set_sv(wheel, sat, val);
243 gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
245     g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
246     return wheel->dragging;
249 static void
250 sp_color_wheel_realize (GtkWidget *widget)
252     SPColorWheel *wheel;
253     GdkWindowAttr attributes;
254     gint attributes_mask;
256     wheel = SP_COLOR_WHEEL (widget);
258     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
260     attributes.window_type = GDK_WINDOW_CHILD;
261     attributes.x = widget->allocation.x;
262     attributes.y = widget->allocation.y;
263     attributes.width = widget->allocation.width;
264     attributes.height = widget->allocation.height;
265     attributes.wclass = GDK_INPUT_OUTPUT;
266     attributes.visual = gdk_rgb_get_visual ();
267     attributes.colormap = gdk_rgb_get_cmap ();
268     attributes.event_mask = gtk_widget_get_events (widget);
269     attributes.event_mask |= (GDK_EXPOSURE_MASK |
270                   GDK_BUTTON_PRESS_MASK |
271                   GDK_BUTTON_RELEASE_MASK |
272                   GDK_POINTER_MOTION_MASK |
273                   GDK_ENTER_NOTIFY_MASK |
274                   GDK_LEAVE_NOTIFY_MASK |
275                   GDK_FOCUS_CHANGE_MASK );
276     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
278     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
279     gdk_window_set_user_data (widget->window, widget);
281     widget->style = gtk_style_attach (widget->style, widget->window);
284 static void
285 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
287     SPColorWheel *wheel;
289     wheel = SP_COLOR_WHEEL (widget);
291     requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
292     requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
295 static void
296 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
298     SPColorWheel *wheel;
300     wheel = SP_COLOR_WHEEL (widget);
302     widget->allocation = *allocation;
304     wheel->_center = MIN(allocation->width, allocation->height)/2;
305     wheel->_inner = (3 * wheel->_center)/4;
306     if ( wheel->_image )
307     {
308         g_free( wheel->_image );
309         wheel->_image = NULL;
310         wheel->_bs = 0;
311     }
313     // Need to render the gradient before we do the triangle over
314     sp_color_wheel_render_hue_wheel(wheel);
315     sp_color_wheel_recalc_triangle(wheel);
316     sp_color_wheel_render_triangle(wheel);
318     if (GTK_WIDGET_REALIZED (widget)) {
319         /* Resize GdkWindow */
320         gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
321     }
324 static gint
325 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
327     SPColorWheel *wheel;
329     wheel = SP_COLOR_WHEEL (widget);
331     if (GTK_WIDGET_DRAWABLE (widget)) {
332         gint width, height;
333         width = widget->allocation.width;
334         height = widget->allocation.height;
335         sp_color_wheel_paint (wheel, &event->area);
336     }
338     return TRUE;
341 static gint
342 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
344     SPColorWheel *wheel;
346     wheel = SP_COLOR_WHEEL (widget);
348     if (event->button == 1) {
349         gint cx, cw;
350         cx = widget->style->xthickness;
351         cw = widget->allocation.width - 2 * cx;
352         gboolean grabbed = FALSE;
354         {
355             double dx = event->x - wheel->_center;
356             double dy = event->y - wheel->_center;
357             gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
358             if ( hyp <= (wheel->_center*wheel->_center) )
359             {
360                 if ( hyp >= (wheel->_inner*wheel->_inner) )
361                 {
362                     gdouble rot = atan2( dy, -dx );
363                     sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
365                     wheel->_inTriangle = FALSE;
366                     grabbed = TRUE;
367                 }
368                 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
369                 {
370                     wheel->_inTriangle = TRUE;
371                     sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
372                     grabbed = TRUE;
373                 }
374             }
375         }
377         if ( grabbed )
378         {
379             gtk_widget_queue_draw( widget );
380             gtk_widget_grab_focus( widget );
382             wheel->dragging = TRUE;
383 #ifdef DUMP_CHANGE_INFO
384             {
385                 SPColor color;
386                 sp_color_wheel_get_color( wheel, &color );
387                 g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
388                            "CHANGED",
389                            color.toRGBA32( 0 ), FOO_NAME(wheel));
390             }
391 #endif
392             gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
393             gdk_pointer_grab (widget->window, FALSE,
394                               (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
395                               NULL, NULL, event->time);
396         }
397     }
399     return TRUE;
402 static gint
403 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
405     SPColorWheel *wheel;
407     wheel = SP_COLOR_WHEEL (widget);
409     if (event->button == 1) {
410         gdk_pointer_ungrab (event->time);
411         wheel->dragging = FALSE;
413 #ifdef DUMP_CHANGE_INFO
414         {
415             SPColor color;
416             sp_color_wheel_get_color( wheel, &color );
417             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
418                        "CHANGED",
419                        color.toRGBA32( 0 ), FOO_NAME(wheel));
420         }
421 #endif
422         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
423     }
425     return TRUE;
428 static gint
429 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
431     SPColorWheel *wheel;
433     wheel = SP_COLOR_WHEEL (widget);
435     if (wheel->dragging) {
436         double dx = event->x - wheel->_center;
437         double dy = event->y - wheel->_center;
438         if ( !wheel->_inTriangle )
439         {
440             gdouble rot = atan2( dy, -dx );
441             sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
442         }
443         else
444         {
445             sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
446         }
448 #ifdef DUMP_CHANGE_INFO
449         {
450             SPColor color;
451             sp_color_wheel_get_color( wheel, &color );
452             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
453                        "CHANGED",
454                        color.toRGBA32( 0 ), FOO_NAME(wheel));
455         }
456 #endif
457         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
458     }
460     return TRUE;
463 GtkWidget *
464 sp_color_wheel_new ()
466     SPColorWheel *wheel;
468     wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
470     return GTK_WIDGET (wheel);
473 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
475     g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
477     if ( wheel->_hue != hue )
478     {
479         wheel->_hue = hue;
481         sp_color_wheel_recalc_triangle(wheel);
483         SPColor color;
484         gfloat rgb[3];
485         sp_color_wheel_get_color( wheel, &color );
486         sp_color_get_rgb_floatv (&color, rgb);
488         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
490         gtk_widget_queue_draw (GTK_WIDGET (wheel));
491     }
495 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
497     static gdouble epsilon = 1e-6;
498     gboolean changed = FALSE;
500     if ( ABS( wheel->_sat - sat ) > epsilon )
501     {
502         wheel->_sat = sat;
503         changed = TRUE;
504     }
505     if ( ABS( wheel->_value - value ) > epsilon )
506     {
507         wheel->_value = value;
508         changed = TRUE;
509     }
511     if ( changed )
512     {
513         SPColor color;
514         gfloat rgb[3];
515         sp_color_wheel_get_color( wheel, &color );
516         sp_color_get_rgb_floatv (&color, rgb);
518         wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
520 #ifdef DUMP_CHANGE_INFO
521         {
522             SPColor color;
523             sp_color_wheel_get_color( wheel, &color );
524             g_message( "%s:%d: About to signal %s to color %08x in %s", __FILE__, __LINE__,
525                        "CHANGED",
526                        color.toRGBA32( 0 ), FOO_NAME(wheel));
527         }
528 #endif
529         gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
530     }
531     gtk_widget_queue_draw (GTK_WIDGET (wheel));
534 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
536     if ( wheel->_triangle )
537     {
538         gdk_region_destroy( wheel->_triangle );
539         wheel->_triangle = NULL;
540     }
541     wheel->_triDirty = TRUE;
543     if ( wheel->_center > 0 && wheel->_inner > 0 )
544     {
545         gdouble dx = cos( M_PI * 2 * wheel->_hue );
546         gdouble dy = -sin( M_PI * 2 * wheel->_hue );
548         wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
549         wheel->_triPoints[0].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[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
554         wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
556         dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
557         dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
558         wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
559         wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
562         wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
563                                                3,
564                                                GDK_EVEN_ODD_RULE );
565     }
568 #define VERT_SWAP( X, Y ) { \
569     gfloat tmpF; \
570  \
571     tmpF = point##X.x; \
572     point##X.x = point##Y.x; \
573     point##Y.x = tmpF; \
574  \
575     tmpF = point##X.y; \
576     point##X.y = point##Y.y; \
577     point##Y.y = tmpF; \
578  \
579     tmpF = rgb##X[0]; \
580     rgb##X[0] = rgb##Y[0]; \
581     rgb##Y[0] = tmpF; \
582  \
583     tmpF = rgb##X[1]; \
584     rgb##X[1] = rgb##Y[1]; \
585     rgb##Y[1] = tmpF; \
586  \
587     tmpF = rgb##X[2]; \
588     rgb##X[2] = rgb##Y[2]; \
589     rgb##Y[2] = tmpF; \
592 #define VERT_COPY( dst, src ) { \
593     point##dst.x = point##src.x; \
594  \
595     point##dst.y = point##src.y; \
596  \
597     rgb##dst[0] = rgb##src[0]; \
598     rgb##dst[1] = rgb##src[1]; \
599     rgb##dst[2] = rgb##src[2]; \
602 typedef struct {
603     gfloat x;
604     gfloat y;
605 } PointF;
607 /**
608  * Render the provided color wheel information as a triangle.
609  */
610 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
612     if ( wheel->_image )
613     {
614         if ( wheel->_triBs < wheel->_bs )
615         {
616             g_free( wheel->_triImage );
617             wheel->_triImage = NULL;
618         }
620         if (wheel->_triDirty || !wheel->_triImage)
621         {
622             if ( !wheel->_triImage )
623             {
624                 wheel->_triBs = wheel->_bs;
625                 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
626                 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
627             }
629             memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
631             PointF pointA, pointB, pointC;
632             pointA.x = wheel->_triPoints[0].x;
633             pointA.y = wheel->_triPoints[0].y;
634             pointB.x = wheel->_triPoints[1].x;
635             pointB.y = wheel->_triPoints[1].y;
636             pointC.x = wheel->_triPoints[2].x;
637             pointC.y = wheel->_triPoints[2].y;
639             gfloat rgbA[3];
640             gfloat rgbB[3] = {0.0, 0.0, 0.0};
641             gfloat rgbC[3] = {1.0, 1.0, 1.0};
643             sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
645 // Start of Gouraud fill ============================================================
646             gint rowStride = wheel->_center * 2 * 3;
647             guchar* dst = wheel->_triImage;
649             if ( pointC.y < pointB.y )
650                 VERT_SWAP( C, B );
652             if ( pointC.y < pointA.y )
653                 VERT_SWAP( C, A );
655             if ( pointB.y < pointA.y )
656                 VERT_SWAP( B, A );
658             if ( pointA.y == pointB.y && pointB.x < pointA.x )
659                 VERT_SWAP( A, B );
661             gfloat dr, dg, db;
663             gfloat dx1,dx2,dx3;
664             gfloat dr1,dr2,dr3;
665             gfloat dg1,dg2,dg3;
666             gfloat db1,db2,db3;
669             PointF pointS;
670             PointF pointE;
671             PointF pointP;
672             gfloat rgbS[3];
673             gfloat rgbE[3];
674             gfloat rgbP[3];
677             if (pointB.y-pointA.y > 0)
678             {
679                 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
680                 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
681                 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
682                 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
683             }
684             else
685             {
686                 dx1=dr1=dg1=db1=0;
687             }
689             if (pointC.y-pointA.y > 0)
690             {
691                 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
692                 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
693                 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
694                 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
695             }
696             else
697             {
698                 dx2=dr2=dg2=db2=0;
699             }
701             if (pointC.y-pointB.y > 0)
702             {
703                 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
704                 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
705                 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
706                 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
707             }
708             else
709             {
710                 dx3=dr3=dg3=db3=0;
711             }
713             VERT_COPY(S, A);
714             VERT_COPY(E, A);
716             int runs = 1; int fill_mode = 0;
718             if ( dx1 == 0 )
719             {
720                 fill_mode = 0;
721             }
722             else if ( dx1 > dx2 )
723             {
724                 fill_mode = 1; runs = 2;
725             }
726             else if ( dx1 )
727             {
728                 fill_mode = 2; runs = 2;
729             }
731             gfloat targetY = 0;
732             int fill_direction_mode = 0;
734             for (int current_run = 0; current_run < runs; current_run++)
735             {
736                 targetY = pointC.y;
737                 switch (fill_mode)
738                 {
739                     case 0:
740                         VERT_COPY(E,B);
741                         fill_direction_mode = 0;
742                         break;
743                     case 1:
744                         if (current_run == 0) {
745                             targetY = pointB.y;
746                             fill_direction_mode = 1;
747                         } else {
748                             VERT_COPY(E,B);
749                             fill_direction_mode = 0;
750                         }
751                         break;
752                     case 2:
753                         if (current_run == 0) {
754                             targetY = pointB.y;
755                             fill_direction_mode = 2;
756                         } else {
757                             VERT_COPY(S,B);
758                             fill_direction_mode = 3;
759                         }
760                         break;
761                 }
763                 for(;pointS.y <= targetY; pointS.y++,pointE.y++)
764                 {
765                     if (pointE.x-pointS.x > 0)
766                     {
767                         dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
768                         dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
769                         db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
770                     }
771                     else
772                     {
773                         dr=dg=db=0;
774                     }
775                     VERT_COPY(P,S);
776                     dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
777                     dst += static_cast<gint>(pointP.x) * 3;
778                     for(;pointP.x < pointE.x;pointP.x++)
779                     {
780                         //putpixel(P);
781                         dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
782                         dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
783                         dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
784                         dst += 3;
785                         rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
786                     }
788                     switch (fill_direction_mode) {
789                         case 0:
790                             pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
791                             pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
792                             break;
793                         case 1:
794                             pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
795                             pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
796                             break;
797                         case 2:
798                             pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
799                             pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
800                             break;
801                         case 3:
802                             pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
803                             pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
804                             break;
805                     }
806                 }
807             }
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     gboolean 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     gboolean focusKept = FALSE;
1090     gboolean wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1091     SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1092     gboolean 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     Geom::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     Geom::Point result = delta * Geom::Rotate(rot);
1142     gdouble sat = CLAMP( result[Geom::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[Geom::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:fileencoding=utf-8:textwidth=99 :