ref: 6772237407110a85e52893d91caba41969dad707
parent: f7ce9af61db9b0d072dad403cb98d8bad4224dda
author: sirjofri <sirjofri@sirjofri.de>
date: Fri Aug 9 08:50:28 EDT 2024
improve performance, fixes cursor issues, general code improvement
--- a/blie.c
+++ b/blie.c
@@ -25,11 +25,6 @@
.keyzoom = 0.2,
};
-Vstate vstate = {
- .zoom = 1.0,
- .offset = { 0, 0 },
-};
-
static void
initvdata(void)
{
@@ -41,6 +36,18 @@
vdata.gray = allocimagemix(display, DBlack, DWhite);
}
+Vstate vstate = {
+ .zoom = 1.0,
+ .offset = { 0, 0 },
+ .dirty = 3,
+};
+
+void
+setdrawingdirty(int d)
+{
+ vstate.dirty = d;
+}
+
typedef struct Estate Estate;
struct Estate {
Editor *ed;
@@ -98,21 +105,23 @@
}
static void
-docomp(Layer *l, int id, void *aux)
+docomp(Layer *l, int id, int, void *aux)
{
int i;
Memimage **img = (Memimage**)aux;
Editor *ed = l->editor;
+ Memimage *(*c)(Layer*, Memimage*);
+
i = (id + 1)%2;
if (bliedebug)
fprint(2, "composite: %s\n", l->name);
- if (ed->composite)
- img[id%2] = ed->composite(l, img[i]);
- else
- img[id%2] = ecomposite(l, img[i]);
- freememimage(img[i]);
+ c = ed->composite ? ed->composite : ecomposite;
+
+ if (!l->composited)
+ l->composited = c(l, img[i]);
+ img[id%2] = l->composited;
img[i] = nil;
}
@@ -124,12 +133,8 @@
img[0] = nil;
img[1] = nil;
- i = foreachlayer(docomp, img);
-
- if (img[i%2])
- writememimage(1, img[i%2]);
- else
- writememimage(1, img[(i+1)%2]);
+ i = (foreachlayer(docomp, img) + 1) % 2;
+ writememimage(1, img[i]);
}
static void
@@ -138,6 +143,12 @@
Memimage *img[2]; /* swapchain */
int i;
+ if (estate.ed && estate.ed->overlay) {
+ if (estate.ed->overlay(estate.l, nil)) {
+ estate.ed->overlay(estate.l, panels.drawing);
+ goto Cursorfix;
+ }
+ }
img[0] = nil;
img[1] = nil;
i = (foreachlayer(docomp, img) + 1) % 2;
@@ -144,10 +155,15 @@
sampleview(panels.drawing, img[i]);
- if (!(estate.ed && estate.ed->overlay))
- return;
+ if (estate.ed && estate.ed->overlay)
+ estate.ed->overlay(estate.l, panels.drawing);
- estate.ed->overlay(estate.l, panels.drawing);
+ /* fix cursor */
+Cursorfix:
+ if (dstate.cursorblit) {
+ freeimage(dstate.cursorblit);
+ dstate.cursorblit = nil;
+ }
}
static void
@@ -158,22 +174,20 @@
} else {
draw(panels.tools, panels.tools->r, display->white, nil, ZP);
}
+ line(panels.tools,
+ Pt(panels.tools->r.min.x, panels.tools->r.max.y-1),
+ subpt(panels.tools->r.max, Pt(0, 1)),
+ Endsquare, Endsquare, 0, display->black, ZP);
}
static void
-redraw(void)
+redraw(int force)
{
redrawlayers(panels.layers, estate.l);
- line(panels.layers, panels.layers->r.min,
- Pt(panels.layers->r.min.x, panels.layers->r.max.y),
- Endsquare, Endsquare, 0, display->black, ZP);
+ if (force)
+ setdrawingdirty(Dpan);
redrawdrawing();
-
redrawtools();
- line(panels.tools,
- Pt(panels.tools->r.min.x, panels.tools->r.max.y-1),
- subpt(panels.tools->r.max, Pt(0, 1)),
- Endsquare, Endsquare, 0, display->black, ZP);
}
static void
@@ -215,7 +229,7 @@
if (new && getwindow(display, Refnone) < 0)
sysfatal("resize failed: %r");
screeninit();
- redraw();
+ redraw(1);
}
static void
@@ -224,7 +238,7 @@
estate.ed = l->editor;
estate.l = l;
screeninit();
- redraw();
+ redraw(1);
}
static void
@@ -255,11 +269,11 @@
static void
handlemouse(Event ev)
{
- int n;
+ Redrawwin n;
Point xy;
Mouse *m = &ev.mouse;
xy = m->xy;
- n = 0;
+ n = Rnil;
if (ptinrect(m->xy, panels.layers->r)) {
esetcursor(nil);
drawcursor(m->xy, 1);
@@ -276,14 +290,15 @@
if (estate.ed->toolinput)
n = estate.ed->toolinput(estate.l, Emouse, ev);
switch (n) {
- case 1:
+ case Rdrawing:
+ setdrawingdirty(Dcontent);
redrawdrawing();
break;
- case 2:
+ case Rtools:
redrawtools();
break;
case 3:
- redraw();
+ redraw(0);
break;
}
return;
@@ -293,14 +308,15 @@
if (estate.ed->drawinput)
n = estate.ed->drawinput(estate.l, Emouse, ev);
switch (n) {
- case 1:
+ case Rdrawing:
+ setdrawingdirty(Dcontent);
redrawdrawing();
break;
- case 2:
+ case Rtools:
redrawtools();
break;
case 3:
- redraw();
+ redraw(0);
break;
}
esetcursor(dstate.cursor);
@@ -316,25 +332,28 @@
switch (kbdc) {
case Kup:
vstate.offset.y += vdata.keyoffset;
- goto Redraw;
+ goto Dirtypan;
case Kdown:
vstate.offset.y -= vdata.keyoffset;
- goto Redraw;
+ goto Dirtypan;
case Kleft:
vstate.offset.x += vdata.keyoffset;
- goto Redraw;
+ goto Dirtypan;
case Kright:
vstate.offset.x -= vdata.keyoffset;
- goto Redraw;
+ goto Dirtypan;
case '.':
vstate.zoom += vdata.keyzoom;
- goto Redraw;
+ goto Dirtyzoom;
case ',':
vstate.zoom -= vdata.keyzoom;
- goto Redraw;
+ goto Dirtyzoom;
}
return 0;
-Redraw:
+Dirtyzoom:
+ vstate.dirty |= Dzoom;
+Dirtypan:
+ vstate.dirty |= Dpan;
redrawdrawing();
return 1;
}
@@ -427,7 +446,7 @@
estate.ed = nil;
estate.l = nil;
screeninit();
- redraw();
+ redraw(1);
for (;;) {
e = event(&ev);
--- a/blie.h
+++ b/blie.h
@@ -12,6 +12,8 @@
char *label;
Drawop op;
Editor *editor;
+ Memimage *composited;
+ int num;
void *data;
};
@@ -20,9 +22,10 @@
void addlayer(char *name);
void movelayer(Layer*, int);
void savelayermeta(Layer*);
+void dirtylayer(Layer*);
void redrawlayers(Image*, Layer*);
void clicklayer(Mouse, void (*f)(Layer*));
-int foreachlayer(void (*f)(Layer*, int, void*), void*);
+int foreachlayer(void (*f)(Layer*, int, int, void*), void*);
void changecursor(Cursor*, Image*, Point);
@@ -34,24 +37,39 @@
Image *gray;
};
+enum {
+ Dpan = 1,
+ Dzoom = 2,
+ Dcontent = Dpan|Dzoom,
+};
+
struct Vstate {
Point offset;
float zoom;
+ int dirty;
};
-/* writes memimage to drawing image, considering pan and zoom */
+void setdrawingdirty(int);
+
+/* writes memimage to drawing image, considering pan and zoom using dirty flags */
void sampleview(Image*, Memimage*);
+typedef enum {
+ Rnil = 0,
+ Rdrawing = 1,
+ Rtools = 2,
+} Redrawwin;
+
struct Editor {
char *name;
Memimage *(*composite)(Layer*, Memimage*);
Memimage *(*raw)(Layer*);
Memimage *(*mask)(Layer*);
- void (*overlay)(Layer*, Image*);
+ int (*overlay)(Layer*, Image*);
Rectangle (*toolrect)(Layer*);
void (*drawtools)(Layer*, Image*);
- int (*drawinput)(Layer*, int, Event);
- int (*toolinput)(Layer*, int, Event);
+ Redrawwin (*drawinput)(Layer*, int, Event);
+ Redrawwin (*toolinput)(Layer*, int, Event);
int (*savedata)(Layer*);
};
--- /dev/null
+++ b/editor.txt
@@ -1,0 +1,97 @@
+Editor functions and properties (blie.h:/^struct Editor)
+
+PROPERTY name
+
+short name of the editor. Works as an identifier, must be unique!
+
+
+FUNCTION composite (*)
+
+param: Layer* Layer to work with
+param: Memimage* Memimage to composite over
+return: Memimage* composited result
+
+
+FUNCTION raw (tbd)
+
+param: Layer* Layer to work with
+return: Memimage* raw image (no duplicate)
+
+
+FUNCTION mask (tbd)
+
+param: Layer* Layer to work with
+return: Memimage* raw mask (no duplicate)
+
+
+FUNCTION overlay (*)
+
+param: Layer* Layer to work with
+param: Image* editor image to draw on
+return: int whether overlay fills the full drawing
+
+This function is called in two situations:
+
+1. To determine if we need to draw the image underneath
+2. To actually draw the overlay
+
+Case 1 is when the editor image is nil. In this case, only the
+return value is relevant.
+
+Case 2 is when the editor image is set. In this case, the return
+value is ignored.
+
+Case 1 is important to improve performance. There's no need to
+composite and draw the drawing when we will overdraw it with
+a full overlay image anyway.
+
+
+FUNCTION toolrect
+
+param: Layer* Layer to work with
+return: Rectangle wanted size of the tool window
+
+For the given layer, return the needed size of the tool window.
+In general, the tool window should be pretty small in height.
+
+
+FUNCTION drawtools
+
+param: Layer* Layer to work with
+param: Image* Image to draw the tools on
+
+For the given layer, draw the tools handles into the supplied
+image.
+
+
+FUNCTION drawinput
+
+param: Layer* Layer to work with
+param: int Event type (Emouse, Ekeyboard)
+param: Event Event structure
+return: Redrawwin Redraw parameters
+
+The mouse xy inside the event structure is normalized to the
+(0,0) position of the drawing window.
+
+Use the return parameter to specify which window should be
+redrawn.
+
+
+FUNCTION toolinput
+
+param: Layer* Layer to work with
+param: int Event type (Emouse, Ekeyboard)
+param: Event Event structure
+return: Redrawwin Redraw parameters
+
+This function is analogous to drawinput, just for the tools
+window.
+
+
+FUNCTION savedata
+
+param: Layer* Layer to work with
+return: int 1 for success, 0 for fail
+
+If failed, set errstr.
--- a/layer.c
+++ b/layer.c
@@ -59,6 +59,18 @@
}
static void
+_renum(Layer *l, int id, int, void*)
+{
+ l->num = id;
+}
+
+static void
+renumlayers(void)
+{
+ foreachlayer(_renum, nil);
+}
+
+static void
addlayerl(Layer *l)
{
List *f;
@@ -65,6 +77,7 @@
if (!firstlayer) {
firstlayer = mallocz(sizeof(List), 1);
firstlayer->layer = l;
+ l->num = 0;
return;
}
for (f = firstlayer; f->next; f = f->next)
@@ -71,6 +84,7 @@
continue;
f->next = mallocz(sizeof(List), 1);
f->next->layer = l;
+ l->num = f->layer->num + 1;
}
void
@@ -157,9 +171,22 @@
parent->next = l->next;
l->next = l->next ? l->next->next : nil;
parent->next->next = l;
+
+ // TODO: verify!
+ l->layer->num--;
+ if (l->next)
+ l->next->layer->num++;
}
void
+dirtylayer(Layer *layer)
+{
+ if (layer->composited)
+ freememimage(layer->composited);
+ layer->composited = nil;
+}
+
+void
savelayermeta(Layer *layer)
{
Biobuf *b;
@@ -176,7 +203,7 @@
}
int
-foreachlayer(void (*f)(Layer*, int, void*), void* aux)
+foreachlayer(void (*f)(Layer*, int, int, void*), void* aux)
{
List *l;
int i;
@@ -183,7 +210,7 @@
i = 0;
for (l = firstlayer; l; l = l->next, i++) {
- f(l->layer, i, aux);
+ f(l->layer, i, !l->next, aux);
}
return i;
@@ -192,7 +219,7 @@
int layerheight;
static void
-redrawlayer(Layer *l, int n, void *aux)
+redrawlayer(Layer *l, int n, int, void *aux)
{
void **data;
Image *img;
@@ -225,10 +252,12 @@
layerheight = vdata.fontheight + 10;
draw(img, img->r, display->white, nil, ZP);
foreachlayer(redrawlayer, data);
+ line(img, img->r.min, Pt(img->r.min.x, img->r.max.y),
+ Endsquare, Endsquare, 0, display->black, ZP);
}
static void
-checkclick(Layer *l, int n, void *aux)
+checkclick(Layer *l, int n, int, void *aux)
{
void **a;
void (*factivate)(Layer*);
--- a/p9image.c
+++ b/p9image.c
@@ -122,7 +122,7 @@
return d->mask;
}
-static void
+static int
p9overlay(Layer *l, Image *i)
{
Data *data;
@@ -131,6 +131,9 @@
p9init(l);
data = (Data*)l->data;
+ if (!i)
+ return tstate.mode != Composite;
+
switch (tstate.mode) {
case Composite:
break;
@@ -142,7 +145,7 @@
goto Mout;
}
changecursor(nil, nil, ZP);
- return;
+ return 0;
Mout:
if (!tstate.circle) {
tstate.circle = allocimage(display, Rect(0, 0, 41, 41), RGBA32, 0, DTransparent);
@@ -151,9 +154,11 @@
}
changecursor(&ccircle, tstate.circle, Pt(-20, -20));
if (!mi)
- return;
+ return 0;
+ setdrawingdirty(Dcontent);
sampleview(i, mi);
+ return 0;
}
static Rectangle
@@ -175,7 +180,7 @@
return 1;
}
-static int
+static Redrawwin
p9drawinput(Layer *l, int e, Event)
{
p9init(l);
@@ -185,18 +190,18 @@
case Emouse:
break;
}
- return 0;
+ return Rnil;
}
-static int
+static Redrawwin
p9toolinput(Layer *l, int e, Event ev)
{
p9init(l);
if (e != Emouse)
- return 0;
+ return Rnil;
if (!ev.mouse.buttons)
- return 0;
+ return Rnil;
switch (ev.mouse.xy.x / 50) {
case 0:
@@ -209,9 +214,9 @@
tstate.mode = Mask;
break;
default:
- return 0;
+ return Rnil;
}
- return 1;
+ return Rdrawing;
}
Editor p9image = {
--- a/sample.c
+++ b/sample.c
@@ -113,29 +113,39 @@
return nil;
}
+Memimage *lastsampled = nil;
+
void
sampleview(Image *img, Memimage *src)
{
Memimage *tmi;
- Memimage *tsi;
Rectangle r;
int nw;
+ if (!vstate.dirty)
+ return;
+
r.min = ZP;
r.max = Pt(Dx(img->r), Dy(img->r));
- tsi = resample(src, Dx(src->r)*vstate.zoom, Dy(src->r)*vstate.zoom);
- if (!tsi)
- return;
+ if (vstate.dirty & Dzoom) {
+ freememimage(lastsampled);
+ lastsampled = nil;
+ }
+ if (!lastsampled) {
+ lastsampled = resample(src, Dx(src->r)*vstate.zoom, Dy(src->r)*vstate.zoom);
+ if (!lastsampled)
+ return;
+ }
tmi = allocmemimage(r, img->chan);
memfillcolor(tmi, DBlack);
- memimagedraw(tmi, tmi->r, tsi, addpt(tmi->r.min, vstate.offset), nil, ZP, S);
+ memimagedraw(tmi, tmi->r, lastsampled, addpt(tmi->r.min, vstate.offset), nil, ZP, S);
nw = tmi->width * tmi->r.max.y * sizeof(ulong); // tmi->r.max.y == Dy(tmi->r)
draw(img, img->r, display->black, nil, ZP);
loadimage(img, img->r, tmi->data->bdata, nw);
- freememimage(tsi);
freememimage(tmi);
+ vstate.dirty = 0;
}