ref: da5ef81858616aa4cd9bd350b2de30a8553330a3
parent: a376bcda599c8533bce9ed6128a0d42eaea18ba4
author: sirjofri <[email protected]>
date: Thu Aug 8 11:56:28 EDT 2024
adds zoom support, images should be r8g8b8(a8), masks can be k8 or r8g8b8
--- a/blie.c
+++ b/blie.c
@@ -22,8 +22,14 @@
Vdata vdata = {
.layerwinwidth = 150,
.keyoffset = 10,
+ .keyzoom = 0.2,
};
+Vstate vstate = {
+ .zoom = 1.0,
+ .offset = { 0, 0 },
+};
+
static void
initvdata(void)
{
@@ -44,7 +50,6 @@
typedef struct Dstate Dstate;
struct Dstate {
- Point offset;
Cursor *cursor;
Image *cursimg;
Image *cursorblit;
@@ -131,28 +136,18 @@
redrawdrawing(void)
{
Memimage *img[2]; /* swapchain */
- Memimage *ti; /* intermediate image */
int i;
- int nw;
img[0] = nil;
img[1] = nil;
i = (foreachlayer(docomp, img) + 1) % 2;
+
+ sampleview(panels.drawing, img[i]);
- ti = allocmemimage(panels.drawing->r, panels.drawing->chan);
- memfillcolor(ti, DBlack);
- memimagedraw(ti, ti->r, img[i], dstate.offset, nil, ZP, S);
-
- nw = ti->width * Dy(ti->r) * sizeof(ulong);
- draw(panels.drawing, panels.drawing->r, display->black, nil, ZP);
- loadimage(panels.drawing, panels.drawing->r, ti->data->bdata, nw);
-
- freememimage(ti);
-
if (!(estate.ed && estate.ed->overlay))
return;
- estate.ed->overlay(estate.l, panels.drawing, dstate.offset);
+ estate.ed->overlay(estate.l, panels.drawing);
}
static void
@@ -320,16 +315,22 @@
{
switch (kbdc) {
case Kup:
- dstate.offset.y += vdata.keyoffset;
+ vstate.offset.y += vdata.keyoffset;
goto Redraw;
case Kdown:
- dstate.offset.y -= vdata.keyoffset;
+ vstate.offset.y -= vdata.keyoffset;
goto Redraw;
case Kleft:
- dstate.offset.x += vdata.keyoffset;
+ vstate.offset.x += vdata.keyoffset;
goto Redraw;
case Kright:
- dstate.offset.x -= vdata.keyoffset;
+ vstate.offset.x -= vdata.keyoffset;
+ goto Redraw;
+ case '.':
+ vstate.zoom += vdata.keyzoom;
+ goto Redraw;
+ case ',':
+ vstate.zoom -= vdata.keyzoom;
goto Redraw;
}
return 0;
--- a/blie.h
+++ b/blie.h
@@ -1,8 +1,10 @@
typedef struct Layer Layer;
typedef struct Editor Editor;
typedef struct Vdata Vdata;
+typedef struct Vstate Vstate;
extern Vdata vdata;
+extern Vstate vstate;
extern int bliedebug;
struct Layer {
@@ -28,15 +30,24 @@
int layerwinwidth; /* width of layers window */
int fontheight; /* height of font */
int keyoffset; /* offset on key input */
+ float keyzoom; /* zoom change */
Image *gray;
};
+struct Vstate {
+ Point offset;
+ float zoom;
+};
+
+/* writes memimage to drawing image, considering pan and zoom */
+void sampleview(Image*, Memimage*);
+
struct Editor {
char *name;
Memimage *(*composite)(Layer*, Memimage*);
Memimage *(*raw)(Layer*);
Memimage *(*mask)(Layer*);
- void (*overlay)(Layer*, Image*, Point);
+ void (*overlay)(Layer*, Image*);
Rectangle (*toolrect)(Layer*);
void (*drawtools)(Layer*, Image*);
int (*drawinput)(Layer*, int, Event);
@@ -50,7 +61,11 @@
int addeditor(Editor*);
Editor *geteditor(char*);
+/* generic composite of layer (using raw) and the composited image
+ * from the previous layer
+ */
Memimage* ecomposite(Layer*, Memimage*);
+
Memimage* gencomposite(Memimage*, Memimage*, Memimage*, Drawop);
Memimage* gencanvas(Memimage*);
Memimage* dupmemimage(Memimage*);
--- a/mkfile
+++ b/mkfile
@@ -6,6 +6,7 @@
blie.$O\
editor.$O\
layer.$O\
+ sample.$O\
util.$O\
p9image.$O\
--- a/p9image.c
+++ b/p9image.c
@@ -123,13 +123,10 @@
}
static void
-p9overlay(Layer *l, Image *i, Point offset)
+p9overlay(Layer *l, Image *i)
{
Data *data;
- Memimage *mi, *tmi;
- Image *tii;
- int nw;
- Rectangle r;
+ Memimage *mi;
p9init(l);
data = (Data*)l->data;
@@ -155,20 +152,8 @@
changecursor(&ccircle, tstate.circle, Pt(-20, -20));
if (!mi)
return;
- r.min.x = r.min.y = 0;
- r.max.x = Dx(i->r);
- r.max.y = Dy(i->r);
- tmi = allocmemimage(r, i->chan);
- memfillcolor(tmi, DBlack);
- memimagedraw(tmi, rectsubpt(mi->r, offset), mi, ZP, nil, ZP, SoverD);
- tii = allocimage(display, r, i->chan, 0, DTransparent);
- nw = tmi->width * Dy(tmi->r) * sizeof(ulong);
- loadimage(tii, tii->r, tmi->data->bdata, nw);
- draw(i, i->r, tii, nil, ZP);
-
- freeimage(tii);
- freememimage(tmi);
+ sampleview(i, mi);
}
static Rectangle
@@ -180,7 +165,6 @@
static void
p9drawtools(Layer *l, Image *i)
{
-
draw(i, insetrect(i->r, 5), display->white, nil, ZP);
}
--- /dev/null
+++ b/sample.c
@@ -1,0 +1,137 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include "blie.h"
+
+/* direct copy from /sys/src/cmd/iconv.c:/^writeuncompressed */
+static void
+writeuncompressed(int fd, Memimage *m)
+{
+ char chanstr[32];
+ int bpl, y, j;
+ uchar *buf;
+
+ if(chantostr(chanstr, m->chan) == nil)
+ sysfatal("can't convert channel descriptor: %r");
+ fprint(fd, "%11s %11d %11d %11d %11d ",
+ chanstr, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);
+
+ bpl = bytesperline(m->r, m->depth);
+ buf = malloc(bpl);
+ if(buf == nil)
+ sysfatal("malloc failed: %r");
+ for(y=m->r.min.y; y<m->r.max.y; y++){
+ j = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), buf, bpl);
+ if(j != bpl)
+ sysfatal("image unload failed: %r");
+ if(write(fd, buf, bpl) != bpl)
+ sysfatal("write failed: %r");
+ }
+ free(buf);
+}
+
+/*
+ * uses external program for resampling in a pipe.
+ * I can see several improvements for performance:
+ *
+ * 1. only scaling what's needed, which means calculating
+ * the rectangle we want, extracting that to a
+ * separate image for resampling.
+ * 2. integrate resample functionality in this program
+ * 3. only resample if zoom level or data changes.
+ * at the moment, it resamples when panning.
+ */
+static Memimage*
+resample(Memimage *src, int nx, int ny)
+{
+ int pin[2];
+ int pout[2];
+ int re, ro;
+ char x[11], y[11];
+ Memimage *tm;
+ Dir *dir;
+
+ snprint(x, sizeof(x), "%d", nx);
+ snprint(y, sizeof(y), "%d", ny);
+
+ if (pipe(pout) < 0) {
+ close(pout[0]);
+ close(pout[1]);
+ goto Err;
+ }
+ if (pipe(pin) < 0) {
+ close(pout[0]);
+ close(pout[1]);
+ close(pin[0]);
+ close(pin[1]);
+ goto Err;
+ }
+
+ dir = dirfstat(pin[0]);
+ if (!dir) {
+ free(dir);
+ goto Err;
+ }
+ dir->length = src->width * Dy(src->r) * sizeof(ulong) + 12 * 5;
+ if (!dirfwstat(pin[0], dir)) {
+ free(dir);
+ goto Err;
+ }
+ free(dir);
+
+ switch (fork()) {
+ case -1: /* error */
+ goto Err;
+ case 0: /* child */
+ dup(pin[1], 0);
+ dup(pout[1], 1);
+ close(pin[0]);
+ close(pout[0]);
+ execl("/bin/resample", "resample", "-x", x, "-y", y, nil);
+ sysfatal("cannot exec: %r");
+ default: /* parent */
+ close(pout[1]);
+ close(pin[1]);
+ re = pout[0];
+ ro = pin[0];
+ }
+
+ writeuncompressed(ro, src);
+ tm = readmemimage(re);
+ if (!tm)
+ goto Err;
+ close(re);
+ return tm;
+Err:
+ fprint(2, "resample: %r\n");
+ return nil;
+}
+
+void
+sampleview(Image *img, Memimage *src)
+{
+ Memimage *tmi;
+ Memimage *tsi;
+ Rectangle r;
+ int nw;
+
+ 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;
+
+ tmi = allocmemimage(r, img->chan);
+ memfillcolor(tmi, DBlack);
+ memimagedraw(tmi, tmi->r, tsi, 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);
+}
binary files a/test/img/layerA/img b/test/img/layerA/img differ
binary files a/test/img/layerA/mask b/test/img/layerA/mask differ
binary files a/test/img/layerB/img b/test/img/layerB/img differ
binary files a/test/img/layerB/mask b/test/img/layerB/mask differ
--- a/words
+++ b/words
@@ -75,6 +75,7 @@
Drawing controls:
- Up, Left, Down, Right: pan image
+- , and .: zoom image
Layer controls: