1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <unistd.h>
5 #include <math.h>
6
7 #include <X11/X.h>
8 #include <X11/Intrinsic.h>
9 #include <Xm/Xm.h>
10 #include <Xm/Form.h>
11 #include <Xm/Label.h>
12 #include <Xm/DrawingA.h>
13 #include <Xm/RowColumn.h>
14 #include <Xm/PushB.h>
15 #include <Xm/ToggleB.h>
16 #include <Xm/Scale.h>
17 #include <Xm/Separator.h>
18 #include <Xm/Text.h>
19 #include <Xm/SelectioB.h>
20
21 #include "RegEdit.h"
22 #include "ida.h"
23 #include "x11.h"
24 #include "readers.h"
25 #include "viewer.h"
26 #include "color.h"
27 #include "lut.h"
28
29 /* ---------------------------------------------------------------------- */
30
31 #define HIST_SIZE 60
32
33 struct ida_coledit;
34
35 struct ida_hist {
36 /* x11 */
37 GC gc;
38 unsigned long color;
39
40 /* histogram */
41 Widget hist;
42 unsigned int max;
43 unsigned int data[256];
44
45 /* mapping */
46 Widget map;
47 struct op_map_parm_ch parm;
48
49 struct ida_coledit *up;
50 };
51
52 struct ida_coledit {
53 /* misc */
54 Widget dlg,form,vals,toggle;
55 Widget l,r,t,b,g;
56 int lock,apply;
57
58 /* histogram data */
59 struct ida_hist red;
60 struct ida_hist green;
61 struct ida_hist blue;
62 struct ida_hist *cur;
63 };
64
65 /* ---------------------------------------------------------------------- */
66
67 static void
color_calchist(struct ida_image * img,struct ida_coledit * me)68 color_calchist(struct ida_image *img, struct ida_coledit *me)
69 {
70 unsigned char *pix;
71 unsigned int i,x,y,max;
72
73 pix = (void*)pixman_image_get_data(img->p);
74 for (y = 0; y < img->i.height; y++) {
75 for (x = 0; x < img->i.width; x++) {
76 me->red.data[pix[0]]++;
77 me->green.data[pix[1]]++;
78 me->blue.data[pix[2]]++;
79 pix += 3;
80 }
81 }
82 max = 0;
83 for (i = 0; i < 256; i++) {
84 if (max < me->red.data[i])
85 max = me->red.data[i];
86 if (max < me->green.data[i])
87 max = me->green.data[i];
88 if (max < me->blue.data[i])
89 max = me->blue.data[i];
90 }
91 me->red.max = max;
92 me->green.max = max;
93 me->blue.max = max;
94 }
95
96 static void
color_update(struct ida_coledit * me,struct ida_hist * h,int text)97 color_update(struct ida_coledit *me, struct ida_hist *h, int text)
98 {
99 struct op_map_parm param;
100 char tmp[32];
101
102 if (me->lock) {
103 if (&me->red != h)
104 me->red.parm = h->parm;
105 if (&me->green != h)
106 me->green.parm = h->parm;
107 if (&me->blue != h)
108 me->blue.parm = h->parm;
109 XClearArea(XtDisplay(me->red.hist), XtWindow(me->red.hist),
110 0,0,0,0, True);
111 XClearArea(XtDisplay(me->red.map), XtWindow(me->red.map),
112 0,0,0,0, True);
113 XClearArea(XtDisplay(me->green.hist), XtWindow(me->green.hist),
114 0,0,0,0, True);
115 XClearArea(XtDisplay(me->green.map), XtWindow(me->green.map),
116 0,0,0,0, True);
117 XClearArea(XtDisplay(me->blue.hist), XtWindow(me->blue.hist),
118 0,0,0,0, True);
119 XClearArea(XtDisplay(me->blue.map), XtWindow(me->blue.map),
120 0,0,0,0, True);
121 } else {
122 XClearArea(XtDisplay(h->hist), XtWindow(h->hist),
123 0,0,0,0, True);
124 XClearArea(XtDisplay(h->map), XtWindow(h->map),
125 0,0,0,0, True);
126 }
127 if ((me->lock || h == me->cur) && text >= 1) {
128 /* mouse-click updateable values */
129 sprintf(tmp,"%d",h->parm.left);
130 XmTextSetString(me->l,tmp);
131 sprintf(tmp,"%d",h->parm.right);
132 XmTextSetString(me->r,tmp);
133 }
134 if ((me->lock || h == me->cur) && text >= 2) {
135 /* others */
136 sprintf(tmp,"%d",h->parm.bottom);
137 XmTextSetString(me->b,tmp);
138 sprintf(tmp,"%d",h->parm.top);
139 XmTextSetString(me->t,tmp);
140 sprintf(tmp,"%.2f",h->parm.gamma);
141 XmTextSetString(me->g,tmp);
142 }
143
144 param.red = me->red.parm;
145 param.green = me->green.parm;
146 param.blue = me->blue.parm;
147 viewer_start_preview(ida,&desc_map,¶m);
148 }
149
150 static void
color_drawmap(Widget widget,XtPointer client_data,XtPointer calldata)151 color_drawmap(Widget widget, XtPointer client_data, XtPointer calldata)
152 {
153 struct ida_hist *me = client_data;
154 XmDrawingAreaCallbackStruct *cb = calldata;
155 XGCValues values;
156 int left,right,top,bottom,i,val,x1,y1,x2,y2;
157 float p;
158
159 if (cb->reason == XmCR_EXPOSE) {
160 /* window needs redraw */
161 XExposeEvent *e = (XExposeEvent*)cb->event;
162 if (e->count)
163 return;
164 values.foreground = x11_gray;
165 XChangeGC(dpy,me->gc,GCForeground,&values);
166 left = me->parm.left * HIST_SIZE / 255;
167 right = me->parm.right * HIST_SIZE / 255;
168 bottom = me->parm.bottom * HIST_SIZE / 255;
169 top = me->parm.top * HIST_SIZE / 255;
170 if (me->parm.left > 0)
171 XFillRectangle(dpy,XtWindow(me->map),me->gc,
172 0,0,left,HIST_SIZE);
173 if (me->parm.right < 255)
174 XFillRectangle(dpy,XtWindow(me->map),me->gc,
175 right,0,HIST_SIZE-right,HIST_SIZE);
176 values.foreground = me->color;
177 XChangeGC(dpy,me->gc,GCForeground,&values);
178 if (me->parm.left > 0)
179 XDrawLine(dpy,XtWindow(me->map),me->gc,
180 0,HIST_SIZE-bottom,left,HIST_SIZE-bottom);
181 if (me->parm.right < 255)
182 XDrawLine(dpy,XtWindow(me->map),me->gc,
183 right,HIST_SIZE-top,HIST_SIZE,HIST_SIZE-top);
184 p = 1/me->parm.gamma;
185 x2 = y2 = 0;
186 for (i = left; i <= right; i++) {
187 val = pow((float)(i-left)/(right-left),p) * (top-bottom) + 0.5;
188 val += bottom;
189 if (val < 0) val = 0;
190 if (val > HIST_SIZE) val = HIST_SIZE;
191 x1 = x2;
192 y1 = y2;
193 x2 = i;
194 y2 = HIST_SIZE-val;
195 if (i > left)
196 XDrawLine(dpy,XtWindow(me->map),me->gc,
197 x1,y1,x2,y2);
198 }
199 }
200 }
201
202 static void
color_drawhist(Widget widget,XtPointer client_data,XtPointer calldata)203 color_drawhist(Widget widget, XtPointer client_data, XtPointer calldata)
204 {
205 struct ida_hist *me = client_data;
206 XmDrawingAreaCallbackStruct *cb = calldata;
207 XGCValues values;
208 int i,val;
209
210 if (cb->reason == XmCR_EXPOSE) {
211 /* window needs redraw */
212 XExposeEvent *e = (XExposeEvent*)cb->event;
213 if (e->count)
214 return;
215 values.foreground = x11_gray;
216 XChangeGC(dpy,me->gc,GCForeground,&values);
217 if (me->parm.left > 0)
218 XFillRectangle(dpy,XtWindow(me->hist),me->gc,
219 0,0,me->parm.left,HIST_SIZE);
220 if (me->parm.right < 255)
221 XFillRectangle(dpy,XtWindow(me->hist),me->gc,
222 me->parm.right,0,256-me->parm.right,HIST_SIZE);
223 values.foreground = me->color;
224 XChangeGC(dpy,me->gc,GCForeground,&values);
225 for (i = 0; i < 256; i++) {
226 val = log(me->data[i])*HIST_SIZE/log(me->max);
227 XDrawLine(dpy,XtWindow(me->hist),me->gc,
228 i,HIST_SIZE,i,HIST_SIZE-val);
229 }
230 }
231 }
232
233 static void
color_mouse(Widget widget,XtPointer client_data,XEvent * ev,Boolean * cont)234 color_mouse(Widget widget, XtPointer client_data,
235 XEvent *ev, Boolean *cont)
236 {
237 struct ida_hist *me = client_data;
238 int x;
239
240 switch (ev->type) {
241 case ButtonPress:
242 case ButtonRelease:
243 {
244 XButtonEvent *e = (XButtonEvent*)ev;
245
246 x = e->x;
247 break;
248 }
249 case MotionNotify:
250 {
251 XMotionEvent *e = (XMotionEvent*)ev;
252
253 x = e->x;
254 break;
255 default:
256 return;
257 }
258 }
259 if (x > (me->parm.right + me->parm.left)/2) {
260 me->parm.right = x;
261 if (me->parm.right > 255)
262 me->parm.right = 255;
263 if (me->parm.right < me->parm.left)
264 me->parm.right = me->parm.left;
265 } else {
266 me->parm.left = x;
267 if (me->parm.left < 0)
268 me->parm.left = 0;
269 if (me->parm.left > me->parm.right)
270 me->parm.left = me->parm.right;
271 }
272 color_update(me->up,me,1);
273 }
274
275 static void
color_lock(Widget widget,XtPointer client_data,XtPointer calldata)276 color_lock(Widget widget, XtPointer client_data, XtPointer calldata)
277 {
278 struct ida_coledit *me = client_data;
279 XmToggleButtonCallbackStruct *cb = calldata;
280 Widget label,button;
281
282 label = XmOptionLabelGadget(me->vals);
283 button = XmOptionButtonGadget(me->vals);
284 me->lock = cb->set;
285 XtVaSetValues(label,XtNsensitive,!me->lock,NULL);
286 XtVaSetValues(button,XtNsensitive,!me->lock,NULL);
287 }
288
289 static void
color_vals(Widget widget,XtPointer client_data,XtPointer calldata)290 color_vals(Widget widget, XtPointer client_data, XtPointer calldata)
291 {
292 struct ida_hist *cur = client_data;
293 struct ida_coledit *me = cur->up;
294
295 me->cur = cur;
296 color_update(me,cur,2);
297 }
298
299 static void
color_text(Widget widget,XtPointer client_data,XtPointer calldata)300 color_text(Widget widget, XtPointer client_data, XtPointer calldata)
301 {
302 struct ida_coledit *me = client_data;
303 int left,right,bottom,top;
304 float gamma;
305
306 if (widget == me->l &&
307 1 == sscanf(XmTextGetString(me->l),"%d",&left) &&
308 left >= 0 && left <= me->cur->parm.right) {
309 me->cur->parm.left = left;
310 }
311 if (widget == me->r &&
312 1 == sscanf(XmTextGetString(me->r),"%d",&right) &&
313 me->cur->parm.left <= right && right <= 255) {
314 me->cur->parm.right = right;
315 }
316 if (widget == me->b &&
317 1 == sscanf(XmTextGetString(me->b),"%d",&bottom) &&
318 bottom <= me->cur->parm.top) {
319 me->cur->parm.bottom = bottom;
320 }
321 if (widget == me->t &&
322 1 == sscanf(XmTextGetString(me->t),"%d",&top) &&
323 me->cur->parm.bottom <= top) {
324 me->cur->parm.top = top;
325 }
326 if (widget == me->g &&
327 1 == sscanf(XmTextGetString(me->g),"%f",&gamma)) {
328 me->cur->parm.gamma = gamma;
329 }
330 color_update(me,me->cur,0);
331 }
332
333 static void
color_pick_ok(int x,int y,unsigned char * pix,XtPointer data)334 color_pick_ok(int x, int y, unsigned char *pix, XtPointer data)
335 {
336 struct ida_coledit *me = data;
337 int max;
338
339 if (debug)
340 fprintf(stderr,"color_pick_ok: +%d+%d %d/%d/%d\n",
341 x,y, pix[0],pix[1],pix[2]);
342
343 max = 0;
344 if (max < pix[0])
345 max = pix[0];
346 if (max < pix[1])
347 max = pix[1];
348 if (max < pix[2])
349 max = pix[2];
350
351 XmToggleButtonSetState(me->toggle,False,True);
352 me->red.parm.right = (int)255 * pix[0] / max;
353 color_update(me,&me->red,1);
354 me->green.parm.right = (int)255 * pix[1] / max;
355 color_update(me,&me->green,1);
356 me->blue.parm.right = (int)255 * pix[2] / max;
357 color_update(me,&me->blue,1);
358
359 if (debug)
360 fprintf(stderr,"color_pick_ok: %d/%d/%d max=%d\n",
361 me->red.parm.right,
362 me->green.parm.right,
363 me->blue.parm.right,
364 max);
365 }
366
367 static void
color_pick(Widget widget,XtPointer client_data,XtPointer calldata)368 color_pick(Widget widget, XtPointer client_data, XtPointer calldata)
369 {
370 struct ida_coledit *me = client_data;
371 viewer_pick(ida,color_pick_ok,me);
372 }
373
374 static void
color_createhist(Widget parent,char * name,unsigned long color,struct ida_hist * me)375 color_createhist(Widget parent, char *name, unsigned long color,
376 struct ida_hist *me)
377 {
378 char tmp[32];
379
380 sprintf(tmp,"h%s",name);
381 me->hist = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent,
382 XtNwidth,256,
383 XtNheight,HIST_SIZE,
384 NULL);
385 sprintf(tmp,"m%s",name);
386 me->map = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent,
387 XtNwidth,HIST_SIZE,
388 XtNheight,HIST_SIZE,
389 NULL);
390 XtAddEventHandler(me->hist,
391 ButtonPressMask |
392 ButtonReleaseMask |
393 ButtonMotionMask,
394 False,color_mouse,me);
395 XtAddCallback(me->hist,XmNexposeCallback,color_drawhist,me);
396 XtAddCallback(me->map,XmNexposeCallback,color_drawmap,me);
397 me->gc = XCreateGC(dpy,XtWindow(app_shell),0,NULL);
398 me->color = color;
399 me->parm = op_map_nothing;
400 }
401
402 static void
color_button_cb(Widget widget,XtPointer client_data,XtPointer calldata)403 color_button_cb(Widget widget, XtPointer client_data, XtPointer calldata)
404 {
405 struct ida_coledit *me = client_data;
406 XmSelectionBoxCallbackStruct *cb = calldata;
407
408 if (cb->reason == XmCR_OK)
409 me->apply = 1;
410 XtDestroyWidget(XtParent(me->form));
411 }
412
413 static void
color_destroy(Widget widget,XtPointer client_data,XtPointer calldata)414 color_destroy(Widget widget, XtPointer client_data, XtPointer calldata)
415 {
416 struct ida_coledit *me = client_data;
417 struct op_map_parm param;
418
419 if (me->apply) {
420 param.red = me->red.parm;
421 param.green = me->green.parm;
422 param.blue = me->blue.parm;
423 viewer_start_op(ida,&desc_map,¶m);
424 } else
425 viewer_cancel_preview(ida);
426 viewer_unpick(ida);
427 free(me);
428 }
429
430 void
color_init(struct ida_image * img)431 color_init(struct ida_image *img)
432 {
433 Widget menu,push,rc;
434 struct ida_coledit *me;
435 Arg args[2];
436
437 me = malloc(sizeof(*me));
438 memset(me,0,sizeof(*me));
439 color_calchist(img,me);
440
441 /* dialog shell */
442 me->dlg = XmCreatePromptDialog(app_shell,"color",NULL,0);
443 XmdRegisterEditres(XtParent(me->dlg));
444 XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_SELECTION_LABEL));
445 XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_HELP_BUTTON));
446 XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_TEXT));
447 me->form = XtVaCreateManagedWidget("form",xmFormWidgetClass,
448 me->dlg,NULL);
449 XtAddCallback(XtParent(me->dlg),XmNdestroyCallback,color_destroy,me);
450 XtAddCallback(me->dlg,XmNokCallback,color_button_cb,me);
451 XtAddCallback(me->dlg,XmNcancelCallback,color_button_cb,me);
452
453 /* histograms */
454 XtVaCreateManagedWidget("hist",xmLabelWidgetClass,
455 me->form,NULL);
456 color_createhist(me->form,"red", x11_red, &me->red);
457 color_createhist(me->form,"green",x11_green,&me->green);
458 color_createhist(me->form,"blue", x11_blue, &me->blue);
459 me->red.up = me;
460 me->green.up = me;
461 me->blue.up = me;
462 XtVaCreateManagedWidget("map",xmLabelWidgetClass,
463 me->form,NULL);
464
465 /* control */
466 me->toggle = XtVaCreateManagedWidget("lock",xmToggleButtonWidgetClass,
467 me->form,NULL);
468 XtAddCallback(me->toggle,XmNvalueChangedCallback,color_lock,me);
469 menu = XmCreatePulldownMenu(me->form,"valsM",NULL,0);
470 XtSetArg(args[0],XmNsubMenuId,menu);
471 me->vals = XmCreateOptionMenu(me->form,"vals",args,1);
472 XtManageChild(me->vals);
473 push = XtVaCreateManagedWidget("red",xmPushButtonWidgetClass,menu,NULL);
474 XtAddCallback(push,XmNactivateCallback,color_vals,&me->red);
475 push = XtVaCreateManagedWidget("green",xmPushButtonWidgetClass,menu,NULL);
476 XtAddCallback(push,XmNactivateCallback,color_vals,&me->green);
477 push = XtVaCreateManagedWidget("blue",xmPushButtonWidgetClass,menu,NULL);
478 XtAddCallback(push,XmNactivateCallback,color_vals,&me->blue);
479
480 /* in range */
481 rc = XtVaCreateManagedWidget("in",xmRowColumnWidgetClass,me->form,NULL);
482 XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
483 me->l = XtVaCreateManagedWidget("left",xmTextWidgetClass,rc,NULL);
484 XtAddCallback(me->l,XmNvalueChangedCallback,color_text,me);
485 me->r = XtVaCreateManagedWidget("right",xmTextWidgetClass,rc,NULL);
486 XtAddCallback(me->r,XmNvalueChangedCallback,color_text,me);
487
488 /* out range */
489 rc = XtVaCreateManagedWidget("out",xmRowColumnWidgetClass,me->form,NULL);
490 XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
491 me->b = XtVaCreateManagedWidget("bottom",xmTextWidgetClass,rc,NULL);
492 XtAddCallback(me->b,XmNvalueChangedCallback,color_text,me);
493 me->t = XtVaCreateManagedWidget("top",xmTextWidgetClass,rc,NULL);
494 XtAddCallback(me->t,XmNvalueChangedCallback,color_text,me);
495
496 /* gamma */
497 rc = XtVaCreateManagedWidget("gamma",xmRowColumnWidgetClass,me->form,NULL);
498 XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
499 me->g = XtVaCreateManagedWidget("gamma",xmTextWidgetClass,rc,NULL);
500 XtAddCallback(me->g,XmNvalueChangedCallback,color_text,me);
501
502 /* testing stuff */
503 rc = XtVaCreateManagedWidget("pick",xmRowColumnWidgetClass,me->form,NULL);
504 push = XtVaCreateManagedWidget("white",xmPushButtonWidgetClass,rc,NULL);
505 XtAddCallback(push,XmNactivateCallback,color_pick,me);
506
507 XtManageChild(me->dlg);
508
509 me->cur = &me->red;
510 color_update(me,me->cur,2);
511 XmToggleButtonSetState(me->toggle,True,True);
512 }
513