1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include "colorchooser.h"
24
25 #include <Xm/XmAll.h>
26 #include <Xm/XmP.h>
27 #include <X11/Xft/Xft.h>
28
29 #include <inttypes.h>
30 #include <limits.h>
31
32 #define XNE_COLOR_DIALOG_TITLE "Select Color"
33
34 #define WIDGET_SPACING 5
35 #define WINDOW_SPACING 8
36
37 typedef struct {
38 Widget dialog;
39
40 Widget preview;
41 Widget textfield;
42 Widget selector;
43 GC gc;
44 GC selGC;
45 XftDraw *d;
46
47 uint8_t base_red;
48 uint8_t base_green;
49 uint8_t base_blue;
50
51 int base_sel_y;
52 float hue;
53 float saturation;
54 float value;
55
56 uint8_t selected_red;
57 uint8_t selected_green;
58 uint8_t selected_blue;
59 XftColor selected_color;
60
61 XImage *image1;
62 Dimension img1_width;
63 Dimension img1_height;
64
65 XImage *image2;
66 Dimension img2_width;
67 Dimension img2_height;
68
69 int has_selection;
70 Dimension img2_select_x;
71 Dimension img2_select_y;
72
73 int parse_textfield_input;
74
75 int status;
76 int end;
77 } cgData;
78
79 static XftDraw* create_xft_draw(Widget w);
80 static void selector_expose(Widget w, XtPointer u, XtPointer c);
81 static void selector_input(Widget w, XtPointer u, XtPointer c);
82 static void preview_color(Widget w, XtPointer u, XtPointer c);
83 static void textfield_changed(Widget w, XtPointer u, XtPointer c);
84 static void set_base_color(cgData *data,
int r,
int g,
int b);
85 static void select_color(cgData *data,
int r,
int g,
int b);
86
87 static void draw_img2(Display *dp, Window win, cgData *data);
88
89 static void okCB(Widget w, XtPointer u, XtPointer c);
90 static void cancelCB(Widget w, XtPointer u, XtPointer c);
91
92 int ColorChooser(Widget parent,
int *red,
int *green,
int *blue) {
93 Arg args[
32];
94 int n;
95 XmString str;
96
97 cgData data;
98 memset(&data,
0,
sizeof(data));
99
100 data.base_sel_y = -
1;
101
102 n =
0;
103 XtSetArg(args[n], XmNautoUnmanage, False); n++;
104 Widget form = XmCreateFormDialog(parent,
"ColorDialog", args, n);
105 Widget dialog = XtParent(form);
106 XtVaSetValues(dialog, XmNtitle,
XNE_COLOR_DIALOG_TITLE,
NULL);
107 XtManageChild(form);
108
109
110
111
112 n =
0;
113 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++;
114 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
115 XtSetArg(args[n], XmNtopOffset,
WINDOW_SPACING); n++;
116 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
117 XtSetArg(args[n], XmNleftOffset,
WINDOW_SPACING); n++;
118 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
119 XtSetArg(args[n], XmNrightPosition,
30); n++;
120
121 data.preview = XmCreateDrawingArea(form,
"cgPreview", args, n);
122 XtManageChild(data.preview);
123 XtAddCallback(data.preview, XmNexposeCallback, preview_color, &data);
124
125
126 n =
0;
127 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
128 XtSetArg(args[n], XmNtopOffset,
WINDOW_SPACING); n++;
129 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
130 XtSetArg(args[n], XmNrightOffset,
WINDOW_SPACING); n++;
131 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
132 XtSetArg(args[n], XmNleftPosition,
33); n++;
133 data.textfield = XmCreateTextField(form,
"cgText", args, n);
134 XtManageChild(data.textfield);
135 XtAddCallback(data.textfield, XmNvalueChangedCallback, textfield_changed, &data);
136
137 XtVaSetValues(data.preview, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, data.textfield,
NULL);
138
139
140
141 n =
0;
142 str = XmStringCreateSimple(
"OK");
143 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
144 XtSetArg(args[n], XmNbottomOffset,
WINDOW_SPACING); n++;
145 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
146 XtSetArg(args[n], XmNleftPosition,
12); n++;
147 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
148 XtSetArg(args[n], XmNrightPosition,
37); n++;
149 XtSetArg(args[n], XmNlabelString, str); n++;
150 Widget ok = XmCreatePushButton(form,
"cgOK", args, n);
151 XtManageChild(ok);
152 XmStringFree(str);
153 XtAddCallback(ok, XmNactivateCallback, okCB, &data);
154
155 n =
0;
156 str = XmStringCreateSimple(
"Cancel");
157 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
158 XtSetArg(args[n], XmNbottomOffset,
WINDOW_SPACING); n++;
159 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
160 XtSetArg(args[n], XmNleftPosition,
63); n++;
161 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
162 XtSetArg(args[n], XmNrightPosition,
88); n++;
163 XtSetArg(args[n], XmNlabelString, str); n++;
164 Widget cancel = XmCreatePushButton(form,
"cgOK", args, n);
165 XtManageChild(cancel);
166 XmStringFree(str);
167 XtAddCallback(cancel, XmNactivateCallback, cancelCB, &data);
168
169 XtVaSetValues(form, XmNdefaultButton, ok,
NULL);
170 XtVaSetValues(form, XmNcancelButton, cancel,
NULL);
171
172
173 static XtTranslations selectorTranslations =
NULL;
174 if(!selectorTranslations) {
175 selectorTranslations = XtParseTranslationTable(
"<Btn1Down>: DrawingAreaInput() ManagerGadgetArm()\n<Btn1Motion>: DrawingAreaInput() ManagerGadgetButtonMotion()");
176 }
177
178 n =
0;
179 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
180 XtSetArg(args[n], XmNtopWidget, data.textfield); n++;
181 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
182 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
183 XtSetArg(args[n], XmNtopOffset,
WINDOW_SPACING); n++;
184 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
185 XtSetArg(args[n], XmNbottomWidget, ok); n++;
186 XtSetArg(args[n], XmNbottomOffset,
WINDOW_SPACING); n++;
187 XtSetArg(args[n], XmNleftOffset,
WINDOW_SPACING); n++;
188 XtSetArg(args[n], XmNrightOffset,
WINDOW_SPACING); n++;
189 XtSetArg(args[n], XmNwidth,
500); n++;
190 XtSetArg(args[n], XmNheight,
400); n++;
191 XtSetArg(args[n], XmNtranslations, selectorTranslations); n++;
192 data.selector = XmCreateDrawingArea(form,
"cgSelector", args, n);
193 XtManageChild(data.selector);
194 XtAddCallback(data.selector, XmNexposeCallback, selector_expose, &data);
195 XtAddCallback(data.selector, XmNinputCallback, selector_input, &data);
196
197 XGCValues gcValues;
198 gcValues.background = data.selector->core.background_pixel;
199 gcValues.foreground = BlackPixelOfScreen(DefaultScreenOfDisplay(XtDisplay(data.selector)));
200 gcValues.graphics_exposures =
0;
201 data.gc = XtAllocateGC(data.selector,
0, GCForeground | GCBackground, &gcValues,
0,
0);
202
203 gcValues.background = data.selector->core.background_pixel;
204 gcValues.foreground = WhitePixelOfScreen(DefaultScreenOfDisplay(XtDisplay(data.selector)));
205 gcValues.graphics_exposures =
0;
206 data.selGC = XtAllocateGC(data.selector,
0, GCForeground | GCBackground, &gcValues,
0,
0);
207
208
209 select_color(&data, *red /
257, *green /
257, *blue /
257);
210
211
212 set_base_color(&data, (*red)/
257, (*green)/
257, (*blue)/
257);
213
214
215 XtManageChild(dialog);
216
217 data.parse_textfield_input =
1;
218
219 XtAppContext app = XtWidgetToApplicationContext(dialog);
220 while(!data.end && !XtAppGetExitFlag(app)) {
221 XEvent event;
222 XtAppNextEvent(app, &event);
223 XtDispatchEvent(&event);
224 }
225
226 XtReleaseGC(data.selector, data.gc);
227 XtReleaseGC(data.selector, data.selGC);
228 if(data.image1) XDestroyImage(data.image1);
229 if(data.image2) XDestroyImage(data.image2);
230 if(data.d) XftDrawDestroy(data.d);
231 XtDestroyWidget(dialog);
232
233 if(data.status ==
1) {
234 *red = data.selected_color.color.red;
235 *green = data.selected_color.color.green;
236 *blue = data.selected_color.color.blue;
237 }
238
239 return data.status;
240 }
241
242 static XftDraw* create_xft_draw(Widget w) {
243 XWindowAttributes attributes;
244 XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attributes);
245
246 Screen *screen = w->core.screen;
247 Visual *visual = screen->root_visual;
248 for(
int i=
0;i<screen->ndepths;i++) {
249 Depth d = screen->depths[i];
250 if(d.depth == w->core.depth) {
251 visual = d.visuals;
252 break;
253 }
254 }
255
256 Display *dp = XtDisplay(w);
257 return XftDrawCreate(
258 dp,
259 XtWindow(w),
260 visual,
261 w->core.colormap);
262
263 }
264
265
266
267
268 static int get_shift(
unsigned long mask) {
269 if(mask ==
0) {
270 return 0;
271 }
272
273 int shift =
0;
274 while((mask &
1L) ==
0) {
275 shift++;
276 mask >>=
1;
277 }
278 return shift;
279 }
280
281
282
283
284 static int get_mask_len(
unsigned long mask) {
285 if(mask ==
0) {
286 return 0;
287 }
288
289 while((mask &
1L) ==
0) {
290 mask >>=
1;
291 }
292 int len =
0;
293 while((mask &
1L) ==
1) {
294 len++;
295 mask >>=
1;
296 }
297 return len;
298 }
299
300 static Visual* get_visual(Screen *screen,
int depth) {
301
302 Visual *visual =
NULL;
303 for(
int i=
0;i<screen->ndepths;i++) {
304 Depth d = screen->depths[i];
305 if(d.depth == depth) {
306 for(
int v=
0;v<d.nvisuals;v++) {
307 Visual *vs = &d.visuals[v];
308 if(get_mask_len(vs->red_mask) ==
8) {
309 visual = vs;
310 break;
311 }
312 }
313 }
314 }
315 return visual;
316 }
317
318 #define IMG1_WIDTH 20
319 #define IMG1_X_OFFSET 20
320 #define IMG1_Y_OFFSET 10
321 #define IMG2_X_OFFSET 10
322 #define IMG2_Y_OFFSET 10
323
324 static void init_pix1(cgData *data, Widget w) {
325 Display *dp = XtDisplay(w);
326 Dimension width =
IMG1_WIDTH;
327 Dimension height = w->core.height -
IMG1_Y_OFFSET*
2;
328
329 if(data->image1) {
330 if(height == data->img2_height)
return;
331 XDestroyImage(data->image1);
332 }
333
334 float height6 = ((
float)height)/
6.f;
335 float step = ((
float)
255.f)/height6;
336
337 Visual *visual = get_visual(w->core.screen, w->core.depth);
338 if(!visual) {
339 return;
340 }
341
342
343 int red_shift = get_shift(visual->red_mask);
344 int green_shift = get_shift(visual->green_mask);
345 int blue_shift = get_shift(visual->blue_mask);
346
347 uint32_t *imgdata = malloc(width * height *
4);
348 uint32_t pixel_init =
UINT32_MAX ^ (visual->red_mask ^ visual->green_mask ^ visual->blue_mask);
349
350
351 int m =
0;
352 float r = 0xFF;
353 float g =
0;
354 float b =
0;
355 for(
int i=
0;i<height;i++) {
356 if(r <
0) r =
0;
357 if(r >
255) r =
255;
358 if(g <
0) g =
0;
359 if(g >
255) g =
255;
360 if(b <
0) b =
0;
361 if(b >
255) b =
255;
362
363 uint8_t red = r;
364 uint8_t green = g;
365 uint8_t blue = b;
366
367 switch(m) {
368 case 0: {
369 g += step;
370 if(g >=
255) m++;
371 break;
372 }
373 case 1: {
374 r -= step;
375 if(r <=
0) m++;
376 break;
377 }
378 case 2: {
379 b += step;
380 if(b >=
255) m++;
381 break;
382 }
383 case 3: {
384 g -= step;
385 if(g <=
0) m++;
386 break;
387 }
388 case 4: {
389 r += step;
390 if(r >=
255) m++;
391 break;
392 }
393 case 5: {
394 b -= step;
395 if(b <=
0) m =
0;
396 break;
397 }
398 }
399
400 uint32_t out = pixel_init;
401 out ^= (red << red_shift);
402 out ^= (green << green_shift);
403 out ^= (blue << blue_shift);
404
405 for(
int j=
0;j<width;j++) {
406 imgdata[i*width + j] = out;
407 }
408 }
409
410 data->image1 = XCreateImage(dp, visual, w->core.depth, ZPixmap,
0, (
char*)imgdata, width, height,
32,
0);
411
412 data->img1_width = width;
413 data->img1_height = height;
414 }
415
416 static void init_pix2(cgData *data, Widget w) {
417 Display *dp = XtDisplay(w);
418 Dimension width = w->core.width -
IMG1_X_OFFSET -
IMG1_WIDTH -
2*
IMG2_X_OFFSET;
419 Dimension height = w->core.height -
2*
IMG2_Y_OFFSET;
420
421 if(data->image2) {
422 if(width == data->img2_width && height == data->img2_height)
return;
423 XDestroyImage(data->image2);
424 data->has_selection =
0;
425 }
426
427 if(!data->has_selection) {
428 data->img2_select_x = (
float)width * data->value -
1;
429 data->img2_select_y = height - ((
float)height * data->saturation);
430 data->has_selection =
1;
431 }
432
433 Visual *visual = get_visual(w->core.screen, w->core.depth);
434 if(!visual) {
435 return;
436 }
437
438
439 int red_shift = get_shift(visual->red_mask);
440 int green_shift = get_shift(visual->green_mask);
441 int blue_shift = get_shift(visual->blue_mask);
442
443 uint32_t *imgdata = malloc(width * height *
4);
444 uint32_t pixel_init =
UINT32_MAX ^ (visual->red_mask ^ visual->green_mask ^ visual->blue_mask);
445
446 float y_base_r = data->base_red;
447 float y_base_g = data->base_green;
448 float y_base_b = data->base_blue;
449
450 float y_step_r = (
255-data->base_red) / (
float)(height-
2*
IMG2_Y_OFFSET);
451 float y_step_g = (
255-data->base_green) / (
float)(height-
2*
IMG2_Y_OFFSET);
452 float y_step_b = (
255-data->base_blue) / (
float)(height-
2*
IMG2_Y_OFFSET);
453
454 for(
int y=
0;y<height;y++) {
455 float x_step_r = y_base_r / (
float)width;
456 float x_step_g = y_base_g / (
float)width;
457 float x_step_b = y_base_b / (
float)width;
458
459 float r = y_base_r;
460 float g = y_base_g;
461 float b = y_base_b;
462
463 y_base_r += y_step_r;
464 y_base_g += y_step_g;
465 y_base_b += y_step_b;
466 if(y_base_r >=
255) y_base_r =
255;
467 if(y_base_g >=
255) y_base_g =
255;
468 if(y_base_b >=
255) y_base_b =
255;
469
470 for(
int x=width-
1;x>=
0;x--) {
471 if(r <
0) r =
0;
472 if(g <
0) g =
0;
473 if(b <
0) b =
0;
474
475 uint8_t red = r;
476 uint8_t green = g;
477 uint8_t blue = b;
478
479 r -= x_step_r;
480 g -= x_step_g;
481 b -= x_step_b;
482
483 uint32_t out = pixel_init;
484 out ^= (red << red_shift);
485 out ^= (green << green_shift);
486 out ^= (blue << blue_shift);
487 imgdata[y*width + x] = out;
488 }
489 }
490
491 data->image2 = XCreateImage(dp, visual, w->core.depth, ZPixmap,
0, (
char*)imgdata, width, height,
32,
0);
492
493 data->img2_width = width;
494 data->img2_height = height;
495 }
496
497 static void draw_img2(Display *dp, Window win, cgData *data) {
498 int img2_x =
IMG1_X_OFFSET + data->img1_width +
IMG2_X_OFFSET;
499 int img2_y =
IMG2_Y_OFFSET;
500 XPutImage(dp, win, data->gc, data->image2,
0,
0, img2_x, img2_y, data->img2_width, data->img2_height);
501 if(data->has_selection) {
502 XDrawLine(dp, win, data->selGC,
503 img2_x, img2_y + data->img2_select_y,
504 img2_x + data->img2_width -
1, img2_y + data->img2_select_y);
505 XDrawLine(dp, win, data->selGC,
506 img2_x + data->img2_select_x, img2_y,
507 img2_x + data->img2_select_x, img2_y + data->img2_height -
1);
508 }
509 }
510
511 #define HUE_INDICATOR_SIZE 9
512
513 static void selector_expose(Widget w, XtPointer u, XtPointer c) {
514 cgData *data = u;
515 Dimension width = w->core.width;
516 Dimension height = w->core.height;
517 Display *dp = XtDisplay(w);
518 Window win = XtWindow(w);
519
520 init_pix1(data, w);
521 init_pix2(data, w);
522
523 if(data->base_sel_y <
0) {
524 float img1h = data->img1_height;
525 float deg2pix = img1h /
360.f;
526 data->base_sel_y = data->hue * deg2pix;
527 }
528
529
530
531
532 XClearArea(XtDisplay(w), XtWindow(w),
1,
1, width-
2,
IMG1_Y_OFFSET-
1, False);
533
534 XClearArea(XtDisplay(w), XtWindow(w),
535 1,
IMG1_Y_OFFSET + data->img1_height,
536 width-
2, height -
IMG1_Y_OFFSET - data->img1_height -
2, False);
537
538 XClearArea(XtDisplay(w), XtWindow(w),
1,
1,
IMG1_X_OFFSET-
1, height-
2, False);
539
540 XPoint indicator[
4];
541 indicator[
0].x =
IMG1_X_OFFSET-
1;
542 indicator[
0].y =
IMG1_Y_OFFSET + data->base_sel_y;
543
544 indicator[
1].x =
IMG1_X_OFFSET-
HUE_INDICATOR_SIZE -
1;
545 indicator[
1].y =
IMG1_Y_OFFSET + data->base_sel_y -
HUE_INDICATOR_SIZE;
546
547 indicator[
2].x =
IMG1_X_OFFSET-
HUE_INDICATOR_SIZE -
1;
548 indicator[
2].y =
IMG1_Y_OFFSET + data->base_sel_y +
HUE_INDICATOR_SIZE;
549
550 indicator[
3].x =
IMG1_X_OFFSET-
1;
551 indicator[
3].y =
IMG1_Y_OFFSET + data->base_sel_y;
552
553
554 XFillPolygon(dp, win, data->gc, indicator,
4, Convex, CoordModeOrigin);
555
556
557 XClearArea(XtDisplay(w), XtWindow(w),
558 IMG1_X_OFFSET + data->img1_width +
IMG2_X_OFFSET + data->img2_width,
1,
559 width -
1 -
IMG1_X_OFFSET - data->img1_width -
IMG2_X_OFFSET - data->img2_width, height-
2, False);
560
561 XClearArea(XtDisplay(w), XtWindow(w),
562 IMG1_X_OFFSET + data->img1_width,
1,
563 IMG2_X_OFFSET, height-
2, False);
564
565
566 if(data->image1) {
567 XPutImage(dp, win, data->gc, data->image1,
0,
0,
IMG1_X_OFFSET,
IMG1_Y_OFFSET, data->img1_width, data->img1_height);
568 }
569 if(data->image2) {
570 draw_img2(dp, win, data);
571 }
572
573 XDrawRectangle(dp, win, DefaultGC(dp,
0),
0,
0, width-
1, height-
1);
574
575 }
576
577 #define SELECT_MARGIN 5
578 static int translate_img1(cgData *data,
int x,
int y,
int *trans_x,
int *trans_y) {
579 if(x <
IMG1_X_OFFSET -
HUE_INDICATOR_SIZE)
return 0;
580 if(y <
IMG1_Y_OFFSET)
return 0;
581
582 int tx = x -
IMG1_X_OFFSET;
583 int ty = y -
IMG1_Y_OFFSET;
584
585 if(tx < data->img1_width && ty < data->img1_height) {
586 *trans_x = tx;
587 *trans_y = ty;
588 return 1;
589 }
590 return 0;
591 }
592
593 static int translate_img2(cgData *data,
int x,
int y,
int *trans_x,
int *trans_y) {
594 if(x <
IMG1_X_OFFSET +
IMG2_X_OFFSET + data->img1_width -
SELECT_MARGIN)
return 0;
595 if(y <
IMG1_Y_OFFSET)
return 0;
596
597 int tx = x - (
IMG1_X_OFFSET +
IMG2_X_OFFSET + data->img1_width);
598 int ty = y -
IMG2_Y_OFFSET;
599
600 if(tx < data->img2_width +
SELECT_MARGIN && ty < data->img2_height +
SELECT_MARGIN) {
601 if(tx <
0) tx =
0;
602 if(ty <
0) ty =
0;
603 if(tx >= data->img2_width) tx = data->img2_width-
1;
604 if(ty >= data->img2_height) ty = data->img2_height-
1;
605 *trans_x = tx;
606 *trans_y = ty;
607 return 1;
608 }
609 return 0;
610 }
611
612 static void selector_input(Widget w, XtPointer u, XtPointer c) {
613 XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct*)c;
614 cgData *data = u;
615 Display *dp = XtDisplay(w);
616 Window win = XtWindow(w);
617
618 Dimension x, y;
619 if(cb->event->type == MotionNotify) {
620 x = cb->event->xmotion.x;
621 y = cb->event->xmotion.y;
622 }
else {
623 x = cb->event->xbutton.x;
624 y = cb->event->xbutton.y;
625 }
626
627 int tx, ty;
628 if(translate_img1(data, x, y, &tx, &ty)) {
629 uint32_t color = XGetPixel(data->image1, tx, ty);
630 int red_shift = get_shift(data->image1->red_mask);
631 int green_shift = get_shift(data->image1->green_mask);
632 int blue_shift = get_shift(data->image1->blue_mask);
633
634 data->base_red = (color & data->image1->red_mask) >> red_shift;
635 data->base_green = (color & data->image1->green_mask) >> green_shift;
636 data->base_blue = (color & data->image1->blue_mask) >> blue_shift;
637
638 data->base_sel_y = ty;
639
640 XDestroyImage(data->image2);
641 data->image2 =
NULL;
642 init_pix2(data, w);
643
644 data->has_selection =
0;
645 selector_expose(w, u,
NULL);
646 }
647 if(translate_img2(data, x, y, &tx, &ty)) {
648 uint32_t color = XGetPixel(data->image2, tx, ty);
649 int red_shift = get_shift(data->image2->red_mask);
650 int green_shift = get_shift(data->image2->green_mask);
651 int blue_shift = get_shift(data->image2->blue_mask);
652
653 data->img2_select_x = tx;
654 data->img2_select_y = ty;
655 data->has_selection =
1;
656
657 select_color(data,
658 (color & data->image2->red_mask) >> red_shift,
659 (color & data->image2->green_mask) >> green_shift,
660 (color & data->image2->blue_mask) >> blue_shift);
661
662 preview_color(data->preview, data,
NULL);
663
664 draw_img2(dp, win, data);
665 }
666 }
667
668 static float max3(
float a,
float b,
float c) {
669 if(a > b) {
670 return a > c ? a : c;
671 }
else {
672 return b > c ? b : c;
673 }
674 }
675
676 static float min3(
float a,
float b,
float c) {
677 if(a < b) {
678 return a < c ? a : c;
679 }
else {
680 return b < c ? b : c;
681 }
682 }
683
684 static void rgbToHsv(
int red,
int green,
int blue,
float *hue,
float *saturation,
float *value) {
685 float r = red;
686 float g = green;
687 float b = blue;
688 r /=
255;
689 g /=
255;
690 b /=
255;
691
692 float rgbMax = max3(r, g, b);
693 float rgbMin = min3(r, g, b);
694
695 float mmDiff = rgbMax - rgbMin;
696
697 float h;
698 if(rgbMax == rgbMin) {
699 h =
0;
700 }
else if(rgbMax == r) {
701 h =
60.f * (g-b)/mmDiff;
702 }
else if(rgbMax == g) {
703 h =
60.f * (
2.f + (b-r)/mmDiff);
704 }
else {
705 h =
60.f * (
4.f + (r-g)/mmDiff);
706 }
707 if(h <
0) h +=
360;
708
709 float s = rgbMax ==
0 ?
0 : mmDiff/rgbMax;
710 float v = rgbMax;
711
712 *hue = h;
713 *saturation = s;
714 *value = v;
715 }
716
717 static void hsvToRgb(
float hue,
float saturation,
float value,
int *red,
int *green,
int *blue) {
718 int hi = hue/
60;
719 float f = (hue/
60) - hi;
720 float p = value * (
1-saturation);
721 float q = value * (
1-saturation * f);
722 float t = value * (
1-saturation * (
1-f));
723
724 float r, g, b;
725 switch(hi) {
726 case 1: {
727 r = q;
728 g = value;
729 b = p;
730 break;
731 }
732 case 2: {
733 r = p;
734 g = value;
735 b = t;
736 break;
737 }
738 case 3: {
739 r = p;
740 g = q;
741 b = value;
742 break;
743 }
744 case 4: {
745 r = t;
746 g = p;
747 b = value;
748 break;
749 }
750 case 5: {
751 r = value;
752 g = p;
753 b = q;
754 break;
755 }
756 default: {
757 r = value;
758 g = t;
759 b = p;
760 }
761 }
762
763 *red = r *
255;
764 *green = g *
255;
765 *blue = b *
255;
766 }
767
768 static void set_base_color(cgData *data,
int r,
int g,
int b) {
769 float h, s, v;
770 rgbToHsv(r, g, b, &h, &s, &v);
771
772 int red, green, blue;
773 hsvToRgb(h,
1,
1, &red, &green, &blue);
774
775 data->base_red = red;
776 data->base_green = green;
777 data->base_blue = blue;
778 data->hue = h;
779 data->saturation = s;
780 data->value = v;
781 data->base_sel_y = -
1;
782 }
783
784 static void select_color(cgData *data,
int r,
int g,
int b) {
785 XftColor color;
786 color.pixel =
0;
787 color.color.alpha = 0xFFFF;
788 color.color.red = r *
257;
789 color.color.green = g *
257;
790 color.color.blue = b *
257;
791 data->selected_color = color;
792
793 char buf[
8];
794 snprintf(buf,
8,
"#%02x%02x%02x", r, g, b);
795 data->parse_textfield_input =
0;
796 XmTextFieldSetString(data->textfield, buf);
797 data->parse_textfield_input =
1;
798 }
799
800 static void preview_color(Widget w, XtPointer u, XtPointer c) {
801 cgData *data = u;
802 if(!data->d) {
803 data->d = create_xft_draw(w);
804 }
805
806 XftDrawRect(data->d, &data->selected_color,
0,
0, w->core.width, w->core.height);
807 XDrawRectangle(XtDisplay(w), XtWindow(w), data->gc,
0,
0, w->core.width-
1, w->core.height-
1);
808 }
809
810 static void textfield_changed(Widget w, XtPointer u, XtPointer c) {
811 cgData *data = u;
812 if(!data->parse_textfield_input)
return;
813
814 char *colorStr = XmTextFieldGetString(w);
815 if(strlen(colorStr) !=
7) {
816 XtFree(colorStr);
817 return;
818 }
819
820 Display *dp = XtDisplay(w);
821 Colormap cmap = w->core.colormap;
822 XColor color;
823 if(XParseColor(dp, cmap, colorStr, &color)) {
824 XftColor c;
825 c.pixel =
0;
826 c.color.alpha = 0xFFFF;
827 c.color.red = color.red;
828 c.color.green = color.green;
829 c.color.blue = color.blue;
830 data->selected_color = c;
831
832 set_base_color(data, color.red/
257, color.green/
257, color.blue/
257);
833
834 XDestroyImage(data->image2);
835 data->image2 =
NULL;
836 init_pix2(data, w);
837
838 data->has_selection =
0;
839
840 selector_expose(data->selector, data,
NULL);
841 preview_color(data->preview, data,
NULL);
842 }
843 XtFree(colorStr);
844 }
845
846 static void okCB(Widget w, XtPointer u, XtPointer c) {
847 cgData *data = u;
848 data->status =
1;
849 data->end =
1;
850 }
851
852 static void cancelCB(Widget w, XtPointer u, XtPointer c) {
853 cgData *data = u;
854 data->status =
0;
855 data->end =
1;
856 }
857