shithub: blie

ref: a3477b9d29227db6e78cede76fdd9d6b83f5ea76
dir: /p9image.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <event.h>
#include <cursor.h>
#include "blie.h"

Cursor ccircle = {
	{-7, -7},
	{0xFF, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07,
	 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07,
	 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07,
	 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xFF},
	{0x00, 0x00, 0x7f, 0xfe, 0x40, 0x02, 0x40, 0x02,
	 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
	 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
	 0x40, 0x02, 0x40, 0x02, 0x7f, 0xfe, 0x00, 0x00}
};

Point toolcell;

typedef struct Data Data;
struct Data {
	Memimage *img;
	Memimage *mask;
	Drawop op;
};

typedef enum {
	Composite,
	Img,
	Mask,
} Mode;

enum {
	DTimg = 0,
	DTmask,
};

typedef struct Tstate Tstate;
struct Tstate {
	Mode mode;
	int drawtarget;
	Image *circle;
	Memimage *circlebrush;
	Memimage *colorbrush;
	int brushrad;
};
Tstate tstate;

static double
distance(Point p, Point q)
{
	double n;
	p = subpt(q, p);
	p.x *= p.x;
	p.y *= p.y;
	n = sqrt((double)p.x + (double)p.y);
	return n;
}

static int
isaturate(int a)
{
	return a < 0 ? 0 : (a > 255 ? 255 : a);
}

static void
setcirclebrush(int r, double exp)
{
	int x, y, d, n;
	double dist;
	
	d = r*2 + 1;
	if (tstate.circlebrush)
		freememimage(tstate.circlebrush);
	tstate.brushrad = r;
	tstate.circlebrush = allocmemimage(Rect(0, 0, d, d), GREY8);
	tstate.circlebrush->flags |= Fbytes|Falpha;
	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);
			
			n = (int)(dist * 256.);
			*byteaddr(tstate.circlebrush, Pt(x, y)) = isaturate(n);
		}
}

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;
	tstate.drawtarget = DTimg;
	
	toolcell = Pt(15, vdata.fontheight + 4);
	
	if (headless)
		return;
	
	tstate.circle = allocimage(display, Rect(0, 0, 41, 41), RGBA32, 0, DTransparent);
	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.);
	setcolorbrush(DRed);
}

static void
p9init(Layer *l)
{
	int fd;
	char *s;
	Data *d;
	
	if (l->data)
		return;
	d = mallocz(sizeof(Data), 1);
	l->data = d;
	
	/* image file */
	s = smprint("l/%s/img", l->name);
	fd = open(s, OREAD);
	if (fd < 0) {
		free(s);
		return;
	}
	free(s);
	
	seek(fd, 0, 0);
	d->img = creadmemimage(fd);
	if (!d->img) {
		seek(fd, 0, 0);
		d->img = readmemimage(fd);
	}
	close(fd);
	
	/* mask file */
	s = smprint("l/%s/mask", l->name);
	fd = open(s, OREAD);
	if (fd < 0) {
		free(s);
		return;
	}
	free(s);
	
	seek(fd, 0, 0);
	d->mask = creadmemimage(fd);
	if (!d->mask) {
		seek(fd, 0, 0);
		d->mask = readmemimage(fd);
	}
	close(fd);
}

/* just use ecompose, which uses raw() and mask() */
static Memimage*
p9composite(Layer *l, Memimage *img)
{
	Data *d;
	
	p9init(l);
	d = (Data*)l->data;
	
	if (!img) {
		fprint(2, "%s: return input image: %p\n", l->name, d->img);
		return dupmemimage(d->img);
	}
	
	fprint(2, "%s: return composite image: %p %p %p\n", l->name,
		img, d->img, d->mask);
	return gencomposite(img, d->img, d->mask, l->op);
}

static Memimage*
p9raw(Layer *l)
{
	Data *d;
	p9init(l);
	d = (Data*)l->data;
	return d->img;
}

static Memimage*
p9mask(Layer *l)
{
	Data *d;
	p9init(l);
	d = (Data*)l->data;
	return d->mask;
}

static int
p9overlay(Layer *l, Image *i)
{
	Data *data;
	Memimage *mi;
	
	p9init(l);
	data = (Data*)l->data;
	
	if (!i)
		return tstate.mode != Composite;
	
	switch (tstate.mode) {
	case Composite:
		break;
	case Img:
		mi = data->img;
		goto Mout;
	case Mask:
		mi = data->mask;
		goto Mout;
	}
	changecursor(nil, nil, ZP);
	return 0;
Mout:
	changecursor(&ccircle, tstate.circle, Pt(-20, -20));
	if (!mi)
		return 0;
	
	setdrawingdirty(Dcontent);
	sampleview(i, mi, 0);
	return 0;
}

static Rectangle
p9toolrect(Layer *l)
{
	return Rect(0, 0, 200, 50);
}

static void
drcells(Image *i, Point p, char *s, int hl)
{
	Rectangle r;
	r.min = p;
	r.max = addpt(p, toolcell);
	border(i, r, 1, vdata.gray, ZP);
	if (hl) {
		r = insetrect(r, 2);
		draw(i, r, vdata.gray, nil, ZP);
	}
	string(i, addpt(p, Pt(2, 2)), display->black, ZP, font, s);
}

static void
p9drawtools(Layer *l, Image *i)
{
	Point p;
	p = i->r.min;
	draw(i, i->r, display->white, nil, ZP);
	
	drcells(i, p, "C", tstate.mode == Composite);
	p.x += toolcell.x;
	drcells(i, p, "S", tstate.mode == Img);
	p.x += toolcell.x;
	drcells(i, p, "M", tstate.mode == Mask);
}

static int
p9savedata(Layer *l)
{
	p9init(l);
	return 1;
}

static Redrawwin
drawbrush(Layer *l, int buttons, Point xy)
{
	Memimage *tgt;
	Data *d;
	Rectangle r;
	
	p9init(l);
	d = (Data*)l->data;
	
	if (!buttons)
		return Rnil;
	
	tgt = nil;
	if (tstate.drawtarget == DTimg)
		tgt = d->img;
	else if (tstate.drawtarget == DTmask)
		tgt = d->mask;
	
	if (!tgt)
		return Rnil;
	
	r = insetrect(tgt->r, -tstate.brushrad);
	if (!ptinrect(xy, r))
		return Rnil;
	
	r = rectaddpt(tstate.circlebrush->r,
		subpt(
			addpt(tgt->r.min, xy),
			Pt(tstate.brushrad, tstate.brushrad)
		)
	);
	
	tstate.colorbrush->clipr = tstate.circlebrush->r;
	memimagedraw(tgt, r, tstate.colorbrush, ZP, tstate.circlebrush, ZP, SoverD);
	setdrawingdirty(Dcontent);
	dirtylayer(l);
	return Rdrawing;
}

static Redrawwin
p9drawinput(Layer *l, int e, Event ev)
{
	p9init(l);
	
	switch (e) {
	case Ekeyboard:
		break;
	case Emouse:
		return drawbrush(l, ev.mouse.buttons, ev.mouse.xy);
		break;
	}
	return Rnil;
}

static Redrawwin
p9toolinput(Layer *l, int e, Event ev)
{
	p9init(l);
	if (e != Emouse)
		return Rnil;
	
	if (!ev.mouse.buttons)
		return Rnil;
	
	if (ev.mouse.xy.y / toolcell.y == 0) {
		switch (ev.mouse.xy.x / toolcell.x) {
		case 0:
			tstate.mode = Composite;
			tstate.drawtarget = DTimg;
			goto Out;
		case 1:
			tstate.mode = Img;
			tstate.drawtarget = DTimg;
			goto Out;
		case 2:
			tstate.mode = Mask;
			tstate.drawtarget = DTmask;
			goto Out;
		}
	}
	return Rnil;
Out:
	setdrawingdirty(Dcontent);
	return Rdrawing|Rtools;
}

Editor p9image = {
	.name = "p9img",
	.init = p9initialize,
	.raw = p9raw,
	.mask = p9mask,
	.overlay = p9overlay,
	.toolrect = p9toolrect,
	.drawtools = p9drawtools,
	.savedata = p9savedata,
	.drawinput = p9drawinput,
	.toolinput = p9toolinput,
};