1 #define __SP_COLOR_WHEEL_C__
3 /*
4 * A wheel color widget
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Jon A. Cruz <jon@joncruz.org>
9 *
10 * Copyright (C) 2001-2002 Lauris Kaplinski
11 * Copyright (C) 2003-2004 Authors
12 *
13 * This code is in public domain
14 */
16 #include <gtk/gtksignal.h>
17 #include "sp-color-wheel.h"
19 #include "libnr/nr-rotate-ops.h"
21 #define WHEEL_SIZE 96
23 enum {
24 CHANGED,
25 LAST_SIGNAL
26 };
28 static void sp_color_wheel_class_init (SPColorWheelClass *klass);
29 static void sp_color_wheel_init (SPColorWheel *wheel);
30 static void sp_color_wheel_destroy (GtkObject *object);
32 static void sp_color_wheel_realize (GtkWidget *widget);
33 static void sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition);
34 static void sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
36 static gint sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event);
37 static gint sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event);
38 static gint sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event);
39 static gint sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event);
41 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue);
42 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value );
43 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel);
45 static void sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area);
46 static void sp_color_wheel_render_hue_wheel (SPColorWheel *wheel);
47 static void sp_color_wheel_render_triangle (SPColorWheel *wheel);
50 static gboolean sp_color_wheel_focus(GtkWidget *widget, GtkDirectionType direction);
52 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
54 static GtkWidgetClass *parent_class;
55 static guint wheel_signals[LAST_SIGNAL] = {0};
57 /*
58 static double
59 get_time (void)
60 {
61 GTimeVal tv;
62 g_get_current_time (&tv);
63 return tv.tv_sec + 1e-6 * tv.tv_usec;
64 }
65 */
67 GtkType
68 sp_color_wheel_get_type (void)
69 {
70 static GtkType type = 0;
71 if (!type) {
72 GtkTypeInfo info = {
73 "SPColorWheel",
74 sizeof (SPColorWheel),
75 sizeof (SPColorWheelClass),
76 (GtkClassInitFunc) sp_color_wheel_class_init,
77 (GtkObjectInitFunc) sp_color_wheel_init,
78 NULL, NULL, NULL
79 };
80 type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
81 }
82 return type;
83 }
85 static void
86 sp_color_wheel_class_init (SPColorWheelClass *klass)
87 {
88 GtkObjectClass *object_class;
89 GtkWidgetClass *widget_class;
91 object_class = (GtkObjectClass *) klass;
92 widget_class = (GtkWidgetClass *) klass;
94 parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
96 wheel_signals[CHANGED] = gtk_signal_new ("changed",
97 (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
98 GTK_CLASS_TYPE(object_class),
99 GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
100 gtk_marshal_NONE__NONE,
101 GTK_TYPE_NONE, 0);
103 object_class->destroy = sp_color_wheel_destroy;
105 widget_class->realize = sp_color_wheel_realize;
106 widget_class->size_request = sp_color_wheel_size_request;
107 widget_class->size_allocate = sp_color_wheel_size_allocate;
109 widget_class->focus = sp_color_wheel_focus;
111 widget_class->expose_event = sp_color_wheel_expose;
112 widget_class->button_press_event = sp_color_wheel_button_press;
113 widget_class->button_release_event = sp_color_wheel_button_release;
114 widget_class->motion_notify_event = sp_color_wheel_motion_notify;
115 }
117 static void
118 sp_color_wheel_init (SPColorWheel *wheel)
119 {
120 /* We are widget with window */
121 GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
122 GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
124 wheel->dragging = FALSE;
126 wheel->_inTriangle = FALSE;
127 wheel->_triDirty = TRUE;
128 wheel->_triangle = NULL;
129 for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
130 {
131 wheel->_triPoints[i].x = 0;
132 wheel->_triPoints[i].y = 0;
133 }
134 wheel->_triImage = NULL;
135 wheel->_triBs = 0;
137 wheel->_image = NULL;
138 wheel->_bs = 0;
139 wheel->_hue = 0.0;
140 wheel->_sat = 0.9;
141 wheel->_value = 0.25;
142 wheel->_inner = 0;
143 wheel->_center = 0;
144 wheel->_spotValue = 1.0;
145 }
147 static void
148 sp_color_wheel_destroy (GtkObject *object)
149 {
150 SPColorWheel *wheel;
152 wheel = SP_COLOR_WHEEL (object);
154 if ( wheel->_image )
155 {
156 g_free( wheel->_image );
157 wheel->_image = NULL;
158 wheel->_bs = 0;
159 }
161 if ( wheel->_triImage )
162 {
163 g_free( wheel->_triImage );
164 wheel->_triImage = NULL;
165 wheel->_triBs = 0;
166 }
168 if (((GtkObjectClass *) (parent_class))->destroy)
169 (* ((GtkObjectClass *) (parent_class))->destroy) (object);
170 }
173 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
174 {
175 float rgb[3];
176 gint i;
177 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
178 g_return_if_fail (wheel != NULL);
179 g_return_if_fail (color != NULL);
181 sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
182 for ( i = 0; i < 3; i++ )
183 {
184 rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
185 }
187 sp_color_set_rgb_float (color, rgb[0], rgb[1], rgb[2]);
188 }
190 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
191 {
192 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
193 g_return_if_fail (wheel != NULL);
194 g_return_if_fail (color != NULL);
196 float hue;
197 float scratch[3];
198 float rgb[3];
200 sp_color_get_rgb_floatv (color, rgb);
201 sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
202 hue = scratch[0];
204 sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
206 gint lowInd = 0;
207 gint hiInd = 0;
208 for ( int i = 1; i < 3; i++ )
209 {
210 if ( scratch[i] < scratch[lowInd] )
211 {
212 lowInd = i;
213 }
214 if ( scratch[i] > scratch[hiInd] )
215 {
216 hiInd = i;
217 }
218 }
219 // scratch[lowInd] should always be 0
220 gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
221 gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
224 sp_color_wheel_set_hue(wheel, hue);
225 sp_color_wheel_set_sv(wheel, sat, val);
226 }
228 bool sp_color_wheel_is_adjusting( SPColorWheel *wheel )
229 {
230 g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
231 return wheel->dragging;
232 }
234 static void
235 sp_color_wheel_realize (GtkWidget *widget)
236 {
237 SPColorWheel *wheel;
238 GdkWindowAttr attributes;
239 gint attributes_mask;
241 wheel = SP_COLOR_WHEEL (widget);
243 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
245 attributes.window_type = GDK_WINDOW_CHILD;
246 attributes.x = widget->allocation.x;
247 attributes.y = widget->allocation.y;
248 attributes.width = widget->allocation.width;
249 attributes.height = widget->allocation.height;
250 attributes.wclass = GDK_INPUT_OUTPUT;
251 attributes.visual = gdk_rgb_get_visual ();
252 attributes.colormap = gdk_rgb_get_cmap ();
253 attributes.event_mask = gtk_widget_get_events (widget);
254 attributes.event_mask |= (GDK_EXPOSURE_MASK |
255 GDK_BUTTON_PRESS_MASK |
256 GDK_BUTTON_RELEASE_MASK |
257 GDK_POINTER_MOTION_MASK |
258 GDK_ENTER_NOTIFY_MASK |
259 GDK_LEAVE_NOTIFY_MASK |
260 GDK_FOCUS_CHANGE_MASK );
261 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
263 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
264 gdk_window_set_user_data (widget->window, widget);
266 widget->style = gtk_style_attach (widget->style, widget->window);
267 }
269 static void
270 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
271 {
272 SPColorWheel *wheel;
274 wheel = SP_COLOR_WHEEL (widget);
276 requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
277 requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
278 }
280 static void
281 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
282 {
283 SPColorWheel *wheel;
285 wheel = SP_COLOR_WHEEL (widget);
287 widget->allocation = *allocation;
289 wheel->_center = MIN(allocation->width, allocation->height)/2;
290 wheel->_inner = (3 * wheel->_center)/4;
291 if ( wheel->_image )
292 {
293 g_free( wheel->_image );
294 wheel->_image = NULL;
295 wheel->_bs = 0;
296 }
298 // Need to render the gradient before we do the triangle over
299 sp_color_wheel_render_hue_wheel(wheel);
300 sp_color_wheel_recalc_triangle(wheel);
301 sp_color_wheel_render_triangle(wheel);
303 if (GTK_WIDGET_REALIZED (widget)) {
304 /* Resize GdkWindow */
305 gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
306 }
307 }
309 static gint
310 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
311 {
312 SPColorWheel *wheel;
314 wheel = SP_COLOR_WHEEL (widget);
316 if (GTK_WIDGET_DRAWABLE (widget)) {
317 gint width, height;
318 width = widget->allocation.width;
319 height = widget->allocation.height;
320 sp_color_wheel_paint (wheel, &event->area);
321 }
323 return TRUE;
324 }
326 static gint
327 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
328 {
329 SPColorWheel *wheel;
331 wheel = SP_COLOR_WHEEL (widget);
333 if (event->button == 1) {
334 gint cx, cw;
335 cx = widget->style->xthickness;
336 cw = widget->allocation.width - 2 * cx;
337 bool grabbed = FALSE;
339 {
340 double dx = event->x - wheel->_center;
341 double dy = event->y - wheel->_center;
342 gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
343 if ( hyp <= (wheel->_center*wheel->_center) )
344 {
345 if ( hyp >= (wheel->_inner*wheel->_inner) )
346 {
347 gdouble rot = atan2( dy, -dx );
348 sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
350 wheel->_inTriangle = FALSE;
351 grabbed = TRUE;
352 }
353 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
354 {
355 wheel->_inTriangle = TRUE;
356 sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
357 grabbed = TRUE;
358 }
359 }
360 }
362 if ( grabbed )
363 {
364 gtk_widget_queue_draw( widget );
365 gtk_widget_grab_focus( widget );
367 wheel->dragging = TRUE;
368 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
369 gdk_pointer_grab (widget->window, FALSE,
370 (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
371 NULL, NULL, event->time);
372 }
373 }
375 return TRUE;
376 }
378 static gint
379 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
380 {
381 SPColorWheel *wheel;
383 wheel = SP_COLOR_WHEEL (widget);
385 if (event->button == 1) {
386 gdk_pointer_ungrab (event->time);
387 wheel->dragging = FALSE;
389 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
390 }
392 return TRUE;
393 }
395 static gint
396 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
397 {
398 SPColorWheel *wheel;
400 wheel = SP_COLOR_WHEEL (widget);
402 if (wheel->dragging) {
403 double dx = event->x - wheel->_center;
404 double dy = event->y - wheel->_center;
405 if ( !wheel->_inTriangle )
406 {
407 gdouble rot = atan2( dy, -dx );
408 sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
409 }
410 else
411 {
412 sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
413 }
415 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
416 }
418 return TRUE;
419 }
421 GtkWidget *
422 sp_color_wheel_new ()
423 {
424 SPColorWheel *wheel;
426 wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
428 return GTK_WIDGET (wheel);
429 }
431 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
432 {
433 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
435 if ( wheel->_hue != hue )
436 {
437 wheel->_hue = hue;
439 sp_color_wheel_recalc_triangle(wheel);
441 SPColor color;
442 gfloat rgb[3];
443 sp_color_wheel_get_color( wheel, &color );
444 sp_color_get_rgb_floatv (&color, rgb);
446 wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
448 gtk_widget_queue_draw (GTK_WIDGET (wheel));
449 }
450 }
453 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
454 {
455 static gdouble epsilon = 1e-6;
456 bool changed = FALSE;
458 if ( ABS( wheel->_sat - sat ) > epsilon )
459 {
460 wheel->_sat = sat;
461 changed = TRUE;
462 }
463 if ( ABS( wheel->_value - value ) > epsilon )
464 {
465 wheel->_value = value;
466 changed = TRUE;
467 }
469 if ( changed )
470 {
471 SPColor color;
472 gfloat rgb[3];
473 sp_color_wheel_get_color( wheel, &color );
474 sp_color_get_rgb_floatv (&color, rgb);
476 wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
478 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
479 }
480 gtk_widget_queue_draw (GTK_WIDGET (wheel));
481 }
483 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
484 {
485 if ( wheel->_triangle )
486 {
487 gdk_region_destroy( wheel->_triangle );
488 wheel->_triangle = NULL;
489 }
490 wheel->_triDirty = TRUE;
492 if ( wheel->_center > 0 && wheel->_inner > 0 )
493 {
494 gdouble dx = cos( M_PI * 2 * wheel->_hue );
495 gdouble dy = -sin( M_PI * 2 * wheel->_hue );
497 wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
498 wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
500 dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
501 dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
502 wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
503 wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
505 dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
506 dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
507 wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
508 wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
511 wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
512 3,
513 GDK_EVEN_ODD_RULE );
514 }
515 }
517 #define VERT_SWAP( X, Y ) { \
518 gfloat tmpF; \
519 \
520 tmpF = point##X.x; \
521 point##X.x = point##Y.x; \
522 point##Y.x = tmpF; \
523 \
524 tmpF = point##X.y; \
525 point##X.y = point##Y.y; \
526 point##Y.y = tmpF; \
527 \
528 tmpF = rgb##X[0]; \
529 rgb##X[0] = rgb##Y[0]; \
530 rgb##Y[0] = tmpF; \
531 \
532 tmpF = rgb##X[1]; \
533 rgb##X[1] = rgb##Y[1]; \
534 rgb##Y[1] = tmpF; \
535 \
536 tmpF = rgb##X[2]; \
537 rgb##X[2] = rgb##Y[2]; \
538 rgb##Y[2] = tmpF; \
539 }
541 #define VERT_COPY( dst, src ) { \
542 point##dst.x = point##src.x; \
543 \
544 point##dst.y = point##src.y; \
545 \
546 rgb##dst[0] = rgb##src[0]; \
547 rgb##dst[1] = rgb##src[1]; \
548 rgb##dst[2] = rgb##src[2]; \
549 }
551 typedef struct {
552 gfloat x;
553 gfloat y;
554 } PointF;
556 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
557 {
558 if ( wheel->_image )
559 {
560 if ( wheel->_triBs < wheel->_bs )
561 {
562 g_free( wheel->_triImage );
563 wheel->_triImage = NULL;
564 }
566 if (wheel->_triDirty || !wheel->_triImage)
567 {
568 if ( !wheel->_triImage )
569 {
570 wheel->_triBs = wheel->_bs;
571 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
572 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
573 }
575 memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
577 PointF pointA, pointB, pointC;
578 pointA.x = wheel->_triPoints[0].x;
579 pointA.y = wheel->_triPoints[0].y;
580 pointB.x = wheel->_triPoints[1].x;
581 pointB.y = wheel->_triPoints[1].y;
582 pointC.x = wheel->_triPoints[2].x;
583 pointC.y = wheel->_triPoints[2].y;
585 gfloat rgbA[3];
586 gfloat rgbB[3] = {0.0, 0.0, 0.0};
587 gfloat rgbC[3] = {1.0, 1.0, 1.0};
589 sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
591 // Start of Gouraud fill ============================================================
592 gint rowStride = wheel->_center * 2 * 3;
593 guchar* dst = wheel->_triImage;
595 if ( pointC.y < pointB.y )
596 VERT_SWAP( C, B );
598 if ( pointC.y < pointA.y )
599 VERT_SWAP( C, A );
601 if ( pointB.y < pointA.y )
602 VERT_SWAP( B, A );
604 if ( pointA.y == pointB.y && pointB.x < pointA.x )
605 VERT_SWAP( A, B );
607 gfloat dr, dg, db;
609 gfloat dx1,dx2,dx3;
610 gfloat dr1,dr2,dr3;
611 gfloat dg1,dg2,dg3;
612 gfloat db1,db2,db3;
615 PointF pointS;
616 PointF pointE;
617 PointF pointP;
618 gfloat rgbS[3];
619 gfloat rgbE[3];
620 gfloat rgbP[3];
623 if (pointB.y-pointA.y > 0)
624 {
625 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
626 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
627 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
628 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
629 }
630 else
631 {
632 dx1=dr1=dg1=db1=0;
633 }
635 if (pointC.y-pointA.y > 0)
636 {
637 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
638 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
639 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
640 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
641 }
642 else
643 {
644 dx2=dr2=dg2=db2=0;
645 }
647 if (pointC.y-pointB.y > 0)
648 {
649 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
650 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
651 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
652 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
653 }
654 else
655 {
656 dx3=dr3=dg3=db3=0;
657 }
659 VERT_COPY(S, A);
660 VERT_COPY(E, A);
662 if ( dx1 == 0 )
663 {
664 VERT_COPY(E,B);
665 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
666 {
667 if (pointE.x-pointS.x > 0)
668 {
669 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
670 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
671 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
672 }
673 else
674 {
675 dr=dg=db=0;
676 }
677 VERT_COPY(P,S);
678 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
679 dst += static_cast<gint>(pointP.x) * 3;
680 for(;pointP.x < pointE.x;pointP.x++)
681 {
682 //putpixel(P);
683 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
684 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
685 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
686 dst += 3;
687 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
688 }
689 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
690 pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
691 }
692 }
693 else if (dx1 > dx2)
694 {
695 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
696 {
697 if (pointE.x-pointS.x > 0)
698 {
699 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
700 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
701 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
702 }
703 else
704 {
705 dr=dg=db=0;
706 }
707 VERT_COPY(P,S);
708 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
709 dst += static_cast<gint>(pointP.x) * 3;
710 for(;pointP.x < pointE.x;pointP.x++)
711 {
712 //putpixel(P);
713 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
714 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
715 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
716 dst += 3;
717 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
718 }
719 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
720 pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
721 }
722 VERT_COPY(E,B);
723 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
724 {
725 if (pointE.x-pointS.x > 0)
726 {
727 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
728 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
729 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
730 }
731 else
732 {
733 dr=dg=db=0;
734 }
735 VERT_COPY(P,S);
736 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
737 dst += static_cast<gint>(pointP.x) * 3;
738 for(;pointP.x < pointE.x;pointP.x++)
739 {
740 //putpixel(P);
741 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
742 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
743 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
744 dst += 3;
745 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
746 }
747 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
748 pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
749 }
750 }
751 else if ( dx1 )
752 {
753 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
754 {
755 if (pointE.x-pointS.x > 0)
756 {
757 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
758 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
759 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
760 }
761 else
762 {
763 dr=dg=db=0;
764 }
765 VERT_COPY(P,S);
766 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
767 dst += static_cast<gint>(pointP.x) * 3;
768 for(;pointP.x < pointE.x;pointP.x++)
769 {
770 //putpixel(P);
771 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
772 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
773 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
774 dst += 3;
775 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
776 }
777 pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
778 pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
779 }
780 VERT_COPY(S,B);
781 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
782 {
783 if (pointE.x-pointS.x > 0)
784 {
785 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
786 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
787 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
788 }
789 else
790 {
791 dr=dg=db=0;
792 }
793 VERT_COPY(P,S);
794 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
795 dst += static_cast<gint>(pointP.x) * 3;
796 for(;pointP.x < pointE.x;pointP.x++)
797 {
798 //putpixel(P);
799 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
800 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
801 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
802 dst += 3;
803 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
804 }
805 pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
806 pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
807 }
808 }
810 // End of Gouraud fill ============================================================
812 wheel->_triDirty = FALSE;
813 //g_message( "Just updated triangle" );
814 }
815 }
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 bool dirty = FALSE;
1020 if (wheel->_image && (wheel->_bs < (size * size) )) {
1021 g_free (wheel->_image);
1022 wheel->_image = NULL;
1023 wheel->_bs = 0;
1025 if ( wheel->_triImage )
1026 {
1027 g_free( wheel->_triImage );
1028 wheel->_triImage = NULL;
1029 wheel->_triBs = 0;
1030 wheel->_triDirty = TRUE;
1031 }
1032 }
1034 if (!wheel->_image) {
1035 wheel->_image = g_new (guchar, size * size * 3);
1036 wheel->_bs = size * size;
1037 dirty = TRUE;
1038 //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
1039 }
1041 if ( dirty )
1042 {
1043 GtkWidget* widget = GTK_WIDGET (wheel);
1044 dp = wheel->_image;
1045 r = widget->style->bg[widget->state].red >> 8;
1046 g = widget->style->bg[widget->state].green >> 8;
1047 b = widget->style->bg[widget->state].blue >> 8;
1048 //g_message( "Rendered as state %d", widget->state );
1050 gint offset = wheel->_center;
1051 gint inner = wheel->_inner * wheel->_inner;
1052 gint rad = wheel->_center * wheel->_center;
1054 for (x = 0; x < size; x++) {
1055 guchar *d = dp;
1056 for (y = 0; y < size; y++) {
1057 gint dx = x - offset;
1058 gint dy = y - offset;
1059 gint hyp = (ABS(dx*dx) + ABS(dy*dy));
1060 if ( hyp >= inner && hyp <= rad)
1061 {
1062 gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
1064 gfloat rgb[3];
1065 sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
1067 d[0] = SP_COLOR_F_TO_U (rgb[0]);
1068 d[1] = SP_COLOR_F_TO_U (rgb[1]);
1069 d[2] = SP_COLOR_F_TO_U (rgb[2]);
1070 }
1071 else
1072 {
1073 /* Background value */
1074 d[0] = r;
1075 d[1] = g;
1076 d[2] = b;
1077 }
1079 d += 3 * size;
1080 }
1081 dp += 3;
1082 }
1083 }
1084 }
1086 static gboolean sp_color_wheel_focus(GtkWidget *widget,
1087 GtkDirectionType direction)
1088 {
1089 bool focusKept = FALSE;
1090 bool wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1091 SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1092 bool goingUp = FALSE;
1094 switch ( direction )
1095 {
1096 case GTK_DIR_TAB_FORWARD:
1097 case GTK_DIR_UP:
1098 case GTK_DIR_LEFT:
1099 goingUp = TRUE;
1100 break;
1102 case GTK_DIR_TAB_BACKWARD:
1103 case GTK_DIR_DOWN:
1104 case GTK_DIR_RIGHT:
1105 goingUp = FALSE;
1106 break;
1107 default:
1108 ;
1109 }
1111 if ( !wasFocused )
1112 {
1113 wheel->_inTriangle = !goingUp;
1114 gtk_widget_grab_focus (widget);
1115 focusKept = TRUE;
1116 }
1117 else if ( (!wheel->_inTriangle) == (!goingUp) )
1118 {
1119 focusKept = FALSE;
1120 }
1121 else
1122 {
1123 wheel->_inTriangle = !wheel->_inTriangle;
1124 gtk_widget_queue_draw( widget );
1125 focusKept = TRUE;
1126 }
1128 return focusKept;
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 NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
1136 y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
1138 gdouble rot = (M_PI * 2 * wheel->_hue );
1140 NR::Point result = delta * NR::rotate(rot);
1142 gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
1144 gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
1145 gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
1147 wheel->_triDirty = TRUE;
1149 sp_color_wheel_set_sv( wheel, sat, value );
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 :