shithub: pain

ref: 1b81533742fa32d4ba26f7fcacb942becfd89c50
dir: /main.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

#include "dat.h"
#include "fns.h"

#define MAXLAYERS 10
#define BACKGROUNDCOLOR 0x22272EFF
#define DEFAULTLAYERCOLOR DWhite
#define CLEARCOLOR DTransparent
#define FPS 30
#define UPDATEPERIOD (1000/FPS)
#define Ar(r) (Dx(r)*Dy(r))
#define RECTOPPT(r, op)	((Rectangle){op(r.min),op(r.max)})
#define DEFAULTCANVASSIZE RECTANGLE(640, 640)

int CanvasMoved = 1;
Rectangle CanvasSize;
Rectangle ZoomedSize;
Point CanvasAt = {0};
Layer * RootLayer = nil;
Layer * CurrentLayer = nil;
Image * Background = nil;
Image * ClearImage = nil;
int CurrentKeyboard = 0;
Image * BrushImage = nil;
Image * ViewImage = nil;
Image * ZoomedImage = nil;
ulong LastUpdate = 0;
int DrawAllLayers = 0;
int Zoom = 1;
int ZoomSensitivity = 1;
Mouse CurrentMouse = {0};
Mouse PastMouse = {0};
Point MousePosition = {0};
Point PastMousePosition = {0};
int RunLoop = 1;

Layer *
newlayer(ulong col)
{
	Layer * l = nil, * e = nil;

	USED(l);
	USED(e);

	l = calloc(1, sizeof(Layer));
	if (l == nil)
		sysfatal("newlayer: failed to allocate layer");
	l->changed = 1;
	l->image = allocimage(display, CanvasSize, RGBA32, 1, col);
	if (l->image == nil)
		sysfatal("newlayer: %r");
	if (RootLayer == nil) {
		RootLayer = l;
		CurrentLayer = l;
	} else {
		for (e = RootLayer; e->next != nil; e = e->next)
			;
		e->next = l;
		l->prev = e;
	}
	return l;
}

void
drawcanvas(void)
{
	int sr;
	Rectangle vr;

	sr = 0;
	if (ViewImage == nil) {
		ViewImage = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTLAYERCOLOR);
		if (ViewImage == nil)
			sysfatal("v allocimage: %r");
		sr = 1;
	}

	if (ZoomedImage == nil) {
		ZoomedImage = allocimage(display, ZoomedSize, RGBA32, 1, DNofill);
		if (ZoomedImage == nil)
			sysfatal("z allocimage: %r");
		sr = 1;
	}

	for (Layer * l = RootLayer; l != nil; l = l->next)
		if (sr || l->changed || DrawAllLayers) {
			draw(ViewImage, ViewImage->r, l->image, nil, ZP);
			l->changed = 0;
		}

	if (Background == nil)
		setbackground(BACKGROUNDCOLOR);

	vr = rectaddpt(ZoomedSize, CanvasAt);
	if (rectclip(&vr, Rect(0, 0, Dx(screen->r), Dy(screen->r))) && ((CanvasMoved && Ar(vr) != Ar(ZoomedSize)) || sr)) {
		vr = rectsubpt(vr, CanvasAt);
		if (resizeimage(ZoomedImage, vr, Zoom, ViewImage, ZP) < 0)
			sysfatal("resizeimage: %r");
	}
	if (CanvasMoved) {
		draw(screen, screen->r, Background, nil, ZP);
		draw(screen, RECTOPPT(ZoomedSize, screentoglobalatcanvaspt), ZoomedImage, nil, ZP);
	}
	CanvasMoved = 0;
	DrawAllLayers = 0;
}

ulong
msec(void)
{
	return (ulong)(nsec()/1000);
}

void
setbackground(ulong col)
{	
	if (Background != nil)
		freeimage(Background);

	Background = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, col);
	if (Background == nil)
		sysfatal("setbackground: %r");
}

void
zoom(int z)
{
	int mc;
	int osx, osy;
	Point c;
	
	mc = Zoom > 1;
	if ((Zoom + z) <= 1)
		Zoom = 1;
	else
		Zoom += z;
 
	c = subpt(CanvasAt, MousePosition);
getsize:
	osx = Dx(ZoomedSize);
	osy = Dy(ZoomedSize);
	
	if ((osx == 0 || osy == 0) && 
		Dx(CanvasSize) != 0 && Dy(CanvasSize) != 0) {
		ZoomedSize = CanvasSize;
		goto getsize;
	}

	ZoomedSize.min = mulpt(ZoomedSize.min, Zoom);
	ZoomedSize.max = mulpt(ZoomedSize.max, Zoom);
	
	if (ZoomedImage != nil) {
		freeimage(ZoomedImage);
		ZoomedImage = nil;
	}
	if (mc || Zoom > 1)
		CanvasAt = addpt(
			Pt(Dx(ZoomedSize)*c.x/osx, Dy(ZoomedSize)*c.y/osy), 
			MousePosition
		);
 	DrawAllLayers = 1;
}

void
stroke()
{
	Point t, f;
	Rectangle viewAtCanvas = {0};

	f = PastMousePosition;
	t = MousePosition;

	viewAtCanvas = rectaddpt(ZoomedSize, CanvasAt);
	if (!ptinrect(t, viewAtCanvas))
		return;

	if (!ptinrect(f, viewAtCanvas))
		f = t;

	if (BrushImage == nil)
		setbrushcolor(DBlack);

	f = screentocanvaspt(subpt(f, viewAtCanvas.min));
	t = screentocanvaspt(subpt(t, viewAtCanvas.min));
	line(CurrentLayer->image, f, t, Enddisc, Enddisc, 1, BrushImage, ZP);
	f = mulpt(f, Zoom);
	t = mulpt(t, Zoom);
	if (ZoomedImage != nil)
		line(ZoomedImage, f, t, Enddisc, Enddisc, 1*Zoom, BrushImage, ZP);
	f = screentoglobalpt(f);
	t = screentoglobalpt(t);
	line(screen, f, t, Enddisc, Enddisc, 1*Zoom, BrushImage, ZP);
	
}

Point
globaltoscreenpt(Point p)
{
	return subpt(p, screen->r.min);
}

Point
globaltoscreenatcanvaspt(Point p)
{
	return addpt(globaltoscreenpt(p), CanvasAt);
}

Point
screentocanvaspt(Point p)
{
	return	(Point){(p.x * (Dx(CanvasSize)) / Dx(ZoomedSize)),
					(p.y * (Dy(CanvasSize)) / Dy(ZoomedSize))};
}

Point
canvastoscreenpt(Point p)
{
	return	(Point){(p.x * (Dx(ZoomedSize)) / Dx(CanvasSize)),
					(p.y * (Dy(ZoomedSize)) / Dy(CanvasSize))};
}

Point
screentoglobalpt(Point p)
{
	return addpt(p, screen->r.min);
}

Point
screentoglobalatcanvaspt(Point p)
{
	return addpt(screentoglobalpt(p), CanvasAt);
}

void
clearlayer(Layer * l)
{
	if (l->image != nil)
		freeimage(l->image);
	if ((l->image = allocimage(display, CanvasSize, RGBA32, 1, 
		l == RootLayer ? DEFAULTLAYERCOLOR : DTransparent)) == nil)
		sysfatal("clearlayer: %r");
	l->changed = 1;
	if (ViewImage != nil) {
		freeimage(ViewImage);
		ViewImage = nil;
	}
	DrawAllLayers = 1;
}

void
setbrushimage(Image * i)
{
	if (BrushImage == nil)
		freeimage(BrushImage);
	BrushImage = i;
}

void
setbrushcolor(ulong c)
{
	Image * i;
	
	i = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, c);
	if (i == nil)
		sysfatal("setbrushcolor: %r");
	setbrushimage(i);
}

void
setcanvassize(Rectangle r)
{
	CanvasSize = r;
	Image * i = nil;

	USED(i);
	for (Layer * l = RootLayer; l != nil; l = l->next) {
		i = allocimage(display, r, RGBA32, 1, DTransparent);
		if (i == nil)
			sysfatal("setcanvassize: %r");
		draw(i, i->r, l->image, nil, ZP);
	}
}

static void
handlekeyboard(int kbdc)
{
	runbindings(BKeyboard, CurrentKeyboard = kbdc);
}

static void
handlemouse(Mouse mouse)
{
	CurrentMouse = mouse;
	MousePosition = globaltoscreenpt(mouse.xy);
	runbindings(BMouse, mouse.buttons);
	PastMousePosition = MousePosition;
}

void
eresized(int)
{	
	if(getwindow(display, Refnone) < 0)
		sysfatal("getwindow: %r");
	setbackground(BACKGROUNDCOLOR);
	CanvasMoved = 1;
	drawcanvas();
}

void
main(int argc, char * argv[])
{
	int d;
	Event e;

	USED(argc);
	USED(argv);

	if (initdraw(nil, nil, "pain") < 0)
		sysfatal("initdraw: %r\n");

	if (fsinit("pain") < 1)
		sysfatal("fsinit: %r");

	if (!atexit(fsclose))
		sysfatal("atexit: %r");
	
	setcanvassize(DEFAULTCANVASSIZE);
	newlayer(DEFAULTLAYERCOLOR);
	zoom(0);
	setdefaultbindings();

	einit(Emouse | Ekeyboard);
	drawcanvas();
	for(;RunLoop;) {
		d = msec()-LastUpdate;
	 	if (d <= UPDATEPERIOD)
	 		sleep(d - UPDATEPERIOD);

		switch (event(&e))
		{
		case Ekeyboard: 
			handlekeyboard(e.kbdc);
			break;
		case Emouse:
			handlemouse(e.mouse);
			break; 
		}

		drawcanvas();
		LastUpdate = msec();
	}

	return;
}