]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/page/gfx.c
merge
[plan9front.git] / sys / src / cmd / page / gfx.c
1 /*
2  * graphics file reading for page
3  */
4
5 #include <u.h>
6 #include <libc.h>
7 #include <draw.h>
8 #include <event.h>
9 #include <bio.h>
10 #include "page.h"
11
12 typedef struct Convert  Convert;
13 typedef struct GfxInfo  GfxInfo;
14 typedef struct Graphic  Graphic;
15
16 struct Convert {
17         char *name;
18         char *cmd;
19         char *truecmd;  /* cmd for true color */
20 };
21
22 struct GfxInfo {
23         Graphic *g;
24 };
25
26 struct Graphic {
27         int type;
28         char *name;
29         uchar *buf;     /* if stdin */
30         int nbuf;
31 };
32
33 enum {
34         Ipic,
35         Itiff,
36         Ijpeg,
37         Igif,
38         Iinferno,
39         Ifax,
40         Icvt2pic,
41         Iplan9bm,
42         Iccittg4,
43         Ippm,
44         Ipng,
45         Iyuv,
46         Ibmp,
47 };
48
49 /*
50  * N.B. These commands need to read stdin if %a is replaced
51  * with an empty string.
52  */
53 Convert cvt[] = {
54 [Ipic]          { "plan9",      "fb/3to1 rgbv %a |fb/pcp -tplan9" },
55 [Itiff]         { "tiff",       "fb/tiff2pic %a | fb/3to1 rgbv | fb/pcp -tplan9" },
56 [Iplan9bm]      { "plan9bm",    nil },
57 [Ijpeg]         { "jpeg",       "jpg -9 %a", "jpg -t9 %a" },
58 [Igif]          { "gif",        "gif -9 %a", "gif -t9 %a" },
59 [Iinferno]      { "inferno",    nil },
60 [Ifax]          { "fax",        "aux/g3p9bit -g %a" },
61 [Icvt2pic]      { "unknown",    "fb/cvt2pic %a |fb/3to1 rgbv" },
62 [Ippm]          { "ppm",        "ppm -9 %a", "ppm -t9 %a" },
63 /* ``temporary'' hack for hobby */
64 [Iccittg4]      { "ccitt-g4",   "cat %a|rx nslocum /usr/lib/ocr/bin/bcp -M|fb/pcp -tcompressed -l0" },
65 [Ipng]          { "png",        "png -9 %a", "png -t9 %a" },
66 [Iyuv]          { "yuv",        "yuv -9 %a", "yuv -t9 %a"  },
67 [Ibmp]          { "bmp",        "bmp -9 %a", "bmp -t9 %a"  },
68 };
69
70 static Image*   convert(Graphic*);
71 static Image*   gfxdrawpage(Document *d, int page);
72 static char*    gfxpagename(Document*, int);
73 static int      spawnrc(char*, uchar*, int);
74 static void     waitrc(void);
75 static int      spawnpost(int);
76 static int      addpage(Document*, char*);
77 static int      rmpage(Document*, int);
78 static int      genaddpage(Document*, char*, uchar*, int);
79
80 static char*
81 gfxpagename(Document *doc, int page)
82 {
83         GfxInfo *gfx = doc->extra;
84         return gfx->g[page].name;
85 }
86
87 static Image*
88 gfxdrawpage(Document *doc, int page)
89 {
90         GfxInfo *gfx = doc->extra;
91         return convert(gfx->g+page);
92 }
93
94 Document*
95 initgfx(Biobuf*, int argc, char **argv, uchar *buf, int nbuf)
96 {
97         GfxInfo *gfx;
98         Document *doc;
99         int i;
100
101         doc = emalloc(sizeof(*doc));
102         gfx = emalloc(sizeof(*gfx));
103         gfx->g = nil;
104         
105         doc->npage = 0;
106         doc->drawpage = gfxdrawpage;
107         doc->pagename = gfxpagename;
108         doc->addpage = addpage;
109         doc->rmpage = rmpage;
110         doc->extra = gfx;
111         doc->fwdonly = 0;
112
113         fprint(2, "reading through graphics...\n");
114         if(argc==0 && buf)
115                 genaddpage(doc, nil, buf, nbuf);
116         else{
117                 for(i=0; i<argc; i++)
118                         if(addpage(doc, argv[i]) < 0)
119                                 fprint(2, "warning: not including %s: %r\n", argv[i]);
120         }
121
122         return doc;
123 }
124
125 static int
126 genaddpage(Document *doc, char *name, uchar *buf, int nbuf)
127 {
128         Graphic *g;
129         GfxInfo *gfx;
130         Biobuf *b;
131         uchar xbuf[32];
132         int i, l;
133
134         l = 0;
135         gfx = doc->extra;
136
137         assert((name == nil) ^ (buf == nil));
138         assert(name != nil || doc->npage == 0);
139
140         for(i=0; i<doc->npage; i++)
141                 if(strcmp(gfx->g[i].name, name) == 0)
142                         return i;
143
144         if(name){
145                 l = strlen(name);
146                 if((b = Bopen(name, OREAD)) == nil) {
147                         werrstr("Bopen: %r");
148                         return -1;
149                 }
150
151                 if(Bread(b, xbuf, sizeof xbuf) != sizeof xbuf) {
152                         werrstr("short read: %r");
153                         return -1;
154                 }
155                 Bterm(b);
156                 buf = xbuf;
157                 nbuf = sizeof xbuf;
158         }
159
160
161         gfx->g = erealloc(gfx->g, (doc->npage+1)*(sizeof(*gfx->g)));
162         g = &gfx->g[doc->npage];
163
164         memset(g, 0, sizeof *g);
165         if(memcmp(buf, "GIF", 3) == 0)
166                 g->type = Igif;
167         else if(memcmp(buf, "\111\111\052\000", 4) == 0) 
168                 g->type = Itiff;
169         else if(memcmp(buf, "\115\115\000\052", 4) == 0)
170                 g->type = Itiff;
171         else if(memcmp(buf, "\377\330\377", 3) == 0)
172                 g->type = Ijpeg;
173         else if(memcmp(buf, "\211PNG\r\n\032\n", 3) == 0)
174                 g->type = Ipng;
175         else if(memcmp(buf, "compressed\n", 11) == 0)
176                 g->type = Iinferno;
177         else if(memcmp(buf, "\0PC Research, Inc", 17) == 0)
178                 g->type = Ifax;
179         else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0)
180                 g->type = Ifax;
181         else if(memcmp(buf, "II*", 3) == 0)
182                 g->type = Ifax;
183         else if(memcmp(buf, "TYPE=ccitt-g4", 13) == 0)
184                 g->type = Iccittg4;
185         else if(memcmp(buf, "TYPE=", 5) == 0)
186                 g->type = Ipic;
187         else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9')
188                 g->type = Ippm;
189         else if(memcmp(buf, "BM", 2) == 0)
190                 g->type = Ibmp;
191         else if(memcmp(buf, "          ", 10) == 0 &&
192                 '0' <= buf[10] && buf[10] <= '9' &&
193                 buf[11] == ' ')
194                 g->type = Iplan9bm;
195         else if(strtochan((char*)buf) != 0)
196                 g->type = Iplan9bm;
197         else if (l > 4 && strcmp(name + l -4, ".yuv") == 0)
198                 g->type = Iyuv;
199         else
200                 g->type = Icvt2pic;
201
202         if(name)
203                 g->name = estrdup(name);
204         else{
205                 g->name = estrdup("stdin");     /* so it can be freed */
206                 g->buf = buf;
207                 g->nbuf = nbuf;
208         }
209
210         if(chatty) fprint(2, "classified \"%s\" as \"%s\"\n", g->name, cvt[g->type].name);
211         return doc->npage++;
212 }
213
214 static int 
215 addpage(Document *doc, char *name)
216 {
217         return genaddpage(doc, name, nil, 0);
218 }
219
220 static int
221 rmpage(Document *doc, int n)
222 {
223         int i;
224         GfxInfo *gfx;
225
226         if(n < 0 || n >= doc->npage)
227                 return -1;
228
229         gfx = doc->extra;
230         doc->npage--;
231         free(gfx->g[n].name);
232
233         for(i=n; i<doc->npage; i++)
234                 gfx->g[i] = gfx->g[i+1];
235
236         if(n < doc->npage)
237                 return n;
238         if(n == 0)
239                 return 0;
240         return n-1;
241 }
242
243
244 static Image*
245 convert(Graphic *g)
246 {
247         int fd;
248         Convert c;
249         char *cmd;
250         char *name, buf[1000];
251         Image *im;
252         int rcspawned = 0;
253         Waitmsg *w;
254
255         c = cvt[g->type];
256         if(c.cmd == nil) {
257                 if(chatty) fprint(2, "no conversion for bitmap \"%s\"...\n", g->name);
258                 if(g->buf == nil){      /* not stdin */
259                         fd = open(g->name, OREAD);
260                         if(fd < 0) {
261                                 fprint(2, "cannot open file: %r\n");
262                                 wexits("open");
263                         }
264                 }else
265                         fd = stdinpipe(g->buf, g->nbuf);        
266         } else {
267                 cmd = c.cmd;
268                 if(truecolor && c.truecmd)
269                         cmd = c.truecmd;
270
271                 if(g->buf != nil)       /* is stdin */
272                         name = "";
273                 else
274                         name = g->name;
275                 if(strlen(cmd)+strlen(name) > sizeof buf) {
276                         fprint(2, "command too long\n");
277                         wexits("convert");
278                 }
279                 snprint(buf, sizeof buf, cmd, name);
280                 if(chatty) fprint(2, "using \"%s\" to convert \"%s\"...\n", buf, g->name);
281                 fd = spawnrc(buf, g->buf, g->nbuf);
282                 rcspawned++;
283                 if(fd < 0) {
284                         fprint(2, "cannot spawn converter: %r\n");
285                         wexits("convert");
286                 }       
287         }
288
289         im = readimage(display, fd, 0);
290         if(im == nil) {
291                 fprint(2, "warning: couldn't read image: %r\n");
292         }
293         close(fd);
294
295         /* for some reason rx doesn't work well with wait */
296         /* for some reason 3to1 exits on success with a non-null status of |3to1 */
297         if(rcspawned && g->type != Iccittg4) {
298                 if((w=wait())!=nil && w->msg[0] && !strstr(w->msg, "3to1"))
299                         fprint(2, "slave wait error: %s\n", w->msg);
300                 free(w);
301         }
302         return im;
303 }
304
305 static int
306 spawnrc(char *cmd, uchar *stdinbuf, int nstdinbuf)
307 {
308         int pfd[2];
309         int pid;
310
311         if(chatty) fprint(2, "spawning(%s)...", cmd);
312
313         if(pipe(pfd) < 0)
314                 return -1;
315         if((pid = fork()) < 0)
316                 return -1;
317
318         if(pid == 0) {
319                 close(pfd[1]);
320                 if(stdinbuf)
321                         dup(stdinpipe(stdinbuf, nstdinbuf), 0);
322                 else
323                         dup(open("/dev/null", OREAD), 0);
324                 dup(pfd[0], 1);
325                 //dup(pfd[0], 2);
326                 execl("/bin/rc", "rc", "-c", cmd, nil);
327                 wexits("exec");
328         }
329         close(pfd[0]);
330         return pfd[1];
331 }
332