10 Point spos; /* position on screen */
11 Point cpos; /* position on canvas */
15 Image *pal[16]; /* palette */
16 Rectangle palr; /* palette rect on screen */
17 Rectangle penr; /* pen size rect on screen */
25 int c64[] = { /* c64 color palette */
45 * A draw operation that touches only the area contained in bot but not in top.
46 * mp and sp get aligned with bot.min.
49 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
50 Image *src, Point sp, Image *mask, Point mp, int op)
56 if(Dx(bot)*Dy(bot) == 0)
59 /* no points in bot - top */
60 if(rectinrect(bot, top))
64 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
65 gendrawop(dst, bot, src, sp, mask, mp, op);
70 /* split bot into rectangles that don't intersect top */
72 if(bot.min.x < top.min.x){
73 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
74 delta = subpt(r.min, origin);
75 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
76 bot.min.x = top.min.x;
80 if(bot.max.x > top.max.x){
81 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
82 delta = subpt(r.min, origin);
83 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
84 bot.max.x = top.max.x;
88 if(bot.min.y < top.min.y){
89 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
90 delta = subpt(r.min, origin);
91 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
92 bot.min.y = top.min.y;
96 if(bot.max.y > top.max.y){
97 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
98 delta = subpt(r.min, origin);
99 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
100 bot.max.y = top.max.y;
105 alphachan(ulong chan)
107 for(; chan; chan >>= 8)
108 if(TYPE(chan) == CAlpha)
114 zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
122 if(r.min.x < d->r.min.x){
123 sp.x += (d->r.min.x - r.min.x)/f;
124 a.x = (d->r.min.x - r.min.x)%f;
125 r.min.x = d->r.min.x;
127 if(r.min.y < d->r.min.y){
128 sp.y += (d->r.min.y - r.min.y)/f;
129 a.y = (d->r.min.y - r.min.y)%f;
130 r.min.y = d->r.min.y;
133 w = s->r.max.x - sp.x;
137 dr.max.x = dr.min.x+w;
138 if(!alphachan(s->chan))
141 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
142 gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
145 if((t = allocimage(display, dr, s->chan, 0, 0)) == nil)
147 for(; dr.min.y < r.max.y; dr.min.y++){
148 dr.max.y = dr.min.y+1;
149 draw(t, dr, s, nil, sp);
156 for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
157 dr.max.x = dr.min.x+1;
158 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
159 gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
160 for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
161 dr.max.x = dr.min.x+1;
162 gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
172 if(p.x < 0) p.x -= zoom-1;
173 if(p.y < 0) p.y -= zoom-1;
174 return addpt(divpt(p, zoom), cpos);
179 return addpt(mulpt(subpt(p, cpos), zoom), spos);
184 return Rpt(c2s(r.min), c2s(r.max));
188 update(Rectangle *rp){
190 draw(screen, screen->r, back, nil, ZP);
194 gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD);
195 zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom);
197 flushimage(display, 1);
207 if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil)
208 sysfatal("allocimage: %r");
209 draw(canvas, canvas->r, back, nil, ZP);
214 if(eqrect(nr, canvas->r))
216 if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil)
218 draw(tmp, canvas->r, canvas, nil, canvas->r.min);
219 gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD);
225 save(Rectangle r, int mark)
231 x = nundo++ % nelem(undo);
236 if(canvas==nil || nundo<0)
238 if(!rectclip(&r, canvas->r))
240 if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil)
242 draw(tmp, r, canvas, nil, r.min);
243 x = nundo++ % nelem(undo);
258 x = --nundo % nelem(undo);
259 if((tmp = undo[x]) == nil)
263 draw(canvas, tmp->r, tmp, nil, tmp->r.min);
274 int yscan; /* current scanline */
275 int wscan; /* bscan width in bytes */
276 Image* iscan; /* scanline image */
277 uchar* bscan; /* scanline buffer */
279 int nmask; /* size of bmask in bytes */
280 int wmask; /* width of bmask in bytes */
281 Image* imask; /* mask image */
282 uchar* bmask; /* mask buffer */
289 fillscan(Filldata *f, Point p0)
296 b = f->bmask + y*f->wmask;
297 if(b[x/8] & 0x80>>(x%8))
301 draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y));
302 if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0)
307 for(x = p0.x; x >= 0; x--){
308 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
310 b[x/8] |= 0x80>>(x%8);
312 for(x = p0.x+1; x < f->r0.max.x; x++){
313 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
315 b[x/8] |= 0x80>>(x%8);
320 for(x = p0.x; x >= 0; x--){
321 if((b[x/8] & 0x80>>(x%8)) == 0)
323 fillscan(f, Pt(x, y));
325 for(x = p0.x+1; x < f->r0.max.x; x++){
326 if((b[x/8] & 0x80>>(x%8)) == 0)
328 fillscan(f, Pt(x, y));
334 for(x = p0.x; x >= 0; x--){
335 if((b[x/8] & 0x80>>(x%8)) == 0)
337 fillscan(f, Pt(x, y));
339 for(x = p0.x+1; x < f->r0.max.x; x++){
340 if((b[x/8] & 0x80>>(x%8)) == 0)
342 fillscan(f, Pt(x, y));
348 floodfill(Image *dst, Rectangle r, Point p, Image *src)
352 if(!rectclip(&r, dst->r))
356 memset(&f, 0, sizeof(f));
359 f.r0 = rectsubpt(r, r.min);
360 f.wmask = bytesperline(f.r0, 1);
361 f.nmask = f.wmask*f.r0.max.y;
362 if((f.bmask = mallocz(f.nmask, 1)) == nil)
364 if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil)
369 if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil)
372 f.wscan = bytesperline(f.iscan->r, f.iscan->depth);
373 if((f.bscan = mallocz(f.wscan, 0)) == nil)
377 f.ncmp = (f.iscan->depth+7) / 8;
378 draw(f.iscan, r, dst, nil, p);
379 if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0)
382 fillscan(&f, subpt(p, f.r.min));
384 loadimage(f.imask, f.imask->r, f.bmask, f.nmask);
385 draw(f.dst, f.r, src, f.imask, f.imask->r.min);
400 if(canvas==nil || d.x==0 && d.y==0)
403 nr = rectaddpt(r, d);
404 rectclip(&r, screen->clipr);
405 draw(screen, rectaddpt(r, d), screen, nil, r.min);
406 zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom);
407 gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD);
408 spos = addpt(spos, d);
409 flushimage(display, 1);
413 setzoom(Point o, int z)
428 cpos = addpt(canvas->r.min,
429 divpt(subpt(canvas->r.max, canvas->r.min), 2));
430 spos = addpt(screen->r.min,
431 divpt(subpt(screen->r.max, screen->r.min), 2));
442 r.min.y = r.max.y - 20;
443 replclipr(screen, 0, r);
446 penr.min.x = r.max.x - NBRUSH*Dy(r);
449 palr.max.x = penr.min.x;
452 draw(screen, r, back, nil, ZP);
454 r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
458 fillellipse(screen, addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)), i, i, ink, ZP);
461 r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
465 draw(screen, rr, ink, nil, ZP);
468 for(i=1; i<=nelem(pal); i++){
469 r.max.x = palr.min.x + i*Dx(palr) / nelem(pal);
473 draw(screen, rr, pal[i-1], nil, ZP);
474 gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD);
480 replclipr(screen, 0, r);
486 if(ptinrect(m.xy, penr)){
488 brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr);
493 if(ptinrect(m.xy, palr)){
496 col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
497 switch(m.buttons & 7){
514 catch(void *, char *msg)
516 if(strstr(msg, "closed pipe"))
522 pipeline(char *fmt, ...)
529 vsnprint(buf, sizeof(buf), fmt, a);
533 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){
543 execl("/bin/rc", "rc", "-c", buf, nil);
553 fprint(2, "usage: %s [ file ]\n", argv0);
558 main(int argc, char *argv[])
574 filename = strdup(argv[0]);
578 if(initdraw(0, 0, "paint") < 0)
579 sysfatal("initdraw: %r");
582 if((fd = open(filename, OREAD)) < 0)
583 sysfatal("open: %r");
584 if((canvas = readimage(display, fd, 0)) == nil)
585 sysfatal("readimage: %r");
589 /* palette initialization */
590 for(i=0; i<nelem(pal); i++){
591 pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1,
592 c64[i % nelem(c64)]<<8 | 0xFF);
594 sysfatal("allocimage: %r");
601 einit(Emouse | Ekeyboard);
611 switch(e.mouse.buttons & 7){
618 /* flood fill brush */
619 if(canvas == nil || !ptinrect(p, canvas->r)){
627 floodfill(canvas, r, p, img);
630 /* wait for mouse release */
631 while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0)
635 r = Rect(p.x-brush, p.y-brush, p.x+brush+1, p.y+brush+1);
638 fillellipse(canvas, p, brush, brush, img, ZP);
642 if(event(&e) != Emouse)
644 if((e.mouse.buttons ^ m.buttons) & 7)
649 r = canonrect(Rpt(p, d));
656 line(canvas, p, d, Enddisc, Enddisc, brush, img, ZP);
664 if(event(&e) != Emouse)
666 if((e.mouse.buttons & 7) != 4)
668 translate(subpt(e.mouse.xy, m.xy));
680 setzoom(e.mouse.xy, zoom*2);
683 setzoom(e.mouse.xy, zoom/2);
700 case '0': case '1': case '2': case '3': case '4':
701 case '5': case '6': case '7': case '8': case '9':
702 brush = e.kbdc - '0';
709 if(filename && (e.kbdc == 'r' || e.kbdc == 'w'))
710 snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename);
711 else if(e.kbdc > 0x20 && e.kbdc < 0x7f)
712 snprint(buf, sizeof(buf), "%C", e.kbdc);
713 if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0)
715 if(strcmp(buf, "q") == 0)
718 while(*s == ' ' || *s == '\t')
724 if((fd = open(s, OREAD)) < 0){
726 snprint(buf, sizeof(buf), "%r");
727 eenter(buf, nil, 0, &e.mouse);
731 filename = strdup(s);
733 unlockdisplay(display);
734 img = readimage(display, fd, 1);
736 lockdisplay(display);
738 werrstr("readimage: %r");
749 if((fd = create(s, OWRITE, 0660)) < 0)
752 filename = strdup(s);
755 if(writeimage(fd, canvas, 0) < 0){
757 werrstr("writeimage: %r");
763 if((fd = pipeline("%s", s)) < 0)
767 if((fd = pipeline("%s", s)) < 0)
773 if((fd = pipeline("%s", s)) < 0)
775 switch(rfork(RFMEM|RFPROC|RFFDG)){
778 werrstr("rfork: %r");
781 writeimage(fd, canvas, 1);
796 if(getwindow(display, Refnone) < 0)
797 sysfatal("resize failed");