ref: 2d1e449a474444fa3288c56937a033c4f5f019f7
parent: a3477b9d29227db6e78cede76fdd9d6b83f5ea76
author: sirjofri <sirjofri@sirjofri.de>
date: Wed Aug 14 12:05:53 EDT 2024
adds color selection (wip), adds brush and colors (p9image)
--- a/blie.c
+++ b/blie.c
@@ -50,6 +50,12 @@
vstate.dirty = d;
}
+void
+setmodedirty(int d)
+{
+ vstate.mdirty = d;
+}
+
typedef struct Estate Estate;
struct Estate {
Editor *ed;
@@ -75,7 +81,26 @@
};
Panels panels;
Screen *scr;
+#define NWIN 12
+Image *windows[NWIN];
+void (*winfunc[NWIN])(Image*);
+static Image**
+addwindow(Image *i, void (*f)(Image*))
+{
+ int n;
+ for (n = 0; n < NWIN; n++) {
+ if (!windows[n]) {
+ windows[n] = i;
+ winfunc[n] = f;
+ if (f)
+ f(windows[n]);
+ return &windows[n];
+ }
+ }
+ sysfatal("no more windows available!");
+}
+
void
changecursor(Cursor *c, Image *i, Point off)
{
@@ -194,11 +219,21 @@
static void
redraw(int force)
{
+ int i;
redrawlayers(panels.layers, estate.l);
if (force)
setdrawingdirty(Dpan);
redrawdrawing();
redrawtools();
+
+ for (i = 0; i < NWIN; i++) {
+ if (windows[i]) {
+ topwindow(windows[i]);
+ if (winfunc[i])
+ winfunc[i](windows[i]);
+ }
+ }
+ flushimage(display, 1);
}
static void
@@ -234,7 +269,24 @@
flushimage(display, 1);
}
+Image**
+reqwin(Rectangle r, ulong color, void (*f)(Image*))
+{
+ Image *i;
+ i = allocwindow(scr, r, 0, color);
+// originwindow(i, Pt(0, 0), r.min);
+ return addwindow(i, f);
+}
+
void
+unreqwin(Image **i)
+{
+ freeimage(*i);
+ *i = nil;
+ winfunc[i-windows] = nil;
+}
+
+void
eresized(int new)
{
if (new && getwindow(display, Refnone) < 0)
@@ -280,6 +332,11 @@
static void
condredraw(Redrawwin w)
{
+ if (vstate.mdirty) {
+ redraw(1);
+ vstate.mdirty = 0;
+ return;
+ }
if (w & Rdrawing)
redrawdrawing();
if (w & Rtools)
@@ -293,6 +350,7 @@
Point xy;
Mouse *m = &ev.mouse;
xy = m->xy;
+ vstate.mousepos = xy;
n = Rnil;
if (ptinrect(m->xy, panels.layers->r)) {
esetcursor(nil);
--- a/blie.h
+++ b/blie.h
@@ -2,6 +2,7 @@
typedef struct Editor Editor;
typedef struct Vdata Vdata;
typedef struct Vstate Vstate;
+typedef struct Color Color;
extern Vdata vdata;
extern Vstate vstate;
@@ -8,6 +9,16 @@
extern int bliedebug;
extern int headless;
+struct Color {
+ float r;
+ float g;
+ float b;
+ float a;
+};
+
+Color askcolor(char*);
+ulong color2value(Color);
+
struct Layer {
char *name;
char *label;
@@ -48,10 +59,15 @@
Point offset;
float zoom;
int dirty;
+ int mdirty;
int maxquality;
+ Point mousepos;
};
+void setmodedirty(int);
void setdrawingdirty(int);
+Image **reqwin(Rectangle, ulong, void (*f)(Image*));
+void unreqwin(Image**);
/* writes memimage to drawing image, considering pan and zoom using dirty flags */
void sampleview(Image*, Memimage*, int quality);
--- a/p9image.c
+++ b/p9image.c
@@ -19,7 +19,10 @@
};
Point toolcell;
+#define NUMCELLS (5)
+Image *tmpcol;
+
typedef struct Data Data;
struct Data {
Memimage *img;
@@ -38,6 +41,12 @@
DTmask,
};
+typedef struct Brush Brush;
+struct Brush {
+ Memimage *i;
+ int r;
+};
+
typedef struct Tstate Tstate;
struct Tstate {
Mode mode;
@@ -46,9 +55,48 @@
Memimage *circlebrush;
Memimage *colorbrush;
int brushrad;
+
+ int cbrad;
+ double cbexp;
+ double cbmult;
+
+ int curbrush;
+ int curcolor;
+
+ Brush brushes[NUMCELLS];
+ ulong colordata[NUMCELLS];
};
Tstate tstate;
+static Brush*
+getcurrentbrush(void)
+{
+ if (tstate.curbrush < 4) /* three cells reserved */
+ return &tstate.brushes[3];
+ if (tstate.curbrush >= NUMCELLS)
+ return nil;
+ return &tstate.brushes[tstate.curbrush];
+}
+
+static void
+setcolorbrush(ulong color)
+{
+ if (tstate.colorbrush)
+ freememimage(tstate.colorbrush);
+ tstate.colorbrush = allocmemimage(Rect(0, 0, 1, 1), RGB24);
+ tstate.colorbrush->flags |= Frepl|Fsimple|Fbytes;
+ memfillcolor(tstate.colorbrush, color);
+}
+
+static Memimage*
+getcurrentcolor(void)
+{
+ if (tstate.curcolor < 0 || tstate.curcolor >= NUMCELLS)
+ return nil;
+ setcolorbrush(tstate.colordata[tstate.curcolor]);
+ return tstate.colorbrush;
+}
+
static double
distance(Point p, Point q)
{
@@ -67,11 +115,15 @@
}
static void
-setcirclebrush(int r, double exp)
+setcirclebrush(int r, double exp, double mult)
{
int x, y, d, n;
double dist;
+ tstate.cbrad = r;
+ tstate.cbexp = exp;
+ tstate.cbmult = mult;
+
d = r*2 + 1;
if (tstate.circlebrush)
freememimage(tstate.circlebrush);
@@ -81,24 +133,21 @@
for (y = 0; y < d; y++)
for (x = 0; x < d; x++) {
dist = distance(Pt(x, y), Pt(r, r))/r;
- dist = pow(1. - dist, exp);
+ if (dist > 1.) {
+ *byteaddr(tstate.circlebrush, Pt(x, y)) = 0x0;
+ continue;
+ }
+ dist = pow(1. - dist, exp) * mult;
n = (int)(dist * 256.);
*byteaddr(tstate.circlebrush, Pt(x, y)) = isaturate(n);
}
+
+ tstate.brushes[3].i = tstate.circlebrush;
+ tstate.brushes[3].r = tstate.brushrad;
}
static void
-setcolorbrush(ulong color)
-{
- if (tstate.colorbrush)
- freememimage(tstate.colorbrush);
- tstate.colorbrush = allocmemimage(Rect(0, 0, 1, 1), RGB24);
- tstate.colorbrush->flags |= Frepl|Fsimple|Fbytes;
- memfillcolor(tstate.colorbrush, color);
-}
-
-static void
p9initialize()
{
tstate.mode = Composite;
@@ -113,8 +162,10 @@
ellipse(tstate.circle, Pt(20, 20), 19, 19, 0, display->white, ZP);
ellipse(tstate.circle, Pt(20, 20), 20, 20, 0, display->black, ZP);
- setcirclebrush(20, 1.);
+ setcirclebrush(20, 1., 1.);
setcolorbrush(DRed);
+ tstate.curbrush = 3;
+ tstate.curcolor = -1;
}
static void
@@ -256,8 +307,30 @@
}
static void
+drcols(Image *i, Point p, int n, int hl)
+{
+ Rectangle r;
+ r.min = p;
+ r.max = addpt(p, toolcell);
+ tmpcol = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, tstate.colordata[n]);
+ draw(i, r, tmpcol, nil, ZP);
+ freeimage(tmpcol);
+ border(i, r, 1, hl ? display->black : vdata.gray, ZP);
+}
+
+static void
+drbrush(Image *i, Point p, int n, int hl)
+{
+ Rectangle r;
+ r.min = p;
+ r.max = addpt(p, toolcell);
+ border(i, r, 1, hl ? display->black : vdata.gray, ZP);
+}
+
+static void
p9drawtools(Layer *l, Image *i)
{
+ int n;
Point p;
p = i->r.min;
draw(i, i->r, display->white, nil, ZP);
@@ -267,6 +340,19 @@
drcells(i, p, "S", tstate.mode == Img);
p.x += toolcell.x;
drcells(i, p, "M", tstate.mode == Mask);
+ p.x += toolcell.x;
+
+ for (n = 3; n < NUMCELLS; n++) {
+ drbrush(i, p, n, tstate.curbrush == n);
+ p.x += toolcell.x;
+ }
+
+ p.y += toolcell.y;
+ p.x = i->r.min.x;
+ for (n = 0; n < NUMCELLS; n++) {
+ drcols(i, p, n, tstate.curcolor == n);
+ p.x += toolcell.x;
+ }
}
static int
@@ -282,6 +368,8 @@
Memimage *tgt;
Data *d;
Rectangle r;
+ Brush *brush;
+ Memimage *color;
p9init(l);
d = (Data*)l->data;
@@ -289,6 +377,12 @@
if (!buttons)
return Rnil;
+ brush = getcurrentbrush();
+ color = getcurrentcolor();
+
+ if (!(brush && brush->i && color))
+ return Rnil;
+
tgt = nil;
if (tstate.drawtarget == DTimg)
tgt = d->img;
@@ -298,19 +392,19 @@
if (!tgt)
return Rnil;
- r = insetrect(tgt->r, -tstate.brushrad);
+ r = insetrect(tgt->r, -brush->r);
if (!ptinrect(xy, r))
return Rnil;
- r = rectaddpt(tstate.circlebrush->r,
+ r = rectaddpt(brush->i->r,
subpt(
addpt(tgt->r.min, xy),
- Pt(tstate.brushrad, tstate.brushrad)
+ Pt(brush->r, brush->r)
)
);
- tstate.colorbrush->clipr = tstate.circlebrush->r;
- memimagedraw(tgt, r, tstate.colorbrush, ZP, tstate.circlebrush, ZP, SoverD);
+ color->clipr = brush->i->r;
+ memimagedraw(tgt, r, color, ZP, brush->i, ZP, SoverD);
setdrawingdirty(Dcontent);
dirtylayer(l);
return Rdrawing;
@@ -331,9 +425,63 @@
return Rnil;
}
+static void
+selectbrush(int num)
+{
+}
+
+static void
+selectcolor(int num)
+{
+ if (num < 0 || num >= NUMCELLS)
+ return;
+ tstate.curcolor = num;
+}
+
+static void
+setbrush(int num)
+{
+}
+
+static void
+setcolor(int num)
+{
+ Color c;
+
+ if (num < 0 || num >= NUMCELLS)
+ return;
+
+ c = askcolor("color");
+ if (c.r < 0)
+ return;
+ tstate.colordata[num] = color2value(c);
+}
+
+static void
+configcirclebrush(Mouse *m)
+{
+ char buf[256];
+ char *args[3];
+ int r;
+ double e, mu;
+
+ snprint(buf, sizeof buf, "%d %f %f", tstate.cbrad, tstate.cbexp, tstate.cbmult);
+ if (eenter("circle (rad, exp, mult)", buf, sizeof buf, m) < 0)
+ return;
+
+ if (tokenize(buf, args, 3) != 3)
+ return;
+
+ r = atoi(args[0]);
+ e = atof(args[1]);
+ mu = atof(args[2]);
+ setcirclebrush(r, e, mu);
+}
+
static Redrawwin
p9toolinput(Layer *l, int e, Event ev)
{
+ Point xy;
p9init(l);
if (e != Emouse)
return Rnil;
@@ -356,6 +504,42 @@
tstate.drawtarget = DTmask;
goto Out;
}
+ }
+ xy.x = ev.mouse.xy.x / toolcell.x;
+ xy.y = ev.mouse.xy.y / toolcell.y;
+
+ if (ev.mouse.buttons & 1) {
+ /* left mouse button */
+ switch (xy.y) {
+ case 0:
+ if (xy.x < 3)
+ return Rnil;
+ selectbrush(xy.x);
+ return Rtools;
+ case 1:
+ selectcolor(xy.x);
+ return Rtools;
+ }
+ return Rnil;
+ }
+ if (ev.mouse.buttons & 4) {
+ /* right mouse button */
+ switch (xy.y) {
+ case 0:
+ if (xy.x < 3)
+ return Rnil;
+ if (xy.x == 3) {
+ /* special case for circle brush */
+ configcirclebrush(&ev.mouse);
+ return Rnil;
+ }
+ setbrush(xy.x);
+ return Rtools;
+ case 1:
+ setcolor(xy.x);
+ return Rtools;
+ }
+ return Rnil;
}
return Rnil;
Out:
--- a/util.c
+++ b/util.c
@@ -3,6 +3,7 @@
#include <draw.h>
#include <memdraw.h>
#include <event.h>
+#include <keyboard.h>
#include "blie.h"
Memimage*
@@ -57,4 +58,82 @@
xy.x = xy.x / vstate.zoom;
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;
}
--- a/words
+++ b/words
@@ -85,3 +85,22 @@
Selecting a layer also changes the "editor". The editor is a
module that takes over part of the control, depending on the
type (see first section about file format).
+
+
+EDITOR p9image
+
+The tools panel shows buttons in a grid.
+
+First row:
+- C: show composite image
+- S: show only the image of this layer ("Solo")
+- M: show only mask
+- the others: brushes (first brush is a configurable circle brush)
+
+Second row:
+- color palette
+
+For the color palette and configurable brushes:
+- left click to select
+- right click to configure
+- middle mouse to preview brush (not implemented yet)