ref: 9d212c517d0aea6bc0e70a891e83aa4c1d139114
parent: f3b08dd655446276be6426506a1fd84d13defbad
author: cinap_lenrek <[email protected]>
date: Sun Jul 22 12:33:42 EDT 2012
paint: floodfill
--- a/sys/man/1/paint
+++ b/sys/man/1/paint
@@ -26,11 +26,14 @@
.B u
Undos the previous action.
.TP
-.B f
-Fills the canvas with the background color.
+.B c
+Clears the canvas with the background color.
.TP
.B 1-9
Select brush size.
+.TP
+.B f
+Select flood fill brush.
.TP
.B +
Doubles magnification.
--- a/sys/src/cmd/paint.c
+++ b/sys/src/cmd/paint.c
@@ -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;
@@ -15,6 +15,9 @@
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];
@@ -263,7 +266,117 @@
}
}
+typedef struct {
+ Rectangle r;
+ Rectangle r0;
+ Image *iscan;
+ uchar *bscan;
+ uchar *bmask;
+ int wmask;
+ int wscan;
+ int yscan;
+ uchar color[4];
+} Floodfill;
+
void
+floodscan(Floodfill *f, Point p0)
+{
+ uchar *b;
+ int x, y;
+
+ x = p0.x;
+ y = p0.y;
+ b = f->bmask + y*f->wmask;
+ if(b[x/8] & 0x80>>(x%8))
+ return;
+
+ if(f->yscan != y){
+ 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))
+ 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))
+ 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;
+ floodscan(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));
+ }
+ }
+
+ 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;
+ floodscan(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));
+ }
+ }
+}
+
+void
+floodfill(Point p, Image *fill)
+{
+ Floodfill f;
+ Image *imask;
+ int nmask;
+
+ 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)
+ return;
+ if((f.iscan = allocimage(display, Rect(0, 0, f.r0.max.x, 1), RGB24, 0, DNofill)) == nil){
+ free(f.bmask);
+ return;
+ }
+ 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);
+ free(f.bmask);
+ draw(canvas, canvas->r, fill, imask, imask->r.min);
+ freeimage(imask);
+}
+
+void
translate(Point d)
{
Rectangle r, nr;
@@ -314,7 +427,7 @@
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;
@@ -322,13 +435,18 @@
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;
+ 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);
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++){
@@ -351,7 +469,7 @@
{
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;
@@ -480,10 +598,23 @@
/* 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 >= 10){
+ /* flood fill brush */
+ if(canvas == nil || !ptinrect(p, canvas->r)){
+ back = img;
+ drawpal();
+ update(nil);
+ break;
+ }
+ save(canvas->r, 1);
+ floodfill(p, img);
+ update(nil);
+ break;
+ }
+ r = Rect(p.x-brush, p.y-brush, p.x+brush+1, p.y+brush+1);
expand(r);
save(r, 1);
- fillellipse(canvas, p, thick, thick, img, ZP);
+ fillellipse(canvas, p, brush, brush, img, ZP);
update(&r);
for(;;){
m = e.mouse;
@@ -495,13 +626,13 @@
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.min.x -= brush;
+ r.min.y -= brush;
+ r.max.x += brush+1;
+ r.max.y += brush+1;
expand(r);
save(r, 0);
- line(canvas, p, d, Enddisc, Enddisc, thick, img, ZP);
+ line(canvas, p, d, Enddisc, Enddisc, brush, img, ZP);
update(&r);
p = d;
}
@@ -530,7 +661,7 @@
case '-':
setzoom(e.mouse.xy, zoom/2);
break;
- case 'f':
+ case 'c':
if(canvas == nil)
break;
save(canvas->r, 1);
@@ -541,9 +672,13 @@
case 'u':
restore(16);
break;
+ case 'f':
+ brush = 10;
+ 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: