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,
51 GtkDirectionType direction);
53 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y );
55 static GtkWidgetClass *parent_class;
56 static guint wheel_signals[LAST_SIGNAL] = {0};
58 /*
59 static double
60 get_time (void)
61 {
62 GTimeVal tv;
63 g_get_current_time (&tv);
64 return tv.tv_sec + 1e-6 * tv.tv_usec;
65 }
66 */
68 GtkType
69 sp_color_wheel_get_type (void)
70 {
71 static GtkType type = 0;
72 if (!type) {
73 GtkTypeInfo info = {
74 "SPColorWheel",
75 sizeof (SPColorWheel),
76 sizeof (SPColorWheelClass),
77 (GtkClassInitFunc) sp_color_wheel_class_init,
78 (GtkObjectInitFunc) sp_color_wheel_init,
79 NULL, NULL, NULL
80 };
81 type = gtk_type_unique (GTK_TYPE_WIDGET, &info);
82 }
83 return type;
84 }
86 static void
87 sp_color_wheel_class_init (SPColorWheelClass *klass)
88 {
89 GtkObjectClass *object_class;
90 GtkWidgetClass *widget_class;
92 object_class = (GtkObjectClass *) klass;
93 widget_class = (GtkWidgetClass *) klass;
95 parent_class = (GtkWidgetClass*)gtk_type_class (GTK_TYPE_WIDGET);
97 wheel_signals[CHANGED] = gtk_signal_new ("changed",
98 (GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
99 GTK_CLASS_TYPE(object_class),
100 GTK_SIGNAL_OFFSET (SPColorWheelClass, changed),
101 gtk_marshal_NONE__NONE,
102 GTK_TYPE_NONE, 0);
104 object_class->destroy = sp_color_wheel_destroy;
106 widget_class->realize = sp_color_wheel_realize;
107 widget_class->size_request = sp_color_wheel_size_request;
108 widget_class->size_allocate = sp_color_wheel_size_allocate;
110 widget_class->focus = sp_color_wheel_focus;
112 widget_class->expose_event = sp_color_wheel_expose;
113 widget_class->button_press_event = sp_color_wheel_button_press;
114 widget_class->button_release_event = sp_color_wheel_button_release;
115 widget_class->motion_notify_event = sp_color_wheel_motion_notify;
116 }
118 static void
119 sp_color_wheel_init (SPColorWheel *wheel)
120 {
121 /* We are widget with window */
122 GTK_WIDGET_UNSET_FLAGS (wheel, GTK_NO_WINDOW);
123 GTK_WIDGET_SET_FLAGS (wheel, GTK_CAN_FOCUS );
125 wheel->dragging = FALSE;
127 wheel->_inTriangle = FALSE;
128 wheel->_triDirty = TRUE;
129 wheel->_triangle = NULL;
130 for ( guint i = 0; i < G_N_ELEMENTS(wheel->_triPoints); i++ )
131 {
132 wheel->_triPoints[i].x = 0;
133 wheel->_triPoints[i].y = 0;
134 }
135 wheel->_triImage = NULL;
136 wheel->_triBs = 0;
138 wheel->_image = NULL;
139 wheel->_bs = 0;
140 wheel->_hue = 0.0;
141 wheel->_sat = 0.9;
142 wheel->_value = 0.25;
143 wheel->_inner = 0;
144 wheel->_center = 0;
145 wheel->_spotValue = 1.0;
146 }
148 static void
149 sp_color_wheel_destroy (GtkObject *object)
150 {
151 SPColorWheel *wheel;
153 wheel = SP_COLOR_WHEEL (object);
155 if ( wheel->_image )
156 {
157 g_free( wheel->_image );
158 wheel->_image = NULL;
159 wheel->_bs = 0;
160 }
162 if ( wheel->_triImage )
163 {
164 g_free( wheel->_triImage );
165 wheel->_triImage = NULL;
166 wheel->_triBs = 0;
167 }
169 if (((GtkObjectClass *) (parent_class))->destroy)
170 (* ((GtkObjectClass *) (parent_class))->destroy) (object);
171 }
174 void sp_color_wheel_get_color( SPColorWheel *wheel, SPColor* color )
175 {
176 float rgb[3];
177 gint i;
178 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
179 g_return_if_fail (wheel != NULL);
180 g_return_if_fail (color != NULL);
182 sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
183 for ( i = 0; i < 3; i++ )
184 {
185 rgb[i] = (rgb[i] * wheel->_sat) + (wheel->_value * (1.0 - wheel->_sat));
186 }
188 sp_color_set_rgb_float (color, rgb[0], rgb[1], rgb[2]);
189 }
191 void sp_color_wheel_set_color( SPColorWheel *wheel, const SPColor* color )
192 {
193 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
194 g_return_if_fail (wheel != NULL);
195 g_return_if_fail (color != NULL);
197 float hue;
198 float scratch[3];
199 float rgb[3];
201 sp_color_get_rgb_floatv (color, rgb);
202 sp_color_rgb_to_hsv_floatv (scratch, rgb[0], rgb[1], rgb[2]);
203 hue = scratch[0];
205 sp_color_hsv_to_rgb_floatv (scratch, hue, 1.0, 1.0);
207 gint lowInd = 0;
208 gint hiInd = 0;
209 for ( int i = 1; i < 3; i++ )
210 {
211 if ( scratch[i] < scratch[lowInd] )
212 {
213 lowInd = i;
214 }
215 if ( scratch[i] > scratch[hiInd] )
216 {
217 hiInd = i;
218 }
219 }
220 // scratch[lowInd] should always be 0
221 gdouble sat = (rgb[hiInd] - rgb[lowInd])/(scratch[hiInd]-scratch[lowInd]);
222 gdouble val = sat < 1.0 ? (rgb[hiInd] - sat * scratch[hiInd])/(1.0-sat) : 0.0;
225 sp_color_wheel_set_hue(wheel, hue);
226 sp_color_wheel_set_sv(wheel, sat, val);
227 }
229 gboolean sp_color_wheel_is_adjusting( SPColorWheel *wheel )
230 {
231 g_return_val_if_fail( SP_IS_COLOR_WHEEL(wheel), FALSE );
232 return wheel->dragging;
233 }
235 static void
236 sp_color_wheel_realize (GtkWidget *widget)
237 {
238 SPColorWheel *wheel;
239 GdkWindowAttr attributes;
240 gint attributes_mask;
242 wheel = SP_COLOR_WHEEL (widget);
244 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
246 attributes.window_type = GDK_WINDOW_CHILD;
247 attributes.x = widget->allocation.x;
248 attributes.y = widget->allocation.y;
249 attributes.width = widget->allocation.width;
250 attributes.height = widget->allocation.height;
251 attributes.wclass = GDK_INPUT_OUTPUT;
252 attributes.visual = gdk_rgb_get_visual ();
253 attributes.colormap = gdk_rgb_get_cmap ();
254 attributes.event_mask = gtk_widget_get_events (widget);
255 attributes.event_mask |= (GDK_EXPOSURE_MASK |
256 GDK_BUTTON_PRESS_MASK |
257 GDK_BUTTON_RELEASE_MASK |
258 GDK_POINTER_MOTION_MASK |
259 GDK_ENTER_NOTIFY_MASK |
260 GDK_LEAVE_NOTIFY_MASK |
261 GDK_FOCUS_CHANGE_MASK );
262 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
264 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
265 gdk_window_set_user_data (widget->window, widget);
267 widget->style = gtk_style_attach (widget->style, widget->window);
268 }
270 static void
271 sp_color_wheel_size_request (GtkWidget *widget, GtkRequisition *requisition)
272 {
273 SPColorWheel *wheel;
275 wheel = SP_COLOR_WHEEL (widget);
277 requisition->width = WHEEL_SIZE + widget->style->xthickness * 2;
278 requisition->height = WHEEL_SIZE + widget->style->ythickness * 2;
279 }
281 static void
282 sp_color_wheel_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
283 {
284 SPColorWheel *wheel;
286 wheel = SP_COLOR_WHEEL (widget);
288 widget->allocation = *allocation;
290 wheel->_center = MIN(allocation->width, allocation->height)/2;
291 wheel->_inner = (3 * wheel->_center)/4;
292 if ( wheel->_image )
293 {
294 g_free( wheel->_image );
295 wheel->_image = NULL;
296 wheel->_bs = 0;
297 }
299 // Need to render the gradient before we do the triangle over
300 sp_color_wheel_render_hue_wheel(wheel);
301 sp_color_wheel_recalc_triangle(wheel);
302 sp_color_wheel_render_triangle(wheel);
304 if (GTK_WIDGET_REALIZED (widget)) {
305 /* Resize GdkWindow */
306 gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
307 }
308 }
310 static gint
311 sp_color_wheel_expose (GtkWidget *widget, GdkEventExpose *event)
312 {
313 SPColorWheel *wheel;
315 wheel = SP_COLOR_WHEEL (widget);
317 if (GTK_WIDGET_DRAWABLE (widget)) {
318 gint width, height;
319 width = widget->allocation.width;
320 height = widget->allocation.height;
321 sp_color_wheel_paint (wheel, &event->area);
322 }
324 return TRUE;
325 }
327 static gint
328 sp_color_wheel_button_press (GtkWidget *widget, GdkEventButton *event)
329 {
330 SPColorWheel *wheel;
332 wheel = SP_COLOR_WHEEL (widget);
334 if (event->button == 1) {
335 gint cx, cw;
336 cx = widget->style->xthickness;
337 cw = widget->allocation.width - 2 * cx;
338 gboolean grabbed = FALSE;
340 {
341 double dx = event->x - wheel->_center;
342 double dy = event->y - wheel->_center;
343 gint hyp = static_cast<gint>(ABS(dx*dx) + ABS(dy*dy));
344 if ( hyp <= (wheel->_center*wheel->_center) )
345 {
346 if ( hyp >= (wheel->_inner*wheel->_inner) )
347 {
348 gdouble rot = atan2( dy, -dx );
349 sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
351 wheel->_inTriangle = FALSE;
352 grabbed = TRUE;
353 }
354 else if ( wheel->_triangle && gdk_region_point_in( wheel->_triangle, (gint)event->x, (gint)event->y ) )
355 {
356 wheel->_inTriangle = TRUE;
357 sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
358 grabbed = TRUE;
359 }
360 }
361 }
363 if ( grabbed )
364 {
365 gtk_widget_queue_draw( widget );
366 gtk_widget_grab_focus( widget );
368 wheel->dragging = TRUE;
369 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
370 gdk_pointer_grab (widget->window, FALSE,
371 (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
372 NULL, NULL, event->time);
373 }
374 }
376 return TRUE;
377 }
379 static gint
380 sp_color_wheel_button_release (GtkWidget *widget, GdkEventButton *event)
381 {
382 SPColorWheel *wheel;
384 wheel = SP_COLOR_WHEEL (widget);
386 if (event->button == 1) {
387 gdk_pointer_ungrab (event->time);
388 wheel->dragging = FALSE;
390 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
391 }
393 return TRUE;
394 }
396 static gint
397 sp_color_wheel_motion_notify (GtkWidget *widget, GdkEventMotion *event)
398 {
399 SPColorWheel *wheel;
401 wheel = SP_COLOR_WHEEL (widget);
403 if (wheel->dragging) {
404 double dx = event->x - wheel->_center;
405 double dy = event->y - wheel->_center;
406 if ( !wheel->_inTriangle )
407 {
408 gdouble rot = atan2( dy, -dx );
409 sp_color_wheel_set_hue (wheel, (rot + M_PI) / (2.0 * M_PI));
410 }
411 else
412 {
413 sp_color_wheel_process_in_triangle( wheel, event->x, event->y );
414 }
416 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
417 }
419 return TRUE;
420 }
422 GtkWidget *
423 sp_color_wheel_new ()
424 {
425 SPColorWheel *wheel;
427 wheel = (SPColorWheel*)gtk_type_new (SP_TYPE_COLOR_WHEEL);
429 return GTK_WIDGET (wheel);
430 }
432 static void sp_color_wheel_set_hue(SPColorWheel *wheel, gdouble hue)
433 {
434 g_return_if_fail (SP_IS_COLOR_WHEEL (wheel));
436 if ( wheel->_hue != hue )
437 {
438 wheel->_hue = hue;
440 sp_color_wheel_recalc_triangle(wheel);
442 SPColor color;
443 gfloat rgb[3];
444 sp_color_wheel_get_color( wheel, &color );
445 sp_color_get_rgb_floatv (&color, rgb);
447 wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
449 gtk_widget_queue_draw (GTK_WIDGET (wheel));
450 }
451 }
454 static void sp_color_wheel_set_sv( SPColorWheel *wheel, gdouble sat, gdouble value )
455 {
456 static gdouble epsilon = 1e-6;
457 gboolean changed = FALSE;
459 if ( ABS( wheel->_sat - sat ) > epsilon )
460 {
461 wheel->_sat = sat;
462 changed = TRUE;
463 }
464 if ( ABS( wheel->_value - value ) > epsilon )
465 {
466 wheel->_value = value;
467 changed = TRUE;
468 }
470 if ( changed )
471 {
472 SPColor color;
473 gfloat rgb[3];
474 sp_color_wheel_get_color( wheel, &color );
475 sp_color_get_rgb_floatv (&color, rgb);
477 wheel->_spotValue = ( (0.299 * rgb[0]) + (0.587 * rgb[1]) + (0.114 * rgb[2]) );
479 gtk_signal_emit (GTK_OBJECT (wheel), wheel_signals[CHANGED]);
480 }
481 gtk_widget_queue_draw (GTK_WIDGET (wheel));
482 }
484 static void sp_color_wheel_recalc_triangle(SPColorWheel *wheel)
485 {
486 if ( wheel->_triangle )
487 {
488 gdk_region_destroy( wheel->_triangle );
489 wheel->_triangle = NULL;
490 }
491 wheel->_triDirty = TRUE;
493 if ( wheel->_center > 0 && wheel->_inner > 0 )
494 {
495 gdouble dx = cos( M_PI * 2 * wheel->_hue );
496 gdouble dy = -sin( M_PI * 2 * wheel->_hue );
498 wheel->_triPoints[0].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
499 wheel->_triPoints[0].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
501 dx = cos( M_PI * 2 * wheel->_hue + ((M_PI * 2)/ 3) );
502 dy = -sin( M_PI * 2 * wheel->_hue + ((M_PI * 2) / 3) );
503 wheel->_triPoints[1].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
504 wheel->_triPoints[1].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
506 dx = cos( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
507 dy = -sin( M_PI * 2 * wheel->_hue - ((M_PI*2) / 3) );
508 wheel->_triPoints[2].x = wheel->_center + static_cast<gint>(dx * wheel->_inner);
509 wheel->_triPoints[2].y = wheel->_center + static_cast<gint>(dy * wheel->_inner);
512 wheel->_triangle = gdk_region_polygon( wheel->_triPoints,
513 3,
514 GDK_EVEN_ODD_RULE );
515 }
516 }
518 #define VERT_SWAP( X, Y ) { \
519 gfloat tmpF; \
520 \
521 tmpF = point##X.x; \
522 point##X.x = point##Y.x; \
523 point##Y.x = tmpF; \
524 \
525 tmpF = point##X.y; \
526 point##X.y = point##Y.y; \
527 point##Y.y = tmpF; \
528 \
529 tmpF = rgb##X[0]; \
530 rgb##X[0] = rgb##Y[0]; \
531 rgb##Y[0] = tmpF; \
532 \
533 tmpF = rgb##X[1]; \
534 rgb##X[1] = rgb##Y[1]; \
535 rgb##Y[1] = tmpF; \
536 \
537 tmpF = rgb##X[2]; \
538 rgb##X[2] = rgb##Y[2]; \
539 rgb##Y[2] = tmpF; \
540 }
542 #define VERT_COPY( dst, src ) { \
543 point##dst.x = point##src.x; \
544 \
545 point##dst.y = point##src.y; \
546 \
547 rgb##dst[0] = rgb##src[0]; \
548 rgb##dst[1] = rgb##src[1]; \
549 rgb##dst[2] = rgb##src[2]; \
550 }
552 typedef struct {
553 gfloat x;
554 gfloat y;
555 } PointF;
557 static void sp_color_wheel_render_triangle (SPColorWheel *wheel)
558 {
559 if ( wheel->_image )
560 {
561 if ( wheel->_triBs < wheel->_bs )
562 {
563 g_free( wheel->_triImage );
564 wheel->_triImage = NULL;
565 }
567 if (wheel->_triDirty || !wheel->_triImage)
568 {
569 if ( !wheel->_triImage )
570 {
571 wheel->_triBs = wheel->_bs;
572 wheel->_triImage = g_new (guchar, wheel->_triBs * 3);
573 //g_message( "just allocated %fKB for tri", ((wheel->_triBs * 3.0)/1024.0) );
574 }
576 memcpy( wheel->_triImage, wheel->_image, wheel->_bs * 3 );
578 PointF pointA, pointB, pointC;
579 pointA.x = wheel->_triPoints[0].x;
580 pointA.y = wheel->_triPoints[0].y;
581 pointB.x = wheel->_triPoints[1].x;
582 pointB.y = wheel->_triPoints[1].y;
583 pointC.x = wheel->_triPoints[2].x;
584 pointC.y = wheel->_triPoints[2].y;
586 gfloat rgbA[3];
587 gfloat rgbB[3] = {0.0, 0.0, 0.0};
588 gfloat rgbC[3] = {1.0, 1.0, 1.0};
590 sp_color_hsv_to_rgb_floatv (rgbA, wheel->_hue, 1.0, 1.0);
592 // Start of Gouraud fill ============================================================
593 gint rowStride = wheel->_center * 2 * 3;
594 guchar* dst = wheel->_triImage;
596 if ( pointC.y < pointB.y )
597 VERT_SWAP( C, B );
599 if ( pointC.y < pointA.y )
600 VERT_SWAP( C, A );
602 if ( pointB.y < pointA.y )
603 VERT_SWAP( B, A );
605 if ( pointA.y == pointB.y && pointB.x < pointA.x )
606 VERT_SWAP( A, B );
608 gfloat dr, dg, db;
610 gfloat dx1,dx2,dx3;
611 gfloat dr1,dr2,dr3;
612 gfloat dg1,dg2,dg3;
613 gfloat db1,db2,db3;
616 PointF pointS;
617 PointF pointE;
618 PointF pointP;
619 gfloat rgbS[3];
620 gfloat rgbE[3];
621 gfloat rgbP[3];
624 if (pointB.y-pointA.y > 0)
625 {
626 dx1=(pointB.x-pointA.x)/(pointB.y-pointA.y);
627 dr1=(rgbB[0]-rgbA[0])/(pointB.y-pointA.y);
628 dg1=(rgbB[1]-rgbA[1])/(pointB.y-pointA.y);
629 db1=(rgbB[2]-rgbA[2])/(pointB.y-pointA.y);
630 }
631 else
632 {
633 dx1=dr1=dg1=db1=0;
634 }
636 if (pointC.y-pointA.y > 0)
637 {
638 dx2=(pointC.x-pointA.x)/(pointC.y-pointA.y);
639 dr2=(rgbC[0]-rgbA[0])/(pointC.y-pointA.y);
640 dg2=(rgbC[1]-rgbA[1])/(pointC.y-pointA.y);
641 db2=(rgbC[2]-rgbA[2])/(pointC.y-pointA.y);
642 }
643 else
644 {
645 dx2=dr2=dg2=db2=0;
646 }
648 if (pointC.y-pointB.y > 0)
649 {
650 dx3=(pointC.x-pointB.x)/(pointC.y-pointB.y);
651 dr3=(rgbC[0]-rgbB[0])/(pointC.y-pointB.y);
652 dg3=(rgbC[1]-rgbB[1])/(pointC.y-pointB.y);
653 db3=(rgbC[2]-rgbB[2])/(pointC.y-pointB.y);
654 }
655 else
656 {
657 dx3=dr3=dg3=db3=0;
658 }
660 VERT_COPY(S, A);
661 VERT_COPY(E, A);
663 if ( dx1 == 0 )
664 {
665 VERT_COPY(E,B);
666 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
667 {
668 if (pointE.x-pointS.x > 0)
669 {
670 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
671 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
672 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
673 }
674 else
675 {
676 dr=dg=db=0;
677 }
678 VERT_COPY(P,S);
679 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
680 dst += static_cast<gint>(pointP.x) * 3;
681 for(;pointP.x < pointE.x;pointP.x++)
682 {
683 //putpixel(P);
684 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
685 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
686 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
687 dst += 3;
688 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
689 }
690 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
691 pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
692 }
693 }
694 else if (dx1 > dx2)
695 {
696 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
697 {
698 if (pointE.x-pointS.x > 0)
699 {
700 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
701 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
702 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
703 }
704 else
705 {
706 dr=dg=db=0;
707 }
708 VERT_COPY(P,S);
709 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
710 dst += static_cast<gint>(pointP.x) * 3;
711 for(;pointP.x < pointE.x;pointP.x++)
712 {
713 //putpixel(P);
714 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
715 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
716 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
717 dst += 3;
718 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
719 }
720 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
721 pointE.x+=dx1; rgbE[0]+=dr1; rgbE[1]+=dg1; rgbE[2]+=db1;
722 }
723 VERT_COPY(E,B);
724 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
725 {
726 if (pointE.x-pointS.x > 0)
727 {
728 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
729 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
730 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
731 }
732 else
733 {
734 dr=dg=db=0;
735 }
736 VERT_COPY(P,S);
737 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
738 dst += static_cast<gint>(pointP.x) * 3;
739 for(;pointP.x < pointE.x;pointP.x++)
740 {
741 //putpixel(P);
742 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
743 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
744 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
745 dst += 3;
746 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
747 }
748 pointS.x+=dx2; rgbS[0]+=dr2; rgbS[1]+=dg2; rgbS[2]+=db2;
749 pointE.x+=dx3; rgbE[0]+=dr3; rgbE[1]+=dg3; rgbE[2]+=db3;
750 }
751 }
752 else if ( dx1 )
753 {
754 for(;pointS.y <= pointB.y; pointS.y++,pointE.y++)
755 {
756 if (pointE.x-pointS.x > 0)
757 {
758 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
759 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
760 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
761 }
762 else
763 {
764 dr=dg=db=0;
765 }
766 VERT_COPY(P,S);
767 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
768 dst += static_cast<gint>(pointP.x) * 3;
769 for(;pointP.x < pointE.x;pointP.x++)
770 {
771 //putpixel(P);
772 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
773 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
774 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
775 dst += 3;
776 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
777 }
778 pointS.x+=dx1; rgbS[0]+=dr1; rgbS[1]+=dg1; rgbS[2]+=db1;
779 pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
780 }
781 VERT_COPY(S,B);
782 for(;pointS.y <= pointC.y; pointS.y++,pointE.y++)
783 {
784 if (pointE.x-pointS.x > 0)
785 {
786 dr=(rgbE[0]-rgbS[0])/(pointE.x-pointS.x);
787 dg=(rgbE[1]-rgbS[1])/(pointE.x-pointS.x);
788 db=(rgbE[2]-rgbS[2])/(pointE.x-pointS.x);
789 }
790 else
791 {
792 dr=dg=db=0;
793 }
794 VERT_COPY(P,S);
795 dst = wheel->_triImage + (static_cast<gint>(pointP.y) * rowStride);
796 dst += static_cast<gint>(pointP.x) * 3;
797 for(;pointP.x < pointE.x;pointP.x++)
798 {
799 //putpixel(P);
800 dst[0] = SP_COLOR_F_TO_U(rgbP[0]);
801 dst[1] = SP_COLOR_F_TO_U(rgbP[1]);
802 dst[2] = SP_COLOR_F_TO_U(rgbP[2]);
803 dst += 3;
804 rgbP[0]+=dr; rgbP[1]+=dg; rgbP[2]+=db;
805 }
806 pointS.x+=dx3; rgbS[0]+=dr3; rgbS[1]+=dg3; rgbS[2]+=db3;
807 pointE.x+=dx2; rgbE[0]+=dr2; rgbE[1]+=dg2; rgbE[2]+=db2;
808 }
809 }
811 // End of Gouraud fill ============================================================
813 wheel->_triDirty = FALSE;
814 //g_message( "Just updated triangle" );
815 }
816 }
817 }
819 static void
820 sp_color_wheel_paint (SPColorWheel *wheel, GdkRectangle *area)
821 {
822 GtkWidget *widget;
823 GdkRectangle warea, carea;
824 GdkRectangle wpaint, cpaint;
826 widget = GTK_WIDGET (wheel);
828 /* Widget area */
829 warea.x = 0;
830 warea.y = 0;
831 warea.width = widget->allocation.width;
832 warea.height = widget->allocation.height;
834 /* Color gradient area */
835 carea.x = widget->style->xthickness;
836 carea.y = widget->style->ythickness;
837 carea.width = widget->allocation.width - 2 * carea.x;
838 carea.height = widget->allocation.height - 2 * carea.y;
840 /* Actual paintable area */
841 if (!gdk_rectangle_intersect (area, &warea, &wpaint)) return;
843 //g_message( "Painted as state %d", widget->state );
845 /* Paintable part of color gradient area */
846 if (gdk_rectangle_intersect (area, &carea, &cpaint)) {
847 sp_color_wheel_render_hue_wheel (wheel);
848 sp_color_wheel_render_triangle (wheel);
849 }
851 /*
852 gtk_draw_box (widget->style,
853 widget->window,
854 (GtkStateType)widget->state,
855 GTK_SHADOW_NONE,
856 warea.x,
857 warea.y,
858 warea.width,
859 warea.height);
860 */
862 gtk_style_apply_default_background( widget->style,
863 widget->window,
864 TRUE,
865 (GtkStateType)widget->state,
866 NULL,
867 0,
868 0,
869 warea.width,
870 warea.height);
873 /* Draw shadow */
874 /*
875 gtk_paint_shadow (widget->style, widget->window,
876 (GtkStateType)widget->state, GTK_SHADOW_IN,
877 NULL, widget, "colorwheel",
878 0, 0,
879 warea.width, warea.height);
880 */
883 /* Draw pixelstore */
884 if (wheel->_triImage != NULL) {
885 //gdouble start, end;
886 //start = get_time();
887 gdk_draw_rgb_image (widget->window, widget->style->black_gc,
888 0, 0,//cpaint.x, cpaint.y,
889 //cpaint.width, cpaint.height,
890 wheel->_center * 2, wheel->_center * 2,
891 GDK_RGB_DITHER_MAX,
892 wheel->_triImage, wheel->_center * 6);
894 //end = get_time();
895 //g_message( "blits took %f", (end-start) );
896 }
898 {
899 gdouble dx = cos( M_PI * 2 * wheel->_hue );
900 gdouble dy = -sin( M_PI * 2 * wheel->_hue );
902 gfloat rgb[3];
903 sp_color_hsv_to_rgb_floatv (rgb, wheel->_hue, 1.0, 1.0);
905 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;
907 gint inx = wheel->_center + static_cast<gint>(dx * wheel->_inner);
908 gint iny = wheel->_center + static_cast<gint>(dy * wheel->_inner);
911 gdk_draw_line (widget->window, line_gc,
912 inx, iny,
913 wheel->_center + static_cast<gint>(dx * wheel->_center), wheel->_center + static_cast<gint>(dy * wheel->_center) );
916 GdkGCValues values;
918 if ( GTK_WIDGET_HAS_FOCUS(wheel) )
919 {
920 line_gc = widget->style->black_gc;
922 gdk_gc_get_values ( line_gc, &values );
924 gdk_gc_set_line_attributes ( line_gc,
925 3, // Line width
926 values.line_style, //GDK_LINE_SOLID,
927 values.cap_style, //GDK_CAP_BUTT,
928 values.join_style ); //GDK_JOIN_MITER );
930 if ( wheel->_inTriangle )
931 {
932 gdk_draw_line (widget->window, line_gc,
933 wheel->_triPoints[0].x, wheel->_triPoints[0].y,
934 wheel->_triPoints[1].x, wheel->_triPoints[1].y );
936 gdk_draw_line (widget->window, line_gc,
937 wheel->_triPoints[1].x, wheel->_triPoints[1].y,
938 wheel->_triPoints[2].x, wheel->_triPoints[2].y );
940 gdk_draw_line (widget->window, line_gc,
941 wheel->_triPoints[2].x, wheel->_triPoints[2].y,
942 wheel->_triPoints[0].x, wheel->_triPoints[0].y );
943 }
944 else
945 {
946 gdk_draw_arc (widget->window, line_gc,
947 FALSE, // filled
948 0, 0,
949 wheel->_center * 2, wheel->_center * 2,
950 0, 64 * 360 );
952 gint diff = wheel->_center - wheel->_inner;
954 gdk_draw_arc (widget->window, line_gc,
955 FALSE, // filled
956 diff, diff,
957 wheel->_inner * 2, wheel->_inner * 2,
958 0, 64 * 360 );
960 }
961 gdk_gc_set_line_attributes ( line_gc,
962 values.line_width, // Line width
963 values.line_style, //GDK_LINE_SOLID,
964 values.cap_style, //GDK_CAP_BUTT,
965 values.join_style ); //GDK_JOIN_MITER );
966 }
967 // ==========
969 // line_gc = (p[3] < 0x80) ? widget->style->white_gc : widget->style->black_gc;
970 line_gc = (wheel->_spotValue < 0.5) ? widget->style->white_gc : widget->style->black_gc;
972 gdk_gc_get_values ( line_gc, &values );
974 gdk_gc_set_line_attributes ( line_gc,
975 2, // Line width
976 values.line_style, //GDK_LINE_SOLID,
977 values.cap_style, //GDK_CAP_BUTT,
978 values.join_style ); //GDK_JOIN_MITER );
980 gint pointX = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].x + wheel->_value*(gdouble)wheel->_triPoints[2].x)
981 + (wheel->_sat * wheel->_triPoints[0].x) );
983 gint pointY = (gint)( (1.0 - wheel->_sat) * ((1.0-wheel->_value)*(gdouble)wheel->_triPoints[1].y + wheel->_value*(gdouble)wheel->_triPoints[2].y)
984 + (wheel->_sat * wheel->_triPoints[0].y) );
987 gdk_gc_set_line_attributes ( line_gc,
988 values.line_width, // Line width
989 values.line_style, //GDK_LINE_SOLID,
990 values.cap_style, //GDK_CAP_BUTT,
991 values.join_style ); //GDK_JOIN_MITER );
993 gdk_draw_arc (widget->window, line_gc,
994 FALSE, // filled
995 pointX - 4, pointY - 4,
996 8, 8,
997 0, 64 * 360 );
999 gdk_draw_arc (widget->window, line_gc,
1000 FALSE, // filled
1001 pointX - 3, pointY - 3,
1002 6, 6,
1003 0, 64 * 360 );
1007 }
1008 }
1010 /* Colors are << 16 */
1012 static void
1013 sp_color_wheel_render_hue_wheel (SPColorWheel *wheel)
1014 {
1015 guchar *dp;
1016 gint x, y;
1017 guint r, g, b;
1018 gint size = wheel->_center * 2;
1019 gboolean dirty = FALSE;
1021 if (wheel->_image && (wheel->_bs < (size * size) )) {
1022 g_free (wheel->_image);
1023 wheel->_image = NULL;
1024 wheel->_bs = 0;
1026 if ( wheel->_triImage )
1027 {
1028 g_free( wheel->_triImage );
1029 wheel->_triImage = NULL;
1030 wheel->_triBs = 0;
1031 wheel->_triDirty = TRUE;
1032 }
1033 }
1035 if (!wheel->_image) {
1036 wheel->_image = g_new (guchar, size * size * 3);
1037 wheel->_bs = size * size;
1038 dirty = TRUE;
1039 //g_message( "just allocated %fKB for hue", ((wheel->_bs * 3.0)/1024.0) );
1040 }
1042 if ( dirty )
1043 {
1044 GtkWidget* widget = GTK_WIDGET (wheel);
1045 dp = wheel->_image;
1046 r = widget->style->bg[widget->state].red >> 8;
1047 g = widget->style->bg[widget->state].green >> 8;
1048 b = widget->style->bg[widget->state].blue >> 8;
1049 //g_message( "Rendered as state %d", widget->state );
1051 gint offset = wheel->_center;
1052 gint inner = wheel->_inner * wheel->_inner;
1053 gint rad = wheel->_center * wheel->_center;
1055 for (x = 0; x < size; x++) {
1056 guchar *d = dp;
1057 for (y = 0; y < size; y++) {
1058 gint dx = x - offset;
1059 gint dy = y - offset;
1060 gint hyp = (ABS(dx*dx) + ABS(dy*dy));
1061 if ( hyp >= inner && hyp <= rad)
1062 {
1063 gdouble rot = atan2( static_cast<gdouble>(dy), static_cast<gdouble>(-dx) );
1065 gfloat rgb[3];
1066 sp_color_hsv_to_rgb_floatv (rgb, (rot + M_PI) / (2.0 * M_PI), 1.0, 1.0);
1068 d[0] = SP_COLOR_F_TO_U (rgb[0]);
1069 d[1] = SP_COLOR_F_TO_U (rgb[1]);
1070 d[2] = SP_COLOR_F_TO_U (rgb[2]);
1071 }
1072 else
1073 {
1074 /* Background value */
1075 d[0] = r;
1076 d[1] = g;
1077 d[2] = b;
1078 }
1080 d += 3 * size;
1081 }
1082 dp += 3;
1083 }
1084 }
1085 }
1087 static gboolean sp_color_wheel_focus(GtkWidget *widget,
1088 GtkDirectionType direction)
1089 {
1090 gboolean focusKept = FALSE;
1091 gboolean wasFocused = GTK_WIDGET_HAS_FOCUS(widget);
1092 SPColorWheel* wheel = SP_COLOR_WHEEL(widget);
1093 gboolean goingUp = FALSE;
1095 switch ( direction )
1096 {
1097 case GTK_DIR_TAB_FORWARD:
1098 case GTK_DIR_UP:
1099 case GTK_DIR_LEFT:
1100 goingUp = TRUE;
1101 break;
1103 case GTK_DIR_TAB_BACKWARD:
1104 case GTK_DIR_DOWN:
1105 case GTK_DIR_RIGHT:
1106 goingUp = FALSE;
1107 break;
1108 default:
1109 ;
1110 }
1112 if ( !wasFocused )
1113 {
1114 wheel->_inTriangle = !goingUp;
1115 gtk_widget_grab_focus (widget);
1116 focusKept = TRUE;
1117 }
1118 else if ( (!wheel->_inTriangle) == (!goingUp) )
1119 {
1120 focusKept = FALSE;
1121 }
1122 else
1123 {
1124 wheel->_inTriangle = !wheel->_inTriangle;
1125 gtk_widget_queue_draw( widget );
1126 focusKept = TRUE;
1127 }
1129 return focusKept;
1130 }
1132 static void sp_color_wheel_process_in_triangle( SPColorWheel *wheel, gdouble x, gdouble y )
1133 {
1134 // njh: dot(rot90(B-C), x) = saturation
1135 // njh: dot(B-C, x) = value
1136 NR::Point delta( x - (((gdouble)(wheel->_triPoints[1].x + wheel->_triPoints[2].x)) / 2.0),
1137 y - (((gdouble)(wheel->_triPoints[1].y + wheel->_triPoints[2].y)) / 2.0) );
1139 gdouble rot = (M_PI * 2 * wheel->_hue );
1141 NR::Point result = delta * NR::rotate(rot);
1143 gdouble sat = CLAMP( result[NR::X] / (wheel->_inner * 1.5), 0.0, 1.0 );
1145 gdouble halfHeight = (wheel->_inner * sin(M_PI/3.0)) * (1.0 - sat);
1146 gdouble value = CLAMP( ((result[NR::Y]+ halfHeight) / (2.0*halfHeight)), 0.0, 1.0 );
1148 wheel->_triDirty = TRUE;
1150 sp_color_wheel_set_sv( wheel, sat, value );
1151 }
1154 /*
1155 Local Variables:
1156 mode:c++
1157 c-file-style:"stroustrup"
1158 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1159 indent-tabs-mode:nil
1160 fill-column:99
1161 End:
1162 */
1163 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :