]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/paint.c
devproc: can't wait for ourselfs to stop (thanks Shamar)
[plan9front.git] / sys / src / cmd / paint.c
index a24a70fbe4b9f74028d26e570d8840d6e338796c..4b2087de252ebf3738a7f366cb6d139cdcf4934a 100644 (file)
@@ -15,6 +15,7 @@ Image *back;
 Image *pal[16];                /* palette */
 Rectangle palr;                /* palette rect on screen */
 Rectangle penr;                /* pen size rect on screen */
+
 enum {
        NBRUSH = 10+1,
 };
@@ -41,6 +42,29 @@ int c64[] = {                /* c64 color palette */
        0x959595,
 };
 
+/*
+ * get bounding rectnagle for stroke from r.min to r.max with
+ * specified brush (size).
+ */
+static Rectangle
+strokerect(Rectangle r, int brush)
+{
+       r = canonrect(r);
+       return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1);
+}
+
+/*
+ * draw stroke from r.min to r.max to dst with color ink and
+ * brush (size).
+ */
+static void
+strokedraw(Image *dst, Rectangle r, Image *ink, int brush)
+{
+       if(!eqpt(r.min, r.max))
+               line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP);
+       fillellipse(dst, r.max, brush, brush, ink, ZP);
+}
+
 /*
  * A draw operation that touches only the area contained in bot but not in top.
  * mp and sp get aligned with bot.min.
@@ -259,30 +283,43 @@ restore(int n)
                if((tmp = undo[x]) == nil)
                        return;
                undo[x] = nil;
-               expand(tmp->r);
-               draw(canvas, tmp->r, tmp, nil, tmp->r.min);
-               update(&tmp->r);
-               freeimage(tmp);
+               if(canvas == nil || canvas->chan != tmp->chan){
+                       freeimage(canvas);
+                       canvas = tmp;
+                       update(nil);
+               } else {
+                       expand(tmp->r);
+                       draw(canvas, tmp->r, tmp, nil, tmp->r.min);
+                       update(&tmp->r);
+                       freeimage(tmp);
+               }
        }
 }
 
 typedef struct {
        Rectangle       r;
        Rectangle       r0;
-       Image           *iscan;
-       uchar           *bscan;
-       uchar           *bmask;
-       int             wmask;
-       int             wscan;
-       int             yscan;
-       uchar           color[4];
-} Floodfill;
+       Image*          dst;
+
+       int             yscan;  /* current scanline */
+       int             wscan;  /* bscan width in bytes */
+       Image*          iscan;  /* scanline image */
+       uchar*          bscan;  /* scanline buffer */
+
+       int             nmask;  /* size of bmask in bytes */
+       int             wmask;  /* width of bmask in bytes */
+       Image*          imask;  /* mask image */
+       uchar*          bmask;  /* mask buffer */
+
+       int             ncmp;
+       uchar           bcmp[4];
+} Filldata;
 
 void
-floodscan(Floodfill *f, Point p0)
+fillscan(Filldata *f, Point p0)
 {
-       uchar *b;
        int x, y;
+       uchar *b;
 
        x = p0.x;
        y = p0.y;
@@ -291,18 +328,19 @@ floodscan(Floodfill *f, Point p0)
                return;
 
        if(f->yscan != y){
+               draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y));
+               if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0)
+                       return;
                f->yscan = y;
-               draw(f->iscan, f->iscan->r, canvas, nil, Pt(f->r.min.x, f->r.min.y+y));
-               unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan);
        }
 
        for(x = p0.x; x >= 0; x--){
-               if(memcmp(f->bscan + x*3, f->color, 3))
+               if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
                        break;
                b[x/8] |= 0x80>>(x%8);
        }
        for(x = p0.x+1; x < f->r0.max.x; x++){
-               if(memcmp(f->bscan + x*3, f->color, 3))
+               if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
                        break;
                b[x/8] |= 0x80>>(x%8);
        }
@@ -312,12 +350,12 @@ floodscan(Floodfill *f, Point p0)
                for(x = p0.x; x >= 0; x--){
                        if((b[x/8] & 0x80>>(x%8)) == 0)
                                break;
-                       floodscan(f, Pt(x, y));
+                       fillscan(f, Pt(x, y));
                }
                for(x = p0.x+1; x < f->r0.max.x; x++){
                        if((b[x/8] & 0x80>>(x%8)) == 0)
                                break;
-                       floodscan(f, Pt(x, y));
+                       fillscan(f, Pt(x, y));
                }
        }
 
@@ -326,54 +364,62 @@ floodscan(Floodfill *f, Point p0)
                for(x = p0.x; x >= 0; x--){
                        if((b[x/8] & 0x80>>(x%8)) == 0)
                                break;
-                       floodscan(f, Pt(x, y));
+                       fillscan(f, Pt(x, y));
                }
                for(x = p0.x+1; x < f->r0.max.x; x++){
                        if((b[x/8] & 0x80>>(x%8)) == 0)
                                break;
-                       floodscan(f, Pt(x, y));
+                       fillscan(f, Pt(x, y));
                }
        }
 }
 
 void
-floodfill(Point p, Image *fill)
+floodfill(Image *dst, Rectangle r, Point p, Image *src)
 {
-       Floodfill f;
-       Image *imask;
-       int nmask;
+       Filldata f;
 
-       f.r = canvas->r;
-       f.r0 = rectsubpt(f.r, f.r.min);
-       f.wmask = bytesperline(f.r0, 1);
-       nmask = f.wmask*f.r0.max.y;
-       if((f.bmask = mallocz(nmask, 1)) == nil)
+       if(!rectclip(&r, dst->r))
                return;
-       if((f.iscan = allocimage(display, Rect(0, 0, f.r0.max.x, 1), RGB24, 0, DNofill)) == nil){
-               free(f.bmask);
+       if(!ptinrect(p, r))
                return;
-       }
+       memset(&f, 0, sizeof(f));
+       f.dst = dst;
+       f.r = r;
+       f.r0 = rectsubpt(r, r.min);
+       f.wmask = bytesperline(f.r0, 1);
+       f.nmask = f.wmask*f.r0.max.y;
+       if((f.bmask = mallocz(f.nmask, 1)) == nil)
+               goto out;
+       if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil)
+               goto out;
+
+       r = f.r0;
+       r.max.y = 1;
+       if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil)
+               goto out;
        f.yscan = -1;
-       f.wscan = bytesperline(f.iscan->r, 24);
-       if((f.bscan = mallocz(f.wscan, 1)) == nil){
-               freeimage(f.iscan);
-               free(f.bmask);
-               return;
-       }
-       memset(f.color, 0, sizeof(f.color));
-       draw(f.iscan, Rect(0,0,1,1), canvas, nil, p);
-       unloadimage(f.iscan, Rect(0,0,1,1), f.color, sizeof(f.color));
-       floodscan(&f, subpt(p, f.r.min));
-       freeimage(f.iscan);
-       free(f.bscan);
-       if((imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil){
-               free(f.bmask);
-               return;
-       }
-       loadimage(imask, imask->r, f.bmask, nmask);
+       f.wscan = bytesperline(f.iscan->r, f.iscan->depth);
+       if((f.bscan = mallocz(f.wscan, 0)) == nil)
+               goto out;
+
+       r = Rect(0,0,1,1);
+       f.ncmp = (f.iscan->depth+7) / 8;
+       draw(f.iscan, r, dst, nil, p);
+       if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0)
+               goto out;
+
+       fillscan(&f, subpt(p, f.r.min));
+
+       loadimage(f.imask, f.imask->r, f.bmask, f.nmask);
+       draw(f.dst, f.r, src, f.imask, f.imask->r.min);
+out:
        free(f.bmask);
-       draw(canvas, canvas->r, fill, imask, imask->r.min);
-       freeimage(imask);
+       free(f.bscan);
+       if(f.iscan)
+               freeimage(f.iscan);
+       if(f.imask)
+               freeimage(f.imask);
 }
 
 void
@@ -434,19 +480,21 @@ drawpal(void)
 
        r = penr;
        draw(screen, r, back, nil, ZP);
-       for(i=0; i<10; i++){
+       for(i=0; i<NBRUSH; i++){
                r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
                rr = r;
                if(i == brush)
                        rr.min.y += Dy(r)/3;
-               fillellipse(screen, addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)), i, i, ink, ZP);
+               if(i == NBRUSH-1){
+                       /* last is special brush for fill draw */
+                       draw(screen, rr, ink, nil, ZP);
+               } else {
+                       rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2));
+                       rr.max = rr.min;
+                       strokedraw(screen, rr, ink, i);
+               }
                r.min.x = r.max.x;
        }
-       r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
-       rr = r;
-       if(i == brush)
-               rr.min.y += Dy(r)/3;
-       draw(screen, rr, ink, nil, ZP);
 
        r = palr;
        for(i=1; i<=nelem(pal); i++){
@@ -514,7 +562,7 @@ pipeline(char *fmt, ...)
        va_end(a);
        if(pipe(p) < 0)
                return -1;
-       switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){
+       switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG|RFREND)){
        case -1:
                close(p[0]);
                close(p[1]);
@@ -598,7 +646,7 @@ main(int argc, char *argv[])
                                /* no break */
                        case 1:
                                p = s2c(e.mouse.xy);
-                               if(brush >= 10){
+                               if(brush == NBRUSH-1){
                                        /* flood fill brush */
                                        if(canvas == nil || !ptinrect(p, canvas->r)){
                                                back = img;
@@ -606,18 +654,20 @@ main(int argc, char *argv[])
                                                update(nil);
                                                break;
                                        }
-                                       save(canvas->r, 1);
-                                       floodfill(p, img);
-                                       update(nil);
+                                       r = canvas->r;
+                                       save(r, 1);
+                                       floodfill(canvas, r, p, img);
+                                       update(&r);
+
                                        /* wait for mouse release */
                                        while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0)
                                                ;
                                        break;
                                }
-                               r = Rect(p.x-brush, p.y-brush, p.x+brush+1, p.y+brush+1);
+                               r = strokerect(Rpt(p, p), brush);
                                expand(r);
                                save(r, 1);
-                               fillellipse(canvas, p, brush, brush, img, ZP);
+                               strokedraw(canvas, Rpt(p, p), img, brush);
                                update(&r);
                                for(;;){
                                        m = e.mouse;
@@ -628,14 +678,10 @@ main(int argc, char *argv[])
                                        d = s2c(e.mouse.xy);
                                        if(eqpt(d, p))
                                                continue;
-                                       r = canonrect(Rpt(p, d));
-                                       r.min.x -= brush;
-                                       r.min.y -= brush;
-                                       r.max.x += brush+1;
-                                       r.max.y += brush+1;
+                                       r = strokerect(Rpt(p, d), brush);
                                        expand(r);
                                        save(r, 0);
-                                       line(canvas, p, d, Enddisc, Enddisc, brush, img, ZP);
+                                       strokedraw(canvas, Rpt(p, d), img, brush);
                                        update(&r);
                                        p = d;
                                }
@@ -659,10 +705,12 @@ main(int argc, char *argv[])
                                center();
                                break;
                        case '+':
-                               setzoom(e.mouse.xy, zoom*2);
+                               if(zoom < 0x1000)
+                                       setzoom(e.mouse.xy, zoom*2);
                                break;
                        case '-':
-                               setzoom(e.mouse.xy, zoom/2);
+                               if(zoom > 1)
+                                       setzoom(e.mouse.xy, zoom/2);
                                break;
                        case 'c':
                                if(canvas == nil)
@@ -676,7 +724,7 @@ main(int argc, char *argv[])
                                restore(16);
                                break;
                        case 'f':
-                               brush = 10;
+                               brush = NBRUSH-1;
                                drawpal();
                                break;
                        case '0': case '1': case '2': case '3': case '4':