ref: 37d050563f02063f183566ee75c767da1e4e70be
dir: /p9image.c/
#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("%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("%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); 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; xy = scalepos(xy); 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; return Rdrawing|Rtools; case 1: tstate.mode = Img; tstate.drawtarget = DTimg; return Rdrawing|Rtools; case 2: tstate.mode = Mask; tstate.drawtarget = DTmask; return Rdrawing|Rtools; } } return Rnil; } Editor p9image = { .name = "p9img", .init = p9initialize, .raw = p9raw, .mask = p9mask, .overlay = p9overlay, .toolrect = p9toolrect, .drawtools = p9drawtools, .savedata = p9savedata, .drawinput = p9drawinput, .toolinput = p9toolinput, };