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,&param);
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,&param);
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