]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/paint.c
rio: fix goodrect() bug (thanks mike)
[plan9front.git] / sys / src / cmd / paint.c
index 15f7b8729f348817dad2ae8cef197face80b8974..4b2087de252ebf3738a7f366cb6d139cdcf4934a 100644 (file)
@@ -6,7 +6,7 @@
 
 char *filename;
 int zoom = 1;
-int thick = 1;
+int brush = 1;
 Point spos;            /* position on screen */
 Point cpos;            /* position on canvas */
 Image *canvas;
@@ -16,6 +16,10 @@ Image *pal[16];              /* palette */
 Rectangle palr;                /* palette rect on screen */
 Rectangle penr;                /* pen size rect on screen */
 
+enum {
+       NBRUSH = 10+1,
+};
+
 int nundo = 0;
 Image *undo[1024];
 
@@ -38,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.
@@ -256,13 +283,145 @@ 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*          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
+fillscan(Filldata *f, Point p0)
+{
+       int x, y;
+       uchar *b;
+
+       x = p0.x;
+       y = p0.y;
+       b = f->bmask + y*f->wmask;
+       if(b[x/8] & 0x80>>(x%8))
+               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;
+       }
+
+       for(x = p0.x; x >= 0; x--){
+               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*f->ncmp, f->bcmp, f->ncmp))
+                       break;
+               b[x/8] |= 0x80>>(x%8);
+       }
+
+       y = p0.y-1;
+       if(y >= 0){
+               for(x = p0.x; x >= 0; x--){
+                       if((b[x/8] & 0x80>>(x%8)) == 0)
+                               break;
+                       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;
+                       fillscan(f, Pt(x, y));
+               }
+       }
+
+       y = p0.y+1;
+       if(y < f->r0.max.y){
+               for(x = p0.x; x >= 0; x--){
+                       if((b[x/8] & 0x80>>(x%8)) == 0)
+                               break;
+                       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;
+                       fillscan(f, Pt(x, y));
+               }
+       }
+}
+
+void
+floodfill(Image *dst, Rectangle r, Point p, Image *src)
+{
+       Filldata f;
+
+       if(!rectclip(&r, dst->r))
+               return;
+       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, 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);
+       free(f.bscan);
+       if(f.iscan)
+               freeimage(f.iscan);
+       if(f.imask)
+               freeimage(f.imask);
+}
+
 void
 translate(Point d)
 {
@@ -314,19 +473,26 @@ drawpal(void)
        replclipr(screen, 0, r);
 
        penr = r;
-       penr.min.x = r.max.x - 10*Dy(r);
+       penr.min.x = r.max.x - NBRUSH*Dy(r);
 
        palr = r;
        palr.max.x = penr.min.x;
 
        r = penr;
        draw(screen, r, back, nil, ZP);
-       for(i=0; i<10; i++){
-               r.max.x = penr.min.x + (i+1)*Dx(penr) / 10;
+       for(i=0; i<NBRUSH; i++){
+               r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
                rr = r;
-               if(i == thick)
+               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;
        }
 
@@ -351,7 +517,7 @@ hitpal(Mouse m)
 {
        if(ptinrect(m.xy, penr)){
                if(m.buttons & 7){
-                       thick = ((m.xy.x - penr.min.x) * 10) / Dx(penr);
+                       brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr);
                        drawpal();
                }
                return 1;
@@ -396,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]);
@@ -480,10 +646,28 @@ main(int argc, char *argv[])
                                /* no break */
                        case 1:
                                p = s2c(e.mouse.xy);
-                               r = Rect(p.x-thick, p.y-thick, p.x+thick+1, p.y+thick+1);
+                               if(brush == NBRUSH-1){
+                                       /* flood fill brush */
+                                       if(canvas == nil || !ptinrect(p, canvas->r)){
+                                               back = img;
+                                               drawpal();
+                                               update(nil);
+                                               break;
+                                       }
+                                       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 = strokerect(Rpt(p, p), brush);
                                expand(r);
                                save(r, 1);
-                               fillellipse(canvas, p, thick, thick, img, ZP);
+                               strokedraw(canvas, Rpt(p, p), img, brush);
                                update(&r);
                                for(;;){
                                        m = e.mouse;
@@ -494,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 -= thick;
-                                       r.min.y -= thick;
-                                       r.max.x += thick+1;
-                                       r.max.y += thick+1;
+                                       r = strokerect(Rpt(p, d), brush);
                                        expand(r);
                                        save(r, 0);
-                                       line(canvas, p, d, Enddisc, Enddisc, thick, img, ZP);
+                                       strokedraw(canvas, Rpt(p, d), img, brush);
                                        update(&r);
                                        p = d;
                                }
@@ -525,12 +705,14 @@ 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 'f':
+                       case 'c':
                                if(canvas == nil)
                                        break;
                                save(canvas->r, 1);
@@ -541,9 +723,13 @@ main(int argc, char *argv[])
                        case 'u':
                                restore(16);
                                break;
+                       case 'f':
+                               brush = NBRUSH-1;
+                               drawpal();
+                               break;
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
-                               thick = e.kbdc - '0';
+                               brush = e.kbdc - '0';
                                drawpal();
                                break;
                        default: