ref: 1b81533742fa32d4ba26f7fcacb942becfd89c50
dir: /main.c/
#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;
}