ref: f5d679a1fea46f5f6128679ed766ec0cace95c79
parent: 2d1e449a474444fa3288c56937a033c4f5f019f7
author: sirjofri <sirjofri@sirjofri.de>
date: Thu Aug 15 15:57:39 EDT 2024
implement eentercolor (generic color picker), adds initlayer editor function
--- a/blie.c
+++ b/blie.c
@@ -109,6 +109,13 @@
dstate.curoffset = off;
}
+static void
+initlayer(Layer *l, int, int, void*)
+{
+ if (l->editor && l->editor->initlayer)
+ l->editor->initlayer(l);
+}
+
int
loadfile(void)
{
@@ -128,6 +135,8 @@
while (s = Brdstr(b, '\n', 1))
addlayer(s);
+ foreachlayer(initlayer, nil);
+
return 1;
}
@@ -350,6 +359,7 @@
Point xy;
Mouse *m = &ev.mouse;
xy = m->xy;
+ vstate.lastmouse = ev.mouse;
vstate.mousepos = xy;
n = Rnil;
if (ptinrect(m->xy, panels.layers->r)) {
--- a/blie.h
+++ b/blie.h
@@ -2,7 +2,6 @@
typedef struct Editor Editor;
typedef struct Vdata Vdata;
typedef struct Vstate Vstate;
-typedef struct Color Color;
extern Vdata vdata;
extern Vstate vstate;
@@ -9,16 +8,9 @@
extern int bliedebug;
extern int headless;
-struct Color {
- float r;
- float g;
- float b;
- float a;
-};
+/* could be extracted to libdraw */
+int eentercolor(char*, ulong*, Mouse*);
-Color askcolor(char*);
-ulong color2value(Color);
-
struct Layer {
char *name;
char *label;
@@ -62,6 +54,7 @@
int mdirty;
int maxquality;
Point mousepos;
+ Mouse lastmouse;
};
void setmodedirty(int);
@@ -81,6 +74,7 @@
struct Editor {
char *name;
void (*init)(void);
+ void (*initlayer)(Layer*);
Memimage *(*composite)(Layer*, Memimage*);
Memimage *(*raw)(Layer*);
Memimage *(*mask)(Layer*);
--- /dev/null
+++ b/eentercolor.c
@@ -1,0 +1,341 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include <keyboard.h>
+
+/* KNOWN BUGS
+
+First pixel row of the image doesn't yield proper values.
+
+*/
+
+typedef struct Hsv Hsv;
+typedef struct Rgb Rgb;
+struct Hsv {
+ double h;
+ double s;
+ double v;
+};
+struct Rgb {
+ double r;
+ double g;
+ double b;
+};
+
+static Rgb
+hsv2rgb(Hsv hsv)
+{
+ Rgb r;
+ int i;
+ double ff, p, q, t;
+
+ if (hsv.s <= 0.) {
+ r.r = hsv.v;
+ r.g = hsv.v;
+ r.b = hsv.v;
+ return r;
+ }
+ hsv.h *= 360.;
+ if (hsv.h >= 360.0)
+ hsv.h = 0.;
+ hsv.h /= 60.;
+ i = hsv.h;
+ ff = hsv.h - i;
+ p = hsv.v * (1.0 - hsv.s);
+ q = hsv.v * (1.0 - (hsv.s * ff));
+ t = hsv.v * (1.0 - (hsv.s * (1.0 - ff)));
+
+ switch (i) {
+ case 0:
+ r.r = hsv.v;
+ r.g = t;
+ r.b = p;
+ break;
+ case 1:
+ r.r = q;
+ r.g = hsv.v;
+ r.b = p;
+ break;
+ case 2:
+ r.r = p;
+ r.g = hsv.v;
+ r.b = t;
+ break;
+ case 3:
+ r.r = p;
+ r.g = q;
+ r.b = hsv.v;
+ break;
+ case 4:
+ r.r = t;
+ r.g = p;
+ r.b = hsv.v;
+ break;
+ case 5:
+ default:
+ r.r = hsv.v;
+ r.g = p;
+ r.b = q;
+ break;
+ }
+ return r;
+}
+
+Image *colors = nil;
+uchar *data;
+
+static void
+initimage(double hue)
+{
+ uchar *d;
+ int x, y, n;
+ Hsv hsv;
+ Rgb rgb;
+
+ n = 100 * 100 * 4*sizeof(uchar);
+ if (!data)
+ data = malloc(n);
+ if (!colors)
+ colors = allocimage(display, Rect(0, 0, 100, 100), RGBA32, 0, DNofill);
+ if (!colors || !data)
+ sysfatal("out of memory: %r");
+
+ hsv.h = hue;
+ for (y = 0; y < 100; y++)
+ for (x = 0; x < 100; x++) {
+ d = data + (y*100 + x)*4*sizeof(uchar);
+
+ hsv.s = (double)x/100;
+ hsv.v = 1.-(double)y/100;
+ rgb = hsv2rgb(hsv);
+
+ d[0] = 255;
+ d[1] = rgb.b * 255;
+ d[2] = rgb.g * 255;
+ d[3] = rgb.r * 255;
+ }
+
+ loadimage(colors, colors->r, data, n);
+}
+
+Image *huegrad = nil;
+
+static void
+inithuegrad(void)
+{
+ uchar *dat, *d;
+ int x, n, p;
+ Rgb rgb;
+ Hsv hsv;
+
+ if (!huegrad)
+ huegrad = allocimage(display, Rect(0, 0, 100, 1), RGBA32, 1, DNofill);
+ if (!huegrad)
+ sysfatal("out of memory: %r");
+ p = huegrad->depth/8;
+ n = 100 * p;
+ dat = malloc(n);
+ if (!dat)
+ sysfatal("out of memory: %r");
+
+ hsv.v = 1.;
+ hsv.s = 1.;
+ for (x = 0; x < 100; x++) {
+ d = dat + x*p;
+ hsv.h = (double)x/100;
+ rgb = hsv2rgb(hsv);
+
+ d[0] = 255;
+ d[1] = rgb.b * 255;
+ d[2] = rgb.g * 255;
+ d[3] = rgb.r * 255;
+ }
+ loadimage(huegrad, huegrad->r, dat, n);
+ free(dat);
+}
+
+static Rgb
+parsergb(char *str)
+{
+ Rgb rgb;
+ ulong c;
+ uchar *p;
+ char *s;
+
+ /* valid formats are: (spaces ignored)
+ (RRR,GGG,BBB)
+ RRR,GGG,BBB
+ #RRGGBB
+ */
+
+ while (*str == ' ' || *str == '\t')
+ str++;
+
+ switch (*str) {
+ case '#':
+ str++;
+ c = strtoul(str, nil, 16);
+ p = (uchar*)&c;
+ rgb.r = (double)p[2] / 255;
+ rgb.g = (double)p[1] / 255;
+ rgb.b = (double)p[0] / 255;
+ return rgb;
+ case '(':
+ str++;
+ }
+
+ rgb.r = (double)strtol(str, &s, 10) / 255;
+ str = s+1;
+ rgb.g = (double)strtol(str, &s, 10) / 255;
+ str = s+1;
+ rgb.b = (double)strtol(str, &s, 10) / 255;
+ return rgb;
+}
+
+int
+eentercolor(char *ask, ulong *out, Mouse *m)
+{
+ Event ev;
+ int e;
+ Rectangle r, ri, rh, rc, ro;
+ Point askp, outp;
+ Point xy;
+ uchar *pixel;
+ int fy;
+ double hue;
+ Rgb rgb;
+ Hsv hsv;
+ Image *blitimg;
+ Image *selcol;
+ char buf[1 + 3 + 1 + 3 + 1 + 3 + 1 + 1]; /* (XXX,XXX,XXX) */
+
+ hue = 0.;
+
+ if (!ask)
+ ask = "color:";
+
+ fy = stringsize(font, "M").y;
+ buf[0] = 0;
+
+ if (!huegrad)
+ inithuegrad();
+ if (!colors)
+ initimage(hue);
+
+ pixel = (uchar*)out;
+
+ /* dimensions of full box */
+ r.min = m->xy;
+ r.max = addpt(r.min, Pt(110, 110));
+ r.max.y += 2 * (fy + 10); /* two lines of text */
+ r.max.y += 15; /* value gradient */
+
+ /* dimensions of inline image */
+ ri = r;
+ ri.min.y += fy + 10;
+ ri.min.x += 5;
+ ri.min.y += 5;
+ ri.max.x = ri.min.x + 100;
+ ri.max.y = ri.min.y + 100;
+
+ /* dimensions of hue image */
+ rh.min.x = ri.min.x;
+ rh.min.y = ri.max.y;
+ rh.max.x = ri.max.x;
+ rh.max.y = rh.min.y + 15;
+
+ /* dimensions of selected color */
+ rc.min.x = rh.min.x;
+ rc.min.y = rh.max.y;
+ rc.max.x = ri.max.x;
+ rc.max.y = rc.min.y + 15;
+
+ /* position of ask string */
+ askp = r.min;
+ askp.x += 5;
+ askp.y += 5;
+
+ /* position of out string */
+ outp = rc.min;
+ outp.y += 15 + 5;
+ ro.min = outp;
+ ro.max = addpt(outp, Pt(100, fy));
+
+ r.max.x = r.min.x + 110;
+ r.max.y = outp.y + fy + 10;
+
+ blitimg = allocimage(display, r, screen->chan, 0, DNofill);
+ draw(blitimg, blitimg->r, screen, nil, blitimg->r.min);
+
+ selcol = nil;
+ for (;;) {
+ /* redraw routines */
+ draw(screen, r, display->white, nil, ZP);
+ border(screen, r, 1, display->black, ZP);
+ string(screen, askp, display->black, ZP, font, ask);
+ string(screen, outp, display->black, ZP, font, buf);
+ draw(screen, ri, colors, nil, ZP);
+ draw(screen, rh, huegrad, nil, ZP);
+ if (selcol)
+ freeimage(selcol);
+ selcol = allocimage(display, rc, RGBA32, 1, *out);
+ draw(screen, rc, selcol, nil, ZP);
+
+ /* event handling */
+ e = event(&ev);
+ switch (e) {
+ case Ekeyboard:
+ if (ev.kbdc == Kesc || ev.kbdc == 'q')
+ goto Abort;
+ if (ev.kbdc == '\n')
+ goto Accept;
+ break;
+ case Emouse:
+ if (!ev.mouse.buttons)
+ break;
+ if (ptinrect(ev.mouse.xy, ri)) {
+ xy = subpt(ev.mouse.xy, ri.min);
+ hsv.h = hue;
+ hsv.s = (double)xy.x/100;
+ hsv.v = 1.-(double)xy.y/100;
+ rgb = hsv2rgb(hsv);
+ pixel[3] = rgb.r * 256;
+ pixel[2] = rgb.g * 256;
+ pixel[1] = rgb.b * 256;
+ pixel[0] = 255;
+ snprint(buf, sizeof buf, "%3d,%3d,%3d", pixel[3], pixel[2], pixel[1]);
+ break;
+ }
+ if (ptinrect(ev.mouse.xy, rh)) {
+ xy.x = ev.mouse.xy.x - rh.min.x;
+ hue = ((double)xy.x / 100);
+ initimage(hue);
+ break;
+ }
+ if (ptinrect(ev.mouse.xy, ro)) {
+ if (!eenter("color:", buf, sizeof buf, &ev.mouse))
+ break;
+ rgb = parsergb(buf);
+ pixel[3] = rgb.r * 255;
+ pixel[2] = rgb.g * 255;
+ pixel[1] = rgb.b * 255;
+ pixel[0] = 255;
+ snprint(buf, sizeof buf, "%3d,%3d,%3d", pixel[3], pixel[2], pixel[1]);
+ break;
+ }
+ }
+ }
+Abort:
+ draw(screen, blitimg->r, blitimg, nil, blitimg->r.min);
+ freeimage(blitimg);
+ if (selcol)
+ freeimage(selcol);
+ return 0;
+Accept:
+ draw(screen, blitimg->r, blitimg, nil, blitimg->r.min);
+ freeimage(blitimg);
+ if (selcol)
+ freeimage(selcol);
+ return 1;
+}
--- a/mkfile
+++ b/mkfile
@@ -7,6 +7,7 @@
editor.$O\
layer.$O\
sample.$O\
+ eentercolor.$O\
util.$O\
p9image.$O\
--- a/p9image.c
+++ b/p9image.c
@@ -220,8 +220,6 @@
p9composite(Layer *l, Memimage *img)
{
Data *d;
-
- p9init(l);
d = (Data*)l->data;
if (!img) {
@@ -238,7 +236,6 @@
p9raw(Layer *l)
{
Data *d;
- p9init(l);
d = (Data*)l->data;
return d->img;
}
@@ -247,7 +244,6 @@
p9mask(Layer *l)
{
Data *d;
- p9init(l);
d = (Data*)l->data;
return d->mask;
}
@@ -258,7 +254,6 @@
Data *data;
Memimage *mi;
- p9init(l);
data = (Data*)l->data;
if (!i)
@@ -358,7 +353,6 @@
static int
p9savedata(Layer *l)
{
- p9init(l);
return 1;
}
@@ -371,7 +365,6 @@
Brush *brush;
Memimage *color;
- p9init(l);
d = (Data*)l->data;
if (!buttons)
@@ -413,8 +406,6 @@
static Redrawwin
p9drawinput(Layer *l, int e, Event ev)
{
- p9init(l);
-
switch (e) {
case Ekeyboard:
break;
@@ -446,15 +437,14 @@
static void
setcolor(int num)
{
- Color c;
+ ulong col;
if (num < 0 || num >= NUMCELLS)
return;
- c = askcolor("color");
- if (c.r < 0)
+ if (!eentercolor("color", &col, &vstate.lastmouse))
return;
- tstate.colordata[num] = color2value(c);
+ tstate.colordata[num] = col;
}
static void
@@ -482,7 +472,6 @@
p9toolinput(Layer *l, int e, Event ev)
{
Point xy;
- p9init(l);
if (e != Emouse)
return Rnil;
@@ -550,6 +539,7 @@
Editor p9image = {
.name = "p9img",
.init = p9initialize,
+ .initlayer = p9init,
.raw = p9raw,
.mask = p9mask,
.overlay = p9overlay,
--- a/util.c
+++ b/util.c
@@ -59,81 +59,3 @@
xy.y = xy.y / vstate.zoom;
return xy;
}
-
-ulong
-color2value(Color c)
-{
- int r, g, b, a;
- r = c.r * 256;
- g = c.g * 256;
- b = c.b * 256;
- a = c.a * 256;
- return r<<24 | g<<16 | b<<8 | a;
-}
-
-Image **askcolimage;
-char *askcolprompt;
-
-static void
-askcolorredraw(Image *i)
-{
- border(i, i->r, 1, display->black, ZP);
- string(i, addpt(i->r.min, Pt(10, 0)), display->black, ZP, font, askcolprompt);
-}
-
-Color
-askcolor(char *prompt)
-{
- Event ev;
- int e;
- Color c;
- Rectangle r;
- Image *col = nil;
- ulong val;
- Point xy;
-
- r.min = vstate.mousepos;
- r.max = addpt(r.min, Pt(100, 100+vdata.fontheight));
-
- askcolprompt = prompt;
- askcolimage = reqwin(r, 0xccccccff, askcolorredraw);
- r.min.y += vdata.fontheight;
-
- for (;;) {
- e = event(&ev);
- switch (e) {
- case Ekeyboard:
- if (ev.kbdc == Kesc || ev.kbdc == 'q')
- goto Abort;
- else if (ev.kbdc == '\n')
- goto Accept;
- break;
- case Emouse:
- if (!ev.mouse.buttons)
- break;
- if (!ptinrect(ev.mouse.xy, r))
- break;
- xy = subpt(ev.mouse.xy, (*askcolimage)->r.min);
- xy.y -= vdata.fontheight;
- c.r = (double)xy.x / 100;
- c.g = (double)xy.y / 100;
- c.b = 0.;
- c.a = 1.;
- val = color2value(c);
- if (col)
- freeimage(col);
- col = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, val);
- border(*askcolimage, (*askcolimage)->r, 2, col, ZP);
-// fprint(2, "col: %f %f %f %f\n", c.r, c.g, c.b, c.a);
- break;
- }
- }
-
-Abort:
- c.r = -1.;
-Accept:
- setmodedirty(1);
- unreqwin(askcolimage);
- askcolimage = nil;
- return c;
-}