paint: new paint program featuring endless canvas, zoom, palette and use of external commands
This commit is contained in:
parent
55ddbff77d
commit
72d4a35942
2 changed files with 692 additions and 174 deletions
|
@ -3,54 +3,78 @@
|
|||
.SH NAME
|
||||
paint \- create image files by drawing with a mouse or other pointing device
|
||||
.SH SYNOPSIS
|
||||
.B paint [file]
|
||||
.B paint
|
||||
[
|
||||
.I file
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Paint
|
||||
provides a window upon which can be drawn lines by moving the cursor while
|
||||
holding down mouse button 1 or its equivalent.
|
||||
shows a canvas upon which can be drawn lines using the mouse holding
|
||||
down buttons 1 or 2 for foreground or background color. The canvas
|
||||
can be moved with button 3. Colors and brush sizes can be selected by
|
||||
clicking on the palette at the bottom of the screen with buttons 1 or 2.
|
||||
.PP
|
||||
A number of keyboard commands are recognized:
|
||||
If the optional
|
||||
.I file
|
||||
argument is specified, then it is read and used as the canvas.
|
||||
.I Paint
|
||||
only recognizes Plan 9 bitmap format (see
|
||||
.IR image (6)).
|
||||
.PP
|
||||
A number of immidiate keyboard commands are recognized:
|
||||
.TP
|
||||
.B b
|
||||
Set the brush size to an ellipse with a horizontal semiaxis of
|
||||
.I n
|
||||
and a vertical semiaxis of
|
||||
.I n
|
||||
(see
|
||||
.IR graphics (2)).
|
||||
Type a number,
|
||||
.I n,
|
||||
in the pop-up box and hit enter.
|
||||
.TP
|
||||
.B c
|
||||
Change the drawing color to
|
||||
.I n,
|
||||
where
|
||||
0 = black, 1 = white, 2 = red, 3 = green, 4 = blue and 5 = yellow.
|
||||
.B u
|
||||
Undos the previous action.
|
||||
.TP
|
||||
.B f
|
||||
Fill the screen with a color,
|
||||
.I n.
|
||||
Any unsaved work will be lost.
|
||||
Fills the canvas with the background color.
|
||||
.TP
|
||||
.B o
|
||||
Open a bitmap image file for editing. Type a path and filename into the
|
||||
pop-up box and hit enter. If the path is omitted, the filename will be opened
|
||||
from the current directory.
|
||||
.B 1-9
|
||||
Select brush size.
|
||||
.TP
|
||||
.B s
|
||||
Save the current screen as a bitmap image. If the path is omitted, the filename will be
|
||||
saved in the current directory.
|
||||
.B +
|
||||
Doubles magnification.
|
||||
.TP
|
||||
.B -
|
||||
Halves magnification.
|
||||
.TP
|
||||
.B esc
|
||||
Centers the canvas and resets magnification.
|
||||
.PP
|
||||
Hitting any other key on the keyboard shows a command prompt
|
||||
where the following commands can be entered:
|
||||
.TP
|
||||
.BI r file
|
||||
Reads the canvas from
|
||||
.I file.
|
||||
.TP
|
||||
.BI w file
|
||||
Writes the canvas to
|
||||
.I file.
|
||||
.TP
|
||||
.BI < command
|
||||
Executes
|
||||
.I command
|
||||
and read the canvas from its standard output.
|
||||
.TP
|
||||
.BI > command
|
||||
Executes
|
||||
.I command
|
||||
and write the canvas to its standard input.
|
||||
.TP
|
||||
.BI | command
|
||||
Transforms the canvas by running it thru
|
||||
.I command.
|
||||
.TP
|
||||
.B q
|
||||
Quit.
|
||||
Quits the program.
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/paint.c
|
||||
.SH "SEE ALSO"
|
||||
.IR graphics (2),
|
||||
.IR resize (1),
|
||||
.IR resample (1),
|
||||
.IR rotate (1),
|
||||
.IR crop (1),
|
||||
.IR jpg (1),
|
||||
.IR page (1),
|
||||
.IR image (6)
|
||||
.SH BUGS
|
||||
.I Paint
|
||||
offers a bare minimum of drawing functionality. Popular features such as
|
||||
.B undo
|
||||
have not yet been implemented.
|
||||
|
|
|
@ -2,149 +2,643 @@
|
|||
#include <libc.h>
|
||||
#include <draw.h>
|
||||
#include <event.h>
|
||||
#include <keyboard.h>
|
||||
|
||||
#define NCOLORS 6
|
||||
char *filename;
|
||||
int zoom = 1;
|
||||
int thick = 1;
|
||||
Point spos; /* position on screen */
|
||||
Point cpos; /* position on canvas */
|
||||
Image *canvas;
|
||||
Image *ink;
|
||||
Image *back;
|
||||
Image *pal[16]; /* palette */
|
||||
Rectangle palr; /* palette rect on screen */
|
||||
Rectangle penr; /* pen size rect on screen */
|
||||
|
||||
Image *colors[NCOLORS];
|
||||
int nundo = 0;
|
||||
Image *undo[1024];
|
||||
|
||||
int c64[] = { /* c64 color palette */
|
||||
0x000000,
|
||||
0xFFFFFF,
|
||||
0x68372B,
|
||||
0x70A4B2,
|
||||
0x6F3D86,
|
||||
0x588D43,
|
||||
0x352879,
|
||||
0xB8C76F,
|
||||
0x6F4F25,
|
||||
0x433900,
|
||||
0x9A6759,
|
||||
0x444444,
|
||||
0x6C6C6C,
|
||||
0x9AD284,
|
||||
0x6C5EB5,
|
||||
0x959595,
|
||||
};
|
||||
|
||||
/*
|
||||
* A draw operation that touches only the area contained in bot but not in top.
|
||||
* mp and sp get aligned with bot.min.
|
||||
*/
|
||||
static void
|
||||
gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
|
||||
Image *src, Point sp, Image *mask, Point mp, int op)
|
||||
{
|
||||
Rectangle r;
|
||||
Point origin;
|
||||
Point delta;
|
||||
|
||||
if(Dx(bot)*Dy(bot) == 0)
|
||||
return;
|
||||
|
||||
/* no points in bot - top */
|
||||
if(rectinrect(bot, top))
|
||||
return;
|
||||
|
||||
/* bot - top ≡ bot */
|
||||
if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
|
||||
gendrawop(dst, bot, src, sp, mask, mp, op);
|
||||
return;
|
||||
}
|
||||
|
||||
origin = bot.min;
|
||||
/* split bot into rectangles that don't intersect top */
|
||||
/* left side */
|
||||
if(bot.min.x < top.min.x){
|
||||
r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
|
||||
delta = subpt(r.min, origin);
|
||||
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
|
||||
bot.min.x = top.min.x;
|
||||
}
|
||||
|
||||
/* right side */
|
||||
if(bot.max.x > top.max.x){
|
||||
r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
|
||||
delta = subpt(r.min, origin);
|
||||
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
|
||||
bot.max.x = top.max.x;
|
||||
}
|
||||
|
||||
/* top */
|
||||
if(bot.min.y < top.min.y){
|
||||
r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
|
||||
delta = subpt(r.min, origin);
|
||||
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
|
||||
bot.min.y = top.min.y;
|
||||
}
|
||||
|
||||
/* bottom */
|
||||
if(bot.max.y > top.max.y){
|
||||
r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
|
||||
delta = subpt(r.min, origin);
|
||||
gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
|
||||
bot.max.y = top.max.y;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
alphachan(ulong chan)
|
||||
{
|
||||
for(; chan; chan >>= 8)
|
||||
if(TYPE(chan) == CAlpha)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
|
||||
{
|
||||
Rectangle dr;
|
||||
Image *t;
|
||||
Point a;
|
||||
int w;
|
||||
|
||||
a = ZP;
|
||||
if(r.min.x < d->r.min.x){
|
||||
sp.x += (d->r.min.x - r.min.x)/f;
|
||||
a.x = (d->r.min.x - r.min.x)%f;
|
||||
r.min.x = d->r.min.x;
|
||||
}
|
||||
if(r.min.y < d->r.min.y){
|
||||
sp.y += (d->r.min.y - r.min.y)/f;
|
||||
a.y = (d->r.min.y - r.min.y)%f;
|
||||
r.min.y = d->r.min.y;
|
||||
}
|
||||
rectclip(&r, d->r);
|
||||
w = s->r.max.x - sp.x;
|
||||
if(w > Dx(r))
|
||||
w = Dx(r);
|
||||
dr = r;
|
||||
dr.max.x = dr.min.x+w;
|
||||
if(!alphachan(s->chan))
|
||||
b = nil;
|
||||
if(f <= 1){
|
||||
if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
|
||||
gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
|
||||
return;
|
||||
}
|
||||
if((t = allocimage(display, dr, s->chan, 0, 0)) == nil)
|
||||
return;
|
||||
for(; dr.min.y < r.max.y; dr.min.y++){
|
||||
dr.max.y = dr.min.y+1;
|
||||
draw(t, dr, s, nil, sp);
|
||||
if(++a.y == f){
|
||||
a.y = 0;
|
||||
sp.y++;
|
||||
}
|
||||
}
|
||||
dr = r;
|
||||
for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
|
||||
dr.max.x = dr.min.x+1;
|
||||
if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
|
||||
gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
|
||||
for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
|
||||
dr.max.x = dr.min.x+1;
|
||||
gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
|
||||
}
|
||||
a.x = 0;
|
||||
}
|
||||
freeimage(t);
|
||||
}
|
||||
|
||||
Point
|
||||
s2c(Point p){
|
||||
p = subpt(p, spos);
|
||||
if(p.x < 0) p.x -= zoom-1;
|
||||
if(p.y < 0) p.y -= zoom-1;
|
||||
return addpt(divpt(p, zoom), cpos);
|
||||
}
|
||||
|
||||
Point
|
||||
c2s(Point p){
|
||||
return addpt(mulpt(subpt(p, cpos), zoom), spos);
|
||||
}
|
||||
|
||||
Rectangle
|
||||
c2sr(Rectangle r){
|
||||
return Rpt(c2s(r.min), c2s(r.max));
|
||||
}
|
||||
|
||||
void
|
||||
update(Rectangle *rp){
|
||||
if(canvas==nil)
|
||||
draw(screen, screen->r, back, nil, ZP);
|
||||
else {
|
||||
if(rp == nil)
|
||||
rp = &canvas->r;
|
||||
gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD);
|
||||
zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom);
|
||||
}
|
||||
flushimage(display, 1);
|
||||
}
|
||||
|
||||
void
|
||||
expand(Rectangle r)
|
||||
{
|
||||
Rectangle nr;
|
||||
Image *tmp;
|
||||
|
||||
if(canvas==nil){
|
||||
if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil)
|
||||
sysfatal("allocimage: %r");
|
||||
draw(canvas, canvas->r, back, nil, ZP);
|
||||
return;
|
||||
}
|
||||
nr = canvas->r;
|
||||
combinerect(&nr, r);
|
||||
if(eqrect(nr, canvas->r))
|
||||
return;
|
||||
if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil)
|
||||
return;
|
||||
draw(tmp, canvas->r, canvas, nil, canvas->r.min);
|
||||
gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD);
|
||||
freeimage(canvas);
|
||||
canvas = tmp;
|
||||
}
|
||||
|
||||
void
|
||||
save(Rectangle r, int mark)
|
||||
{
|
||||
Image *tmp;
|
||||
int x;
|
||||
|
||||
if(mark){
|
||||
x = nundo++ % nelem(undo);
|
||||
if(undo[x])
|
||||
freeimage(undo[x]);
|
||||
undo[x] = nil;
|
||||
}
|
||||
if(canvas==nil || nundo<0)
|
||||
return;
|
||||
if(!rectclip(&r, canvas->r))
|
||||
return;
|
||||
if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil)
|
||||
return;
|
||||
draw(tmp, r, canvas, nil, r.min);
|
||||
x = nundo++ % nelem(undo);
|
||||
if(undo[x])
|
||||
freeimage(undo[x]);
|
||||
undo[x] = tmp;
|
||||
}
|
||||
|
||||
void
|
||||
restore(int n)
|
||||
{
|
||||
Image *tmp;
|
||||
int x;
|
||||
|
||||
while(nundo > 0){
|
||||
if(n-- == 0)
|
||||
return;
|
||||
x = --nundo % nelem(undo);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
translate(Point d)
|
||||
{
|
||||
Rectangle r, nr;
|
||||
|
||||
if(canvas==nil || d.x==0 && d.y==0)
|
||||
return;
|
||||
r = c2sr(canvas->r);
|
||||
nr = rectaddpt(r, d);
|
||||
rectclip(&r, screen->clipr);
|
||||
draw(screen, rectaddpt(r, d), screen, nil, r.min);
|
||||
zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom);
|
||||
gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD);
|
||||
spos = addpt(spos, d);
|
||||
flushimage(display, 1);
|
||||
}
|
||||
|
||||
void
|
||||
setzoom(Point o, int z)
|
||||
{
|
||||
if(z < 1)
|
||||
return;
|
||||
cpos = s2c(o);
|
||||
spos = o;
|
||||
zoom = z;
|
||||
update(nil);
|
||||
}
|
||||
|
||||
void
|
||||
center(void)
|
||||
{
|
||||
cpos = ZP;
|
||||
if(canvas)
|
||||
cpos = addpt(canvas->r.min,
|
||||
divpt(subpt(canvas->r.max, canvas->r.min), 2));
|
||||
spos = addpt(screen->r.min,
|
||||
divpt(subpt(screen->r.max, screen->r.min), 2));
|
||||
update(nil);
|
||||
}
|
||||
|
||||
void
|
||||
drawpal(void)
|
||||
{
|
||||
Rectangle r, rr;
|
||||
int i;
|
||||
|
||||
r = screen->r;
|
||||
r.min.y = r.max.y - 20;
|
||||
replclipr(screen, 0, r);
|
||||
|
||||
penr = r;
|
||||
penr.min.x = r.max.x - 10*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;
|
||||
rr = r;
|
||||
if(i == thick)
|
||||
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 = palr;
|
||||
for(i=1; i<=nelem(pal); i++){
|
||||
r.max.x = palr.min.x + i*Dx(palr) / nelem(pal);
|
||||
rr = r;
|
||||
if(ink == pal[i-1])
|
||||
rr.min.y += Dy(r)/3;
|
||||
draw(screen, rr, pal[i-1], nil, ZP);
|
||||
gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD);
|
||||
r.min.x = r.max.x;
|
||||
}
|
||||
|
||||
r = screen->r;
|
||||
r.max.y -= Dy(palr);
|
||||
replclipr(screen, 0, r);
|
||||
}
|
||||
|
||||
int
|
||||
hitpal(Mouse m)
|
||||
{
|
||||
if(ptinrect(m.xy, penr)){
|
||||
if(m.buttons & 7){
|
||||
thick = ((m.xy.x - penr.min.x) * 10) / Dx(penr);
|
||||
drawpal();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if(ptinrect(m.xy, palr)){
|
||||
Image *col;
|
||||
|
||||
col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
|
||||
switch(m.buttons & 7){
|
||||
case 1:
|
||||
ink = col;
|
||||
drawpal();
|
||||
break;
|
||||
case 2:
|
||||
back = col;
|
||||
drawpal();
|
||||
update(nil);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
catch(void *, char *msg)
|
||||
{
|
||||
if(strstr(msg, "closed pipe"))
|
||||
noted(NCONT);
|
||||
noted(NDFLT);
|
||||
}
|
||||
|
||||
int
|
||||
pipeline(char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list a;
|
||||
int p[2];
|
||||
|
||||
va_start(a, fmt);
|
||||
vsnprint(buf, sizeof(buf), fmt, a);
|
||||
va_end(a);
|
||||
if(pipe(p) < 0)
|
||||
return -1;
|
||||
switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){
|
||||
case -1:
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
return -1;
|
||||
case 0:
|
||||
close(p[1]);
|
||||
dup(p[0], 0);
|
||||
dup(p[0], 1);
|
||||
close(p[0]);
|
||||
execl("/bin/rc", "rc", "-c", buf, nil);
|
||||
exits("exec");
|
||||
}
|
||||
close(p[0]);
|
||||
return p[1];
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [ file ]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *s, buf[1024];
|
||||
Rectangle r;
|
||||
Image *img;
|
||||
int i, fd;
|
||||
Event e;
|
||||
Mouse m;
|
||||
Point p, d;
|
||||
|
||||
ARGBEGIN {
|
||||
default:
|
||||
usage();
|
||||
} ARGEND;
|
||||
|
||||
if(argc == 1)
|
||||
filename = strdup(argv[0]);
|
||||
else if(argc != 0)
|
||||
usage();
|
||||
|
||||
if(initdraw(0, 0, "paint") < 0)
|
||||
sysfatal("initdraw: %r");
|
||||
|
||||
if(filename){
|
||||
if((fd = open(filename, OREAD)) < 0)
|
||||
sysfatal("open: %r");
|
||||
if((canvas = readimage(display, fd, 0)) == nil)
|
||||
sysfatal("readimage: %r");
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* palette initialization */
|
||||
for(i=0; i<nelem(pal); i++){
|
||||
pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1,
|
||||
c64[i % nelem(c64)]<<8 | 0xFF);
|
||||
if(pal[i] == nil)
|
||||
sysfatal("allocimage: %r");
|
||||
}
|
||||
ink = pal[0];
|
||||
back = pal[1];
|
||||
drawpal();
|
||||
center();
|
||||
|
||||
einit(Emouse | Ekeyboard);
|
||||
|
||||
notify(catch);
|
||||
for(;;) {
|
||||
switch(event(&e)){
|
||||
case Emouse:
|
||||
if(hitpal(e.mouse))
|
||||
continue;
|
||||
|
||||
img = ink;
|
||||
switch(e.mouse.buttons & 7){
|
||||
case 2:
|
||||
img = back;
|
||||
/* 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);
|
||||
expand(r);
|
||||
save(r, 1);
|
||||
fillellipse(canvas, p, thick, thick, img, ZP);
|
||||
update(&r);
|
||||
for(;;){
|
||||
m = e.mouse;
|
||||
if(event(&e) != Emouse)
|
||||
break;
|
||||
if((e.mouse.buttons ^ m.buttons) & 7)
|
||||
break;
|
||||
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;
|
||||
expand(r);
|
||||
save(r, 0);
|
||||
line(canvas, p, d, Enddisc, Enddisc, thick, img, ZP);
|
||||
update(&r);
|
||||
p = d;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for(;;){
|
||||
m = e.mouse;
|
||||
if(event(&e) != Emouse)
|
||||
break;
|
||||
if((e.mouse.buttons & 7) != 4)
|
||||
break;
|
||||
translate(subpt(e.mouse.xy, m.xy));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Ekeyboard:
|
||||
switch(e.kbdc){
|
||||
case Kesc:
|
||||
zoom = 1;
|
||||
center();
|
||||
break;
|
||||
case '+':
|
||||
setzoom(e.mouse.xy, zoom*2);
|
||||
break;
|
||||
case '-':
|
||||
setzoom(e.mouse.xy, zoom/2);
|
||||
break;
|
||||
case 'f':
|
||||
if(canvas == nil)
|
||||
break;
|
||||
save(canvas->r, 1);
|
||||
freeimage(canvas);
|
||||
canvas = nil;
|
||||
update(nil);
|
||||
break;
|
||||
case 'u':
|
||||
restore(16);
|
||||
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';
|
||||
drawpal();
|
||||
break;
|
||||
default:
|
||||
if(e.kbdc == Kdel)
|
||||
e.kbdc = 'q';
|
||||
buf[0] = 0;
|
||||
if(filename && (e.kbdc == 'r' || e.kbdc == 'w'))
|
||||
snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename);
|
||||
else if(e.kbdc > 0x20 && e.kbdc < 0x7f)
|
||||
snprint(buf, sizeof(buf), "%C", e.kbdc);
|
||||
if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0)
|
||||
break;
|
||||
if(strcmp(buf, "q") == 0)
|
||||
exits(nil);
|
||||
s = buf+1;
|
||||
while(*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
if(*s == 0)
|
||||
break;
|
||||
switch(buf[0]){
|
||||
case 'r':
|
||||
if((fd = open(s, OREAD)) < 0){
|
||||
Error:
|
||||
snprint(buf, sizeof(buf), "%r");
|
||||
eenter(buf, nil, 0, &e.mouse);
|
||||
break;
|
||||
}
|
||||
free(filename);
|
||||
filename = strdup(s);
|
||||
Readimage:
|
||||
unlockdisplay(display);
|
||||
img = readimage(display, fd, 1);
|
||||
close(fd);
|
||||
lockdisplay(display);
|
||||
if(img == nil){
|
||||
werrstr("readimage: %r");
|
||||
goto Error;
|
||||
}
|
||||
if(canvas){
|
||||
save(canvas->r, 1);
|
||||
freeimage(canvas);
|
||||
}
|
||||
canvas = img;
|
||||
center();
|
||||
break;
|
||||
case 'w':
|
||||
if((fd = create(s, OWRITE, 0660)) < 0)
|
||||
goto Error;
|
||||
free(filename);
|
||||
filename = strdup(s);
|
||||
Writeimage:
|
||||
if(canvas)
|
||||
if(writeimage(fd, canvas, 0) < 0){
|
||||
close(fd);
|
||||
werrstr("writeimage: %r");
|
||||
goto Error;
|
||||
}
|
||||
close(fd);
|
||||
break;
|
||||
case '<':
|
||||
if((fd = pipeline("%s", s)) < 0)
|
||||
goto Error;
|
||||
goto Readimage;
|
||||
case '>':
|
||||
if((fd = pipeline("%s", s)) < 0)
|
||||
goto Error;
|
||||
goto Writeimage;
|
||||
case '|':
|
||||
if(canvas == nil)
|
||||
break;
|
||||
if((fd = pipeline("%s", s)) < 0)
|
||||
goto Error;
|
||||
switch(rfork(RFMEM|RFPROC|RFFDG)){
|
||||
case -1:
|
||||
close(fd);
|
||||
werrstr("rfork: %r");
|
||||
goto Error;
|
||||
case 0:
|
||||
writeimage(fd, canvas, 1);
|
||||
exits(nil);
|
||||
}
|
||||
goto Readimage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
eresized(int)
|
||||
{
|
||||
if(getwindow(display, Refnone) < 0)
|
||||
sysfatal("resize failed");
|
||||
}
|
||||
|
||||
int
|
||||
loadimg(char *name)
|
||||
{
|
||||
Image *b;
|
||||
int fd;
|
||||
|
||||
if((fd = open(name, OREAD)) < 0)
|
||||
return -1;
|
||||
if((b = readimage(display, fd, 0)) == nil){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
draw(screen, screen->r, b, 0, b->r.min);
|
||||
flushimage(display, 1);
|
||||
freeimage(b);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
saveimg(char *name)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if((fd = create(name, OWRITE|OTRUNC, 0666)) < 0)
|
||||
return -1;
|
||||
writeimage(fd, screen, 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Event e;
|
||||
Point last;
|
||||
int b = 1;
|
||||
int c = 0;
|
||||
int cn, f;
|
||||
int haslast = 0;
|
||||
char brush[128];
|
||||
char color[NCOLORS];
|
||||
char file[128];
|
||||
char fill[NCOLORS];
|
||||
|
||||
if(initdraw(0, 0, "paint") < 0){
|
||||
fprint(2, "paint: initdraw failed: %r\n");
|
||||
exits("initdraw");
|
||||
}
|
||||
einit(Emouse | Ekeyboard);
|
||||
draw(screen, screen->r, display->white, 0, ZP);
|
||||
flushimage(display, 1);
|
||||
|
||||
colors[0] = display->black;
|
||||
colors[1] = display->white;
|
||||
colors[2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed);
|
||||
colors[3] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreen);
|
||||
colors[4] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlue);
|
||||
colors[5] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
|
||||
|
||||
ARGBEGIN{
|
||||
default:
|
||||
goto Usage;
|
||||
}ARGEND
|
||||
switch(argc){
|
||||
default:
|
||||
Usage:
|
||||
fprint(2, "Usage: [file]\n");
|
||||
exits("usage");
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
snprint(file, sizeof(file), "%s", argv[0]);
|
||||
if(loadimg(file) < 0)
|
||||
sysfatal("%r");
|
||||
break;
|
||||
}
|
||||
|
||||
while(1){
|
||||
switch(event(&e)){
|
||||
case Emouse:
|
||||
if(e.mouse.buttons & 1){
|
||||
if(haslast)
|
||||
line(screen, last, e.mouse.xy, Enddisc, Enddisc, b, colors[c], ZP);
|
||||
else
|
||||
fillellipse(screen, e.mouse.xy, b, b, colors[c], ZP);
|
||||
last = e.mouse.xy;
|
||||
haslast = 1;
|
||||
flushimage(display, 1);
|
||||
} else
|
||||
haslast = 0;
|
||||
break;
|
||||
case Ekeyboard:
|
||||
if(e.kbdc == 'b'){
|
||||
if(eenter("Brush", brush, sizeof(brush), &e.mouse) <= 0)
|
||||
break;
|
||||
b = atoi(brush);
|
||||
}
|
||||
if(e.kbdc == 'c'){
|
||||
if(eenter("Color", color, sizeof(color), &e.mouse) <= 0)
|
||||
break;
|
||||
cn = atoi(color);
|
||||
if(cn >= 0 && cn < NCOLORS)
|
||||
c = cn;
|
||||
}
|
||||
if(e.kbdc == 'f'){
|
||||
if(eenter("Fill", fill, sizeof(fill), &e.mouse) <= 0)
|
||||
break;
|
||||
f = atoi(fill);
|
||||
if(f >= 0 && f < NCOLORS)
|
||||
draw(screen, screen->r, colors[f], 0, ZP);
|
||||
}
|
||||
if(e.kbdc == 'o'){
|
||||
if(eenter("Open file", file, sizeof(file), &e.mouse) <= 0)
|
||||
break;
|
||||
if(loadimg(file) < 0){
|
||||
rerrstr(file, sizeof(file));
|
||||
eenter(file, 0, 0, &e.mouse);
|
||||
}
|
||||
}
|
||||
if(e.kbdc == 'q')
|
||||
exits(nil);
|
||||
if(e.kbdc == 's'){
|
||||
if(eenter("Save to", file, sizeof(file), &e.mouse) <= 0)
|
||||
break;
|
||||
if(saveimg(file) < 0){
|
||||
rerrstr(file, sizeof(file));
|
||||
eenter(file, 0, 0, &e.mouse);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
drawpal();
|
||||
update(nil);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue