2 This code was taken from 9front repository (https://code.9front.org/hg/plan9front).
3 It is subject to license from 9front, below is a reproduction of the license.
5 Copyright (c) 20XX 9front
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 /* additional libdraw function needed - defined here to avoid API change */
32 extern int eenter(char*, char*, int, Mouse*);
37 Point spos; /* position on screen */
38 Point cpos; /* position on canvas */
42 Image *pal[16]; /* palette */
43 Rectangle palr; /* palette rect on screen */
44 Rectangle penr; /* pen size rect on screen */
53 int c64[] = { /* c64 color palette */
73 * get bounding rectnagle for stroke from r.min to r.max with
74 * specified brush (size).
77 strokerect(Rectangle r, int brush)
80 return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1);
84 * draw stroke from r.min to r.max to dst with color ink and
88 strokedraw(Image *dst, Rectangle r, Image *ink, int brush)
90 if(!eqpt(r.min, r.max))
91 line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP);
92 fillellipse(dst, r.max, brush, brush, ink, ZP);
96 * A draw operation that touches only the area contained in bot but not in top.
97 * mp and sp get aligned with bot.min.
100 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
101 Image *src, Point sp, Image *mask, Point mp, int op)
107 if(Dx(bot)*Dy(bot) == 0)
110 /* no points in bot - top */
111 if(rectinrect(bot, top))
114 /* bot - top ≡ bot */
115 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
116 gendrawop(dst, bot, src, sp, mask, mp, op);
121 /* split bot into rectangles that don't intersect top */
123 if(bot.min.x < top.min.x){
124 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
125 delta = subpt(r.min, origin);
126 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
127 bot.min.x = top.min.x;
131 if(bot.max.x > top.max.x){
132 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
133 delta = subpt(r.min, origin);
134 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
135 bot.max.x = top.max.x;
139 if(bot.min.y < top.min.y){
140 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
141 delta = subpt(r.min, origin);
142 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
143 bot.min.y = top.min.y;
147 if(bot.max.y > top.max.y){
148 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
149 delta = subpt(r.min, origin);
150 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
151 bot.max.y = top.max.y;
156 alphachan(ulong chan)
158 for(; chan; chan >>= 8)
159 if(TYPE(chan) == CAlpha)
165 zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
173 if(r.min.x < d->r.min.x){
174 sp.x += (d->r.min.x - r.min.x)/f;
175 a.x = (d->r.min.x - r.min.x)%f;
176 r.min.x = d->r.min.x;
178 if(r.min.y < d->r.min.y){
179 sp.y += (d->r.min.y - r.min.y)/f;
180 a.y = (d->r.min.y - r.min.y)%f;
181 r.min.y = d->r.min.y;
184 w = s->r.max.x - sp.x;
188 dr.max.x = dr.min.x+w;
189 if(!alphachan(s->chan))
192 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
193 gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
196 if((t = allocimage(display, dr, s->chan, 0, 0)) == nil)
198 for(; dr.min.y < r.max.y; dr.min.y++){
199 dr.max.y = dr.min.y+1;
200 draw(t, dr, s, nil, sp);
207 for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
208 dr.max.x = dr.min.x+1;
209 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
210 gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
211 for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
212 dr.max.x = dr.min.x+1;
213 gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
223 if(p.x < 0) p.x -= zoom-1;
224 if(p.y < 0) p.y -= zoom-1;
225 return addpt(divpt(p, zoom), cpos);
230 return addpt(mulpt(subpt(p, cpos), zoom), spos);
235 return Rpt(c2s(r.min), c2s(r.max));
239 update(Rectangle *rp){
241 draw(screen, screen->r, back, nil, ZP);
245 gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD);
246 zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom);
248 flushimage(display, 1);
258 if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil)
259 sysfatal("allocimage: %r");
260 draw(canvas, canvas->r, back, nil, ZP);
265 if(eqrect(nr, canvas->r))
267 if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil)
269 draw(tmp, canvas->r, canvas, nil, canvas->r.min);
270 gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD);
276 save(Rectangle r, int mark)
282 x = nundo++ % nelem(undo);
287 if(canvas==nil || nundo<0)
289 if(!rectclip(&r, canvas->r))
291 if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil)
293 draw(tmp, r, canvas, nil, r.min);
294 x = nundo++ % nelem(undo);
309 x = --nundo % nelem(undo);
310 if((tmp = undo[x]) == nil)
313 if(canvas == nil || canvas->chan != tmp->chan){
319 draw(canvas, tmp->r, tmp, nil, tmp->r.min);
331 int yscan; /* current scanline */
332 int wscan; /* bscan width in bytes */
333 Image* iscan; /* scanline image */
334 uchar* bscan; /* scanline buffer */
336 int nmask; /* size of bmask in bytes */
337 int wmask; /* width of bmask in bytes */
338 Image* imask; /* mask image */
339 uchar* bmask; /* mask buffer */
346 fillscan(Filldata *f, Point p0)
353 b = f->bmask + y*f->wmask;
354 if(b[x/8] & 0x80>>(x%8))
358 draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y));
359 if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0)
364 for(x = p0.x; x >= 0; x--){
365 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
367 b[x/8] |= 0x80>>(x%8);
369 for(x = p0.x+1; x < f->r0.max.x; x++){
370 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp))
372 b[x/8] |= 0x80>>(x%8);
377 for(x = p0.x; x >= 0; x--){
378 if((b[x/8] & 0x80>>(x%8)) == 0)
380 fillscan(f, Pt(x, y));
382 for(x = p0.x+1; x < f->r0.max.x; x++){
383 if((b[x/8] & 0x80>>(x%8)) == 0)
385 fillscan(f, Pt(x, y));
391 for(x = p0.x; x >= 0; x--){
392 if((b[x/8] & 0x80>>(x%8)) == 0)
394 fillscan(f, Pt(x, y));
396 for(x = p0.x+1; x < f->r0.max.x; x++){
397 if((b[x/8] & 0x80>>(x%8)) == 0)
399 fillscan(f, Pt(x, y));
405 floodfill(Image *dst, Rectangle r, Point p, Image *src)
409 if(!rectclip(&r, dst->r))
413 memset(&f, 0, sizeof(f));
416 f.r0 = rectsubpt(r, r.min);
417 f.wmask = bytesperline(f.r0, 1);
418 f.nmask = f.wmask*f.r0.max.y;
419 if((f.bmask = mallocz(f.nmask, 1)) == nil)
421 if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil)
426 if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil)
429 f.wscan = bytesperline(f.iscan->r, f.iscan->depth);
430 if((f.bscan = mallocz(f.wscan, 0)) == nil)
434 f.ncmp = (f.iscan->depth+7) / 8;
435 draw(f.iscan, r, dst, nil, p);
436 if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0)
439 fillscan(&f, subpt(p, f.r.min));
441 loadimage(f.imask, f.imask->r, f.bmask, f.nmask);
442 draw(f.dst, f.r, src, f.imask, f.imask->r.min);
457 if(canvas==nil || d.x==0 && d.y==0)
460 nr = rectaddpt(r, d);
461 rectclip(&r, screen->clipr);
462 draw(screen, rectaddpt(r, d), screen, nil, r.min);
463 zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom);
464 gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD);
465 spos = addpt(spos, d);
466 flushimage(display, 1);
470 setzoom(Point o, int z)
485 cpos = addpt(canvas->r.min,
486 divpt(subpt(canvas->r.max, canvas->r.min), 2));
487 spos = addpt(screen->r.min,
488 divpt(subpt(screen->r.max, screen->r.min), 2));
499 r.min.y = r.max.y - 20;
500 replclipr(screen, 0, r);
503 penr.min.x = r.max.x - NBRUSH*Dy(r);
506 palr.max.x = penr.min.x;
509 draw(screen, r, back, nil, ZP);
510 for(i=0; i<NBRUSH; i++){
511 r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH;
516 /* last is special brush for fill draw */
517 draw(screen, rr, ink, nil, ZP);
519 rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2));
521 strokedraw(screen, rr, ink, i);
527 for(i=1; i<=nelem(pal); i++){
528 r.max.x = palr.min.x + i*Dx(palr) / nelem(pal);
532 draw(screen, rr, pal[i-1], nil, ZP);
533 gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD);
539 replclipr(screen, 0, r);
545 if(ptinrect(m.xy, penr)){
547 brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr);
552 if(ptinrect(m.xy, palr)){
555 col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
556 switch(m.buttons & 7){
573 catch(void * _, char *msg)
576 if(strstr(msg, "closed pipe"))
582 pipeline(char *fmt, ...)
589 vsnprint(buf, sizeof(buf), fmt, a);
593 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){ // RFEND not available in libc port
603 execl("/bin/rc", "rc", "-c", buf, nil);
613 fprint(2, "usage: %s [ file ]\n", argv0);
618 main(int argc, char *argv[])
634 filename = strdup(argv[0]);
638 if(initdraw(0, 0, "paint") < 0)
639 sysfatal("initdraw: %r");
642 if((fd = open(filename, OREAD)) < 0)
643 sysfatal("open: %r");
644 if((canvas = readimage(display, fd, 0)) == nil)
645 sysfatal("readimage: %r");
649 /* palette initialization */
650 for(i=0; i<nelem(pal); i++){
651 pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1,
652 c64[i % nelem(c64)]<<8 | 0xFF);
654 sysfatal("allocimage: %r");
661 einit(Emouse | Ekeyboard);
671 switch(e.mouse.buttons & 7){
677 if(brush == NBRUSH-1){
678 /* flood fill brush */
679 if(canvas == nil || !ptinrect(p, canvas->r)){
687 floodfill(canvas, r, p, img);
690 /* wait for mouse release */
691 while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0)
695 r = strokerect(Rpt(p, p), brush);
698 strokedraw(canvas, Rpt(p, p), img, brush);
702 if(event(&e) != Emouse)
704 if((e.mouse.buttons ^ m.buttons) & 7)
709 r = strokerect(Rpt(p, d), brush);
712 strokedraw(canvas, Rpt(p, d), img, brush);
720 if(event(&e) != Emouse)
722 if((e.mouse.buttons & 7) != 4)
724 translate(subpt(e.mouse.xy, m.xy));
737 setzoom(e.mouse.xy, zoom*2);
741 setzoom(e.mouse.xy, zoom/2);
758 case '0': case '1': case '2': case '3': case '4':
759 case '5': case '6': case '7': case '8': case '9':
760 brush = e.kbdc - '0';
767 if(filename && (e.kbdc == 'r' || e.kbdc == 'w'))
768 snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename);
769 else if(e.kbdc > 0x20 && e.kbdc < 0x7f)
770 snprint(buf, sizeof(buf), "%C", e.kbdc);
771 if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0)
773 if(strcmp(buf, "q") == 0)
776 while(*s == ' ' || *s == '\t')
782 if((fd = open(s, OREAD)) < 0){
784 snprint(buf, sizeof(buf), "%r");
785 eenter(buf, nil, 0, &e.mouse);
789 filename = strdup(s);
791 unlockdisplay(display);
792 img = readimage(display, fd, 1);
794 lockdisplay(display);
796 werrstr("readimage: %r");
807 if((fd = create(s, OWRITE, 0660)) < 0)
810 filename = strdup(s);
813 if(writeimage(fd, canvas, 0) < 0){
815 werrstr("writeimage: %r");
821 if((fd = pipeline("%s", s)) < 0)
825 if((fd = pipeline("%s", s)) < 0)
831 if((fd = pipeline("%s", s)) < 0)
833 switch(rfork(RFMEM|RFPROC|RFFDG)){
836 werrstr("rfork: %r");
839 writeimage(fd, canvas, 1);
855 if(getwindow(display, Refnone) < 0)
856 sysfatal("resize failed");