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;
127 }
129 static void
130 sp_color_wheel_init (SPColorWheel *wheel)
131 {
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;
157 }
159 static void
160 sp_color_wheel_destroy (GtkObject *object)
161 {
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);
182 }
185 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
186 {
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] );
200 }
202 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
203 {
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);
241 }
243 gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
244 {
245 g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
246 return wheel->dragging;
247 }
249 static void
250 sp_color_wheel_realize (GtkWidget *widget)
251 {
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);
282 }
284 static void
285 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
286 {
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;
293 }
295 static void
296 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
297 {
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 }
322 }
324 static gint
325 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
326 {
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;
339 }
341 static gint
342 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
343 {
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;
400 }
402 static gint
403 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
404 {
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;
426 }
428 static gint
429 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
430 {
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;
461 }
463 GtkWidget *
464 sp_color_wheel_new ()
465 {
466 SPColorWheel *wheel;
468 wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
470 return GTK_WIDGET (wheel);
471 }
473 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
474 {
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 }
492 }
495 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
496 {
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));
532 }
534 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
535 {
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 }
566 }
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; \
590 }
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]; \
600 }
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)
611 {
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 }
816 }
818 static void
819 sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
820 {
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 }
1007 }
1009 /* Colors are << 16 */
1011 static void
1012 sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
1013 {
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 }
1084 }
1086 static gboolean sp_color_wheel_focus(GtkWidget *widget,
1087 GtkDirectionType direction)
1088 {
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;
1129 }
1131 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
1132 {
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 );
1150 }
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 :