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