#include "paint.h" typedef struct Aux { struct { Memimage *mem; Image *img; int pos; /* relative to img->r.min.y */ } slider; struct { Memimage *mem; Image *img; Point pos; /* relative to img->r.min */ Point oldpos; } square; struct { Memimage *mem; Image *img; int pos; /* relative to img->r.min.x */ } opaque; } Aux; static Image *αbg; static Image *lightcursor; static Image *darkcursor; static uchar bgbits[] = { 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, }; static uchar brightness(ulong c) { return 0.30*(c>>24) + 0.59*(c>>16 & 0xFF) + 0.11*(c>>8 & 0xFF); } static ulong r1at(Aux *aux, int p) { return (Dy(aux->slider.img->r) - p - 1) << 24 | 0x0000FF; } static ulong r2at(Aux *aux, Point p) { uchar r, g, b; p.y = Dy(aux->square.img->r) - p.y - 1; r = r1at(aux, aux->slider.pos); g = p.x; b = p.y; return r<<24 | g<<16 | b<<8 | 0xFF; } static uchar g1at(Aux *aux, int p) { return Dy(aux->slider.img->r) - p - 1; } static ulong g2at(Aux *aux, Point p) { uchar r, g, b; p.y = Dy(aux->square.img->r) - p.y - 1; r = p.x; g = g1at(aux, aux->slider.pos); b = p.y; return r<<24 | g<<16 | b<<8 | 0xFF; } static uchar b1at(Aux *aux, int p) { return Dy(aux->slider.img->r) - p - 1; } static ulong b2at(Aux *aux, Point p) { uchar r, g, b; p.y = Dy(aux->square.img->r) - p.y - 1; r = p.x; g = p.y; b = b1at(aux, aux->slider.pos); return r<<24 | g<<16 | b<<8 | 0xFF; } static ulong h1at(Aux *, int p) { ulong hsva; hsva = p << 24 | 0xFFFFFF; return hsvtorgb(hsva); } static ulong h2at(Aux *aux, Point p) { uchar h, s, v; h = aux->slider.pos; s = p.x; v = Dy(aux->square.img->r) - p.y - 1; return hsvtorgb(h<<24 | s<<16 | v<<8 | 0xFF); } static void hset(Aux *aux, ulong rgba) { ulong hsva; uchar h, s, v, a; hsva = rgbtohsv(rgba); h = hsva >> 24; s = hsva >> 16; v = hsva >> 8; a = hsva; aux->slider.pos = h; aux->square.pos.x = s; aux->square.pos.y = Dy(aux->square.img->r) - v - 1; aux->opaque.pos = a; } static ulong (*slidercolor)(Aux *, int) = h1at; static ulong (*squarecolor)(Aux *, Point) = h2at; void setcolorpickercolor(Widget *w, ulong rgba) { Aux *aux; aux = w->aux; hset(aux, rgba); } static void setcolor(Aux *aux, int button) { Brush *brush; brush = getbrush(button); brush->c = setalpha(squarecolor(aux, aux->square.pos), aux->opaque.pos); brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); } static void fillsquare(Aux *aux) { Memimage *mem; Rectangle r; int x, y; ulong c; uchar *p; mem = aux->square.mem; r = mem->r; assert(Dx(r) >= 256); assert(Dy(r) >= 256); for(y = r.min.y; y < r.max.y; y++) { for(x = r.min.x; x < r.max.x; x++) { c = squarecolor(aux, Pt(x, y)); p = byteaddr(mem, Pt(x, y)); p[2] = c >> 24; p[1] = c >> 16; p[0] = c >> 8; } } } static void fillslider(Aux *aux) { Memimage *mem; Rectangle r; int y; ulong c; uchar *p; mem = aux->slider.mem; r = aux->slider.mem->r; assert(Dy(mem->r) >= 256); for(y = r.min.y; y < r.max.y; y++) { c = slidercolor(aux, y); p = byteaddr(mem, Pt(0, y)); p[2] = c >> 24; p[1] = c >> 16; p[0] = c >> 8; } } static void fillopaque(Aux *aux) { Memimage *mem; Rectangle r; int x; ulong c, a; uchar *p; mem = aux->opaque.mem; r = mem->r; assert(Dx(mem->r) >= 256); c = squarecolor(aux, aux->square.pos); for(x = r.min.x; x < r.max.x; x++) { a = setalpha(c, x); p = byteaddr(mem, Pt(x, 0)); p[3] = a >> 24; p[2] = a >> 16; p[1] = a >> 8; p[0] = a; } } static int opaquemouse(Widget *w, Mousectl *mctl) { Aux *aux; Brush *brush, old; int b0; if((mctl->buttons & 7) == 0) return 0; aux = w->aux; brush = getbrush(mctl->buttons); old = *brush; b0 = mctl->buttons; do { aux->opaque.pos = clampint(w->r.min.x, mctl->xy.x, w->r.max.x) - w->r.min.x; setcolor(aux, mctl->buttons); w->up->redraw(w->up, RDinc); readmouse(mctl); } while((mctl->buttons & 7) == b0); if(mctl->buttons == 0) return 0; *brush = old; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); while(mctl->buttons) readmouse(mctl); return 0; } static void redrawopaque(Widget *w, RedrawMode redrawmode) { Aux *aux; Image *cur; aux = w->aux; fillopaque(aux); if(syncimg(aux->opaque.img, aux->opaque.mem, aux->opaque.mem->r) < 0) fprint(2, "sync opaque: %r"); draw(w->screen, w->r, αbg, nil, ZP); draw(w->screen, w->r, aux->opaque.img, nil, ZP); if(aux->opaque.pos >= aux->opaque.img->r.min.x && aux->opaque.pos < aux->opaque.img->r.max.x) { cur = brightness(squarecolor(aux, aux->square.pos)) < 127 ? lightcursor : darkcursor; draw(w->screen, w->r, cur, nil, Pt(-aux->opaque.pos, -Dy(w->r)/2)); } if(redrawmode == RDall) { border(w->screen, w->r, -1, display->black, ZP); drawshadow(w->screen, w->r); } } static Widget * mkopaque(Aux *aux) { Widget *w; if((aux->opaque.mem = allocmemimage(Rect(0, 0, 256, 1), RGBA32)) == nil || (aux->opaque.img = allocimage(display, aux->opaque.mem->r, aux->opaque.mem->chan, 1, DNofill)) == nil ) sysfatal("alloc opaque: %r"); aux->opaque.pos = 0.9 * Dx(aux->opaque.mem->r); if((w = mkwidget(Rect(0, 0, 256, 16), opaquemouse, widgetsyncnop, redrawopaque, widgetdimsnop, widgetresizenop, freewidget, aux )) == nil) sysfatal("mkopaque: mkwidget: %r"); return w; } static int slidermouse(Widget *w, Mousectl *mctl) { Aux *aux; Brush *brush, old; int b0; if((mctl->buttons & 7) == 0) return 0; aux = w->aux; brush = getbrush(mctl->buttons); old = *brush; b0 = mctl->buttons; do { aux->slider.pos = clampint(w->r.min.y, mctl->xy.y, w->r.max.y) - w->r.min.y; setcolor(aux, mctl->buttons); w->up->redraw(w->up, RDinc); readmouse(mctl); } while((mctl->buttons & 7) == b0); if(mctl->buttons == 0) return 0; *brush = old; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); while(mctl->buttons) readmouse(mctl); return 0; } static void redrawslider(Widget *w, RedrawMode redrawmode) { Aux *aux; Image *cur; aux = w->aux; draw(w->screen, w->r, aux->slider.img, nil, ZP); if(aux->slider.pos >= aux->slider.img->r.min.y && aux->slider.pos < aux->slider.img->r.max.y) { cur = brightness(slidercolor(aux, aux->slider.pos)) < 127 ? lightcursor : darkcursor; draw(w->screen, w->r, cur, nil, Pt(-Dx(w->r)/2, -aux->slider.pos)); } if(redrawmode == RDall) { border(w->screen, w->r, -1, display->black, ZP); drawshadow(w->screen, w->r); } } static Widget * mkslider(Aux *aux) { Widget *w; if((aux->slider.mem = allocmemimage(Rect(0, 0, 1, 256), RGB24)) == nil || (aux->slider.img = allocimage(display, aux->slider.mem->r, aux->slider.mem->chan, 1, DNofill)) == nil ) sysfatal("alloc slider: %r"); fillslider(aux); if(syncimg(aux->slider.img, aux->slider.mem, aux->slider.mem->r) < 0) sysfatal("sync slider: %r"); aux->slider.pos = 127; if((w = mkwidget(Rect(0, 0, 16, 256), slidermouse, widgetsyncnop, redrawslider, widgetdimsnop, widgetresizenop, freewidget, aux )) == nil) sysfatal("mkslider: mkwidget: %r"); return w; } static int squaremouse(Widget *w, Mousectl *mctl) { Aux *aux; Brush *brush, old; int b0; if((mctl->buttons & 7) == 0) return 0; aux = w->aux; brush = getbrush(mctl->buttons); old = *brush; b0 = mctl->buttons; do { aux->square.pos = clamppt(subpt(mctl->xy, w->r.min), aux->square.img->r); setcolor(aux, mctl->buttons); w->up->redraw(w->up, RDinc); readmouse(mctl); } while((mctl->buttons & 7) == b0); if(mctl->buttons == 0) return 0; *brush = old; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); while(mctl->buttons) readmouse(mctl); return 0; } static void redrawsquare(Widget *w, RedrawMode redrawmode) { Aux *aux; Image *cur; aux = w->aux; fillsquare(aux); if(syncimg(aux->square.img, aux->square.mem, aux->square.mem->r) < 0) sysfatal("sync square: %r"); if(redrawmode == RDall) { border(w->screen, w->r, -1, display->black, ZP); drawshadow(w->screen, w->r); } draw(w->screen, w->r, aux->square.img, nil, ZP); if(ptinrect(aux->square.pos, aux->square.img->r)) { cur = brightness(squarecolor(aux, aux->square.pos)) < 127 ? lightcursor : darkcursor; draw(w->screen, w->r, cur, nil, subpt(ZP, aux->square.pos)); } aux->square.oldpos = aux->square.pos; } static Widget * mksquare(Aux *aux) { Widget *w; if((aux->square.mem = allocmemimage(Rect(0, 0, 256, 256), RGB24)) == nil || (aux->square.img = allocimage(display, aux->square.mem->r, aux->square.mem->chan, 0, DNofill)) == nil ) sysfatal("alloc square: %r"); aux->square.pos = Pt(-1, -1); aux->square.oldpos = ZP; if((w = mkwidget(aux->square.mem->r, squaremouse, widgetsyncnop, redrawsquare, widgetdimsnop, widgetresizenop, freewidget, aux )) == nil) sysfatal("mkcolorpickerwidget: mkwidget: %r"); return w; } static Image * mkcursor(Image *color, Image *outline) { Image *cur; if((cur = allocimage(display, Rect(-10, -10, 10, 10), RGBA32, 0, DTransparent)) == nil) return nil; ellipse(cur, ZP, 2, 2, 0, outline, ZP); ellipse(cur, ZP, 4, 4, 1, color, ZP); ellipse(cur, ZP, 6, 6, 0, outline, ZP); return cur; } static void wfree(Widget *w) { Aux *aux; aux = w->aux; freememimage(aux->slider.mem); freeimage(aux->slider.img); freememimage(aux->square.mem); freeimage(aux->square.img); freememimage(aux->opaque.mem); freeimage(aux->opaque.img); free(w->aux); freewidget(w); } Widget * mkcolorpickerwidget(ulong rgba) { Widget *w, *slider, *square, *opaque; Aux *aux; Rectangle r; if(αbg == nil) { if((αbg = allocimage(display, Rect(0, 0, 8, 8), GREY1, 1, DNofill)) == nil) sysfatal("mkcolorpickerwidget: alloc bg: %r"); if(loadimage(αbg, αbg->r, bgbits, sizeof bgbits) < 0) sysfatal("mkcolorpickerwidget: load αbg: %r"); if((lightcursor = mkcursor(display->white, display->black)) == nil || (darkcursor = mkcursor(display->black, display->white)) == nil ) sysfatal("mkcolorpickerwidget: mkcursor: %r"); } aux = emalloc(sizeof(*aux)); slider = mkslider(aux); square = mksquare(aux); opaque = mkopaque(aux); r = square->r; r.max = addpt(r.max, Pt(14+16, 14+16)); w = mkwidget(r, widgetmousedescend, widgetsyncnop, widgetredrawdescend, widgetdimsnop, widgetresizenop, wfree, aux ); if(w == nil) sysfatal("mkcolorpickerwidget: %r"); addchildwidget(w, slider, Pt(270, 0)); addchildwidget(w, square, ZP); addchildwidget(w, opaque, Pt(0, 270)); setcolorpickercolor(w, rgba); return w; }