aboutsummaryrefslogtreecommitdiffstats
path: root/color.c
diff options
context:
space:
mode:
Diffstat (limited to 'color.c')
-rw-r--r--color.c513
1 files changed, 513 insertions, 0 deletions
diff --git a/color.c b/color.c
new file mode 100644
index 0000000..51d32ea
--- /dev/null
+++ b/color.c
@@ -0,0 +1,513 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <X11/X.h>
+#include <X11/Intrinsic.h>
+#include <Xm/Xm.h>
+#include <Xm/Form.h>
+#include <Xm/Label.h>
+#include <Xm/DrawingA.h>
+#include <Xm/RowColumn.h>
+#include <Xm/PushB.h>
+#include <Xm/ToggleB.h>
+#include <Xm/Scale.h>
+#include <Xm/Separator.h>
+#include <Xm/Text.h>
+#include <Xm/SelectioB.h>
+
+#include "RegEdit.h"
+#include "ida.h"
+#include "x11.h"
+#include "readers.h"
+#include "viewer.h"
+#include "color.h"
+#include "lut.h"
+
+/* ---------------------------------------------------------------------- */
+
+#define HIST_SIZE 60
+
+struct ida_coledit;
+
+struct ida_hist {
+ /* x11 */
+ GC gc;
+ unsigned long color;
+
+ /* histogram */
+ Widget hist;
+ unsigned int max;
+ unsigned int data[256];
+
+ /* mapping */
+ Widget map;
+ struct op_map_parm_ch parm;
+
+ struct ida_coledit *up;
+};
+
+struct ida_coledit {
+ /* misc */
+ Widget dlg,form,vals,toggle;
+ Widget l,r,t,b,g;
+ int lock,apply;
+
+ /* histogram data */
+ struct ida_hist red;
+ struct ida_hist green;
+ struct ida_hist blue;
+ struct ida_hist *cur;
+};
+
+/* ---------------------------------------------------------------------- */
+
+static void
+color_calchist(struct ida_image *img, struct ida_coledit *me)
+{
+ unsigned char *pix;
+ unsigned int i,x,y,max;
+
+ pix = img->data;
+ for (y = 0; y < img->i.height; y++) {
+ for (x = 0; x < img->i.width; x++) {
+ me->red.data[pix[0]]++;
+ me->green.data[pix[1]]++;
+ me->blue.data[pix[2]]++;
+ pix += 3;
+ }
+ }
+ max = 0;
+ for (i = 0; i < 256; i++) {
+ if (max < me->red.data[i])
+ max = me->red.data[i];
+ if (max < me->green.data[i])
+ max = me->green.data[i];
+ if (max < me->blue.data[i])
+ max = me->blue.data[i];
+ }
+ me->red.max = max;
+ me->green.max = max;
+ me->blue.max = max;
+}
+
+static void
+color_update(struct ida_coledit *me, struct ida_hist *h, int text)
+{
+ struct op_map_parm param;
+ char tmp[32];
+
+ if (me->lock) {
+ if (&me->red != h)
+ me->red.parm = h->parm;
+ if (&me->green != h)
+ me->green.parm = h->parm;
+ if (&me->blue != h)
+ me->blue.parm = h->parm;
+ XClearArea(XtDisplay(me->red.hist), XtWindow(me->red.hist),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(me->red.map), XtWindow(me->red.map),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(me->green.hist), XtWindow(me->green.hist),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(me->green.map), XtWindow(me->green.map),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(me->blue.hist), XtWindow(me->blue.hist),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(me->blue.map), XtWindow(me->blue.map),
+ 0,0,0,0, True);
+ } else {
+ XClearArea(XtDisplay(h->hist), XtWindow(h->hist),
+ 0,0,0,0, True);
+ XClearArea(XtDisplay(h->map), XtWindow(h->map),
+ 0,0,0,0, True);
+ }
+ if ((me->lock || h == me->cur) && text >= 1) {
+ /* mouse-click updateable values */
+ sprintf(tmp,"%d",h->parm.left);
+ XmTextSetString(me->l,tmp);
+ sprintf(tmp,"%d",h->parm.right);
+ XmTextSetString(me->r,tmp);
+ }
+ if ((me->lock || h == me->cur) && text >= 2) {
+ /* others */
+ sprintf(tmp,"%d",h->parm.bottom);
+ XmTextSetString(me->b,tmp);
+ sprintf(tmp,"%d",h->parm.top);
+ XmTextSetString(me->t,tmp);
+ sprintf(tmp,"%.2f",h->parm.gamma);
+ XmTextSetString(me->g,tmp);
+ }
+
+ param.red = me->red.parm;
+ param.green = me->green.parm;
+ param.blue = me->blue.parm;
+ viewer_start_preview(ida,&desc_map,&param);
+}
+
+static void
+color_drawmap(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_hist *me = client_data;
+ XmDrawingAreaCallbackStruct *cb = calldata;
+ XGCValues values;
+ int left,right,top,bottom,i,val,x1,y1,x2,y2;
+ float p;
+
+ if (cb->reason == XmCR_EXPOSE) {
+ /* window needs redraw */
+ XExposeEvent *e = (XExposeEvent*)cb->event;
+ if (e->count)
+ return;
+ values.foreground = x11_gray;
+ XChangeGC(dpy,me->gc,GCForeground,&values);
+ left = me->parm.left * HIST_SIZE / 255;
+ right = me->parm.right * HIST_SIZE / 255;
+ bottom = me->parm.bottom * HIST_SIZE / 255;
+ top = me->parm.top * HIST_SIZE / 255;
+ if (me->parm.left > 0)
+ XFillRectangle(dpy,XtWindow(me->map),me->gc,
+ 0,0,left,HIST_SIZE);
+ if (me->parm.right < 255)
+ XFillRectangle(dpy,XtWindow(me->map),me->gc,
+ right,0,HIST_SIZE-right,HIST_SIZE);
+ values.foreground = me->color;
+ XChangeGC(dpy,me->gc,GCForeground,&values);
+ if (me->parm.left > 0)
+ XDrawLine(dpy,XtWindow(me->map),me->gc,
+ 0,HIST_SIZE-bottom,left,HIST_SIZE-bottom);
+ if (me->parm.right < 255)
+ XDrawLine(dpy,XtWindow(me->map),me->gc,
+ right,HIST_SIZE-top,HIST_SIZE,HIST_SIZE-top);
+ p = 1/me->parm.gamma;
+ x2 = y2 = 0;
+ for (i = left; i <= right; i++) {
+ val = pow((float)(i-left)/(right-left),p) * (top-bottom) + 0.5;
+ val += bottom;
+ if (val < 0) val = 0;
+ if (val > HIST_SIZE) val = HIST_SIZE;
+ x1 = x2;
+ y1 = y2;
+ x2 = i;
+ y2 = HIST_SIZE-val;
+ if (i > left)
+ XDrawLine(dpy,XtWindow(me->map),me->gc,
+ x1,y1,x2,y2);
+ }
+ }
+}
+
+static void
+color_drawhist(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_hist *me = client_data;
+ XmDrawingAreaCallbackStruct *cb = calldata;
+ XGCValues values;
+ int i,val;
+
+ if (cb->reason == XmCR_EXPOSE) {
+ /* window needs redraw */
+ XExposeEvent *e = (XExposeEvent*)cb->event;
+ if (e->count)
+ return;
+ values.foreground = x11_gray;
+ XChangeGC(dpy,me->gc,GCForeground,&values);
+ if (me->parm.left > 0)
+ XFillRectangle(dpy,XtWindow(me->hist),me->gc,
+ 0,0,me->parm.left,HIST_SIZE);
+ if (me->parm.right < 255)
+ XFillRectangle(dpy,XtWindow(me->hist),me->gc,
+ me->parm.right,0,256-me->parm.right,HIST_SIZE);
+ values.foreground = me->color;
+ XChangeGC(dpy,me->gc,GCForeground,&values);
+ for (i = 0; i < 256; i++) {
+ val = log(me->data[i])*HIST_SIZE/log(me->max);
+ XDrawLine(dpy,XtWindow(me->hist),me->gc,
+ i,HIST_SIZE,i,HIST_SIZE-val);
+ }
+ }
+}
+
+static void
+color_mouse(Widget widget, XtPointer client_data,
+ XEvent *ev, Boolean *cont)
+{
+ struct ida_hist *me = client_data;
+ int x,y;
+
+ switch (ev->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ XButtonEvent *e = (XButtonEvent*)ev;
+
+ x = e->x;
+ y = e->y;
+ break;
+ }
+ case MotionNotify:
+ {
+ XMotionEvent *e = (XMotionEvent*)ev;
+
+ x = e->x;
+ y = e->y;
+ break;
+ default:
+ return;
+ }
+ }
+ if (x > (me->parm.right + me->parm.left)/2) {
+ me->parm.right = x;
+ if (me->parm.right > 255)
+ me->parm.right = 255;
+ if (me->parm.right < me->parm.left)
+ me->parm.right = me->parm.left;
+ } else {
+ me->parm.left = x;
+ if (me->parm.left < 0)
+ me->parm.left = 0;
+ if (me->parm.left > me->parm.right)
+ me->parm.left = me->parm.right;
+ }
+ color_update(me->up,me,1);
+}
+
+static void
+color_lock(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_coledit *me = client_data;
+ XmToggleButtonCallbackStruct *cb = calldata;
+ Widget label,button;
+
+ label = XmOptionLabelGadget(me->vals);
+ button = XmOptionButtonGadget(me->vals);
+ me->lock = cb->set;
+ XtVaSetValues(label,XtNsensitive,!me->lock,NULL);
+ XtVaSetValues(button,XtNsensitive,!me->lock,NULL);
+}
+
+static void
+color_vals(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_hist *cur = client_data;
+ struct ida_coledit *me = cur->up;
+
+ me->cur = cur;
+ color_update(me,cur,2);
+}
+
+static void
+color_text(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_coledit *me = client_data;
+ int left,right,bottom,top;
+ float gamma;
+
+ if (widget == me->l &&
+ 1 == sscanf(XmTextGetString(me->l),"%d",&left) &&
+ left >= 0 && left <= me->cur->parm.right) {
+ me->cur->parm.left = left;
+ }
+ if (widget == me->r &&
+ 1 == sscanf(XmTextGetString(me->r),"%d",&right) &&
+ me->cur->parm.left <= right && right <= 255) {
+ me->cur->parm.right = right;
+ }
+ if (widget == me->b &&
+ 1 == sscanf(XmTextGetString(me->b),"%d",&bottom) &&
+ bottom <= me->cur->parm.top) {
+ me->cur->parm.bottom = bottom;
+ }
+ if (widget == me->t &&
+ 1 == sscanf(XmTextGetString(me->t),"%d",&top) &&
+ me->cur->parm.bottom <= top) {
+ me->cur->parm.top = top;
+ }
+ if (widget == me->g &&
+ 1 == sscanf(XmTextGetString(me->g),"%f",&gamma)) {
+ me->cur->parm.gamma = gamma;
+ }
+ color_update(me,me->cur,0);
+}
+
+static void
+color_pick_ok(int x, int y, unsigned char *pix, XtPointer data)
+{
+ struct ida_coledit *me = data;
+ int max;
+
+ if (debug)
+ fprintf(stderr,"color_pick_ok: +%d+%d %d/%d/%d\n",
+ x,y, pix[0],pix[1],pix[2]);
+
+ max = 0;
+ if (max < pix[0])
+ max = pix[0];
+ if (max < pix[1])
+ max = pix[1];
+ if (max < pix[2])
+ max = pix[2];
+
+ XmToggleButtonSetState(me->toggle,False,True);
+ me->red.parm.right = (int)255 * pix[0] / max;
+ color_update(me,&me->red,1);
+ me->green.parm.right = (int)255 * pix[1] / max;
+ color_update(me,&me->green,1);
+ me->blue.parm.right = (int)255 * pix[2] / max;
+ color_update(me,&me->blue,1);
+
+ if (debug)
+ fprintf(stderr,"color_pick_ok: %d/%d/%d max=%d\n",
+ me->red.parm.right,
+ me->green.parm.right,
+ me->blue.parm.right,
+ max);
+}
+
+static void
+color_pick(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_coledit *me = client_data;
+ viewer_pick(ida,color_pick_ok,me);
+}
+
+static void
+color_createhist(Widget parent, char *name, unsigned long color,
+ struct ida_hist *me)
+{
+ char tmp[32];
+
+ sprintf(tmp,"h%s",name);
+ me->hist = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent,
+ XtNwidth,256,
+ XtNheight,HIST_SIZE,
+ NULL);
+ sprintf(tmp,"m%s",name);
+ me->map = XtVaCreateManagedWidget(tmp,xmDrawingAreaWidgetClass,parent,
+ XtNwidth,HIST_SIZE,
+ XtNheight,HIST_SIZE,
+ NULL);
+ XtAddEventHandler(me->hist,
+ ButtonPressMask |
+ ButtonReleaseMask |
+ ButtonMotionMask,
+ False,color_mouse,me);
+ XtAddCallback(me->hist,XmNexposeCallback,color_drawhist,me);
+ XtAddCallback(me->map,XmNexposeCallback,color_drawmap,me);
+ me->gc = XCreateGC(dpy,XtWindow(app_shell),0,NULL);
+ me->color = color;
+ me->parm = op_map_nothing;
+}
+
+static void
+color_button_cb(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_coledit *me = client_data;
+ XmSelectionBoxCallbackStruct *cb = calldata;
+
+ if (cb->reason == XmCR_OK)
+ me->apply = 1;
+ XtDestroyWidget(XtParent(me->form));
+}
+
+static void
+color_destroy(Widget widget, XtPointer client_data, XtPointer calldata)
+{
+ struct ida_coledit *me = client_data;
+ struct op_map_parm param;
+
+ if (me->apply) {
+ param.red = me->red.parm;
+ param.green = me->green.parm;
+ param.blue = me->blue.parm;
+ viewer_start_op(ida,&desc_map,&param);
+ } else
+ viewer_cancel_preview(ida);
+ viewer_unpick(ida);
+ free(me);
+}
+
+void
+color_init(struct ida_image *img)
+{
+ Widget menu,push,rc;
+ struct ida_coledit *me;
+ Arg args[2];
+
+ me = malloc(sizeof(*me));
+ memset(me,0,sizeof(*me));
+ color_calchist(img,me);
+
+ /* dialog shell */
+ me->dlg = XmCreatePromptDialog(app_shell,"color",NULL,0);
+ XmdRegisterEditres(XtParent(me->dlg));
+ XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_SELECTION_LABEL));
+ XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_HELP_BUTTON));
+ XtUnmanageChild(XmSelectionBoxGetChild(me->dlg,XmDIALOG_TEXT));
+ me->form = XtVaCreateManagedWidget("form",xmFormWidgetClass,
+ me->dlg,NULL);
+ XtAddCallback(XtParent(me->dlg),XmNdestroyCallback,color_destroy,me);
+ XtAddCallback(me->dlg,XmNokCallback,color_button_cb,me);
+ XtAddCallback(me->dlg,XmNcancelCallback,color_button_cb,me);
+
+ /* histograms */
+ XtVaCreateManagedWidget("hist",xmLabelWidgetClass,
+ me->form,NULL);
+ color_createhist(me->form,"red", x11_red, &me->red);
+ color_createhist(me->form,"green",x11_green,&me->green);
+ color_createhist(me->form,"blue", x11_blue, &me->blue);
+ me->red.up = me;
+ me->green.up = me;
+ me->blue.up = me;
+ XtVaCreateManagedWidget("map",xmLabelWidgetClass,
+ me->form,NULL);
+
+ /* control */
+ me->toggle = XtVaCreateManagedWidget("lock",xmToggleButtonWidgetClass,
+ me->form,NULL);
+ XtAddCallback(me->toggle,XmNvalueChangedCallback,color_lock,me);
+ menu = XmCreatePulldownMenu(me->form,"valsM",NULL,0);
+ XtSetArg(args[0],XmNsubMenuId,menu);
+ me->vals = XmCreateOptionMenu(me->form,"vals",args,1);
+ XtManageChild(me->vals);
+ push = XtVaCreateManagedWidget("red",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,color_vals,&me->red);
+ push = XtVaCreateManagedWidget("green",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,color_vals,&me->green);
+ push = XtVaCreateManagedWidget("blue",xmPushButtonWidgetClass,menu,NULL);
+ XtAddCallback(push,XmNactivateCallback,color_vals,&me->blue);
+
+ /* in range */
+ rc = XtVaCreateManagedWidget("in",xmRowColumnWidgetClass,me->form,NULL);
+ XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
+ me->l = XtVaCreateManagedWidget("left",xmTextWidgetClass,rc,NULL);
+ XtAddCallback(me->l,XmNvalueChangedCallback,color_text,me);
+ me->r = XtVaCreateManagedWidget("right",xmTextWidgetClass,rc,NULL);
+ XtAddCallback(me->r,XmNvalueChangedCallback,color_text,me);
+
+ /* out range */
+ rc = XtVaCreateManagedWidget("out",xmRowColumnWidgetClass,me->form,NULL);
+ XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
+ me->b = XtVaCreateManagedWidget("bottom",xmTextWidgetClass,rc,NULL);
+ XtAddCallback(me->b,XmNvalueChangedCallback,color_text,me);
+ me->t = XtVaCreateManagedWidget("top",xmTextWidgetClass,rc,NULL);
+ XtAddCallback(me->t,XmNvalueChangedCallback,color_text,me);
+
+ /* gamma */
+ rc = XtVaCreateManagedWidget("gamma",xmRowColumnWidgetClass,me->form,NULL);
+ XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc,NULL);
+ me->g = XtVaCreateManagedWidget("gamma",xmTextWidgetClass,rc,NULL);
+ XtAddCallback(me->g,XmNvalueChangedCallback,color_text,me);
+
+ /* testing stuff */
+ rc = XtVaCreateManagedWidget("pick",xmRowColumnWidgetClass,me->form,NULL);
+ push = XtVaCreateManagedWidget("white",xmPushButtonWidgetClass,rc,NULL);
+ XtAddCallback(push,XmNactivateCallback,color_pick,me);
+
+ XtManageChild(me->dlg);
+
+ me->cur = &me->red;
+ color_update(me,me->cur,2);
+ XmToggleButtonSetState(me->toggle,True,True);
+}