rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 1 | #include <u.h> |
| 2 | #include <libc.h> |
| 3 | #include <bio.h> |
| 4 | #include <draw.h> |
| 5 | #include <event.h> |
| 6 | #include "imagefile.h" |
| 7 | |
| 8 | int cflag = 0; |
| 9 | int dflag = 0; |
| 10 | int eflag = 0; |
| 11 | int jflag = 0; |
| 12 | int fflag = 0; |
| 13 | int Fflag = 0; |
| 14 | int nineflag = 0; |
| 15 | int threeflag = 0; |
| 16 | int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */ |
| 17 | int output = 0; |
| 18 | ulong outchan = CMAP8; |
| 19 | Image *image; |
| 20 | int defaultcolor = 1; |
| 21 | |
| 22 | enum{ |
| 23 | Border = 2, |
| 24 | Edge = 5 |
| 25 | }; |
| 26 | |
| 27 | char *show(int, char*, int); |
| 28 | |
| 29 | void |
| 30 | eresized(int new) |
| 31 | { |
| 32 | Rectangle r; |
| 33 | |
| 34 | if(new && getwindow(display, Refnone) < 0){ |
| 35 | fprint(2, "jpg: can't reattach to window\n"); |
| 36 | exits("resize"); |
| 37 | } |
| 38 | if(image == nil) |
| 39 | return; |
rsc | 846ec3d | 2006-02-15 12:11:03 +0000 | [diff] [blame] | 40 | r = rectaddpt(image->clipr, subpt(screen->r.min, image->clipr.min)); |
rsc | 28a8042 | 2006-03-19 06:11:51 +0000 | [diff] [blame] | 41 | if(!new && !winsize) |
rsc | 846ec3d | 2006-02-15 12:11:03 +0000 | [diff] [blame] | 42 | drawresizewindow(r); |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 43 | draw(screen, r, image, nil, image->r.min); |
| 44 | flushimage(display, 1); |
| 45 | } |
| 46 | |
| 47 | void |
rsc | be36ff6 | 2004-04-29 17:13:24 +0000 | [diff] [blame] | 48 | usage(void) |
| 49 | { |
| 50 | fprint(2, "usage: jpg -39cdefFkJrtv -W winsize [file.jpg ...]\n"); |
| 51 | exits("usage"); |
| 52 | } |
| 53 | |
| 54 | void |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 55 | main(int argc, char *argv[]) |
| 56 | { |
| 57 | int fd, i, yflag; |
| 58 | char *err; |
| 59 | char buf[12+1]; |
| 60 | |
| 61 | yflag = 0; |
| 62 | ARGBEGIN{ |
rsc | be36ff6 | 2004-04-29 17:13:24 +0000 | [diff] [blame] | 63 | case 'W': |
| 64 | winsize = EARGF(usage()); |
| 65 | break; |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 66 | case 'c': /* produce encoded, compressed, bitmap file; no display by default */ |
| 67 | cflag++; |
| 68 | dflag++; |
| 69 | output++; |
| 70 | if(defaultcolor) |
| 71 | outchan = CMAP8; |
| 72 | break; |
| 73 | case 'd': /* suppress display of image */ |
| 74 | dflag++; |
| 75 | break; |
| 76 | case 'e': /* disable floyd-steinberg error diffusion */ |
| 77 | eflag++; |
| 78 | break; |
| 79 | case 'F': |
| 80 | Fflag++; /* make a movie */ |
| 81 | fflag++; /* merge two fields per image */ |
| 82 | break; |
| 83 | case 'f': |
| 84 | fflag++; /* merge two fields per image */ |
| 85 | break; |
| 86 | case 'J': /* decode jpeg only; no display or remap (for debugging, etc.) */ |
| 87 | jflag++; |
| 88 | break; |
| 89 | case 'k': /* force black and white */ |
| 90 | defaultcolor = 0; |
| 91 | outchan = GREY8; |
| 92 | break; |
| 93 | case 'r': |
| 94 | colorspace = CRGB; |
| 95 | break; |
| 96 | case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ |
| 97 | threeflag++; |
| 98 | /* fall through */ |
| 99 | case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ |
| 100 | cflag++; |
| 101 | dflag++; |
| 102 | output++; |
| 103 | defaultcolor = 0; |
| 104 | outchan = RGB24; |
| 105 | break; |
| 106 | case 'v': /* force RGBV */ |
| 107 | defaultcolor = 0; |
| 108 | outchan = CMAP8; |
| 109 | break; |
| 110 | case 'y': /* leave it in CYCbCr; for debugging only */ |
| 111 | yflag = 1; |
| 112 | colorspace = CYCbCr; |
| 113 | break; |
| 114 | case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ |
| 115 | nineflag++; |
| 116 | dflag++; |
| 117 | output++; |
| 118 | if(defaultcolor) |
| 119 | outchan = CMAP8; |
| 120 | break; |
| 121 | default: |
rsc | be36ff6 | 2004-04-29 17:13:24 +0000 | [diff] [blame] | 122 | usage(); |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 123 | }ARGEND; |
| 124 | |
| 125 | if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */ |
| 126 | fd = open("/dev/screen", OREAD); |
| 127 | if(fd > 0){ |
| 128 | buf[12] = '\0'; |
| 129 | if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8) |
| 130 | colorspace = CRGB; |
| 131 | close(fd); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | err = nil; |
| 136 | if(argc == 0) |
| 137 | err = show(0, "<stdin>", outchan); |
| 138 | else{ |
| 139 | for(i=0; i<argc; i++){ |
| 140 | fd = open(argv[i], OREAD); |
| 141 | if(fd < 0){ |
| 142 | fprint(2, "jpg: can't open %s: %r\n", argv[i]); |
| 143 | err = "open"; |
| 144 | }else{ |
| 145 | err = show(fd, argv[i], outchan); |
| 146 | close(fd); |
| 147 | } |
| 148 | if((nineflag || cflag) && argc>1 && err==nil){ |
| 149 | fprint(2, "jpg: exiting after one file\n"); |
| 150 | break; |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | exits(err); |
| 155 | } |
| 156 | |
| 157 | Rawimage** |
| 158 | vidmerge(Rawimage **aa1, Rawimage **aa2) |
| 159 | { |
| 160 | Rawimage **aao, *ao, *a1, *a2; |
| 161 | int i, c, row, col; |
| 162 | |
| 163 | aao = nil; |
| 164 | for (i = 0; aa1[i]; i++) { |
| 165 | |
| 166 | a1 = aa1[i]; |
| 167 | a2 = aa2[i]; |
| 168 | if (a2 == nil){ |
| 169 | fprint(2, "jpg: vidmerge: unequal lengths\n"); |
| 170 | return nil; |
| 171 | } |
| 172 | aao = realloc(aao, (i+2)*sizeof(Rawimage *)); |
| 173 | if (aao == nil){ |
| 174 | fprint(2, "jpg: vidmerge: realloc\n"); |
| 175 | return nil; |
| 176 | } |
| 177 | aao[i+1] = nil; |
| 178 | ao = aao[i] = malloc(sizeof(Rawimage)); |
| 179 | if (ao == nil){ |
| 180 | fprint(2, "jpg: vidmerge: realloc\n"); |
| 181 | return nil; |
| 182 | } |
| 183 | memcpy(ao, a1, sizeof(Rawimage)); |
| 184 | if (!eqrect(a1->r , a2->r)){ |
| 185 | fprint(2, "jpg: vidmerge: rects different in img %d\n", i); |
| 186 | return nil; |
| 187 | } |
| 188 | if (a1->cmaplen != a2->cmaplen){ |
| 189 | fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i); |
| 190 | return nil; |
| 191 | } |
| 192 | if (a1->nchans != a2->nchans){ |
| 193 | fprint(2, "jpg: vidmerge: nchans different in img %d\n", i); |
| 194 | return nil; |
| 195 | } |
| 196 | if (a1->fields != a2->fields){ |
| 197 | fprint(2, "jpg: vidmerge: fields different in img %d\n", i); |
| 198 | return nil; |
| 199 | } |
| 200 | ao->r.max.y += Dy(ao->r); |
| 201 | ao->chanlen += ao->chanlen; |
| 202 | if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){ |
| 203 | fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n", |
| 204 | ao->chanlen, Dx(ao->r), Dy(ao->r)); |
| 205 | return nil; |
| 206 | } |
| 207 | row = Dx(a1->r); |
| 208 | for (c = 0; c < ao->nchans; c++) { |
| 209 | uchar *po, *p1, *p2; |
| 210 | |
| 211 | ao->chans[c] = malloc(ao->chanlen); |
| 212 | po = ao->chans[c]; |
| 213 | p1 = a1->chans[c]; |
| 214 | p2 = a2->chans[c]; |
| 215 | for (col = 0; col < Dy(a1->r); col++) { |
| 216 | memcpy(po, p1, row); |
| 217 | po += row, p1 += row; |
| 218 | memcpy(po, p2, row); |
| 219 | po += row, p2 += row; |
| 220 | } |
| 221 | free(a1->chans[c]); |
| 222 | free(a2->chans[c]); |
| 223 | } |
| 224 | if(a2->cmap != nil) |
| 225 | free(a2->cmap); |
| 226 | free(a1); |
| 227 | free(a2); |
| 228 | } |
| 229 | if (aa2[i] != nil) |
| 230 | fprint(2, "jpg: vidmerge: unequal lengths\n"); |
| 231 | free(aa1); |
| 232 | free(aa2); |
| 233 | return aao; |
| 234 | } |
| 235 | |
| 236 | char* |
| 237 | show(int fd, char *name, int outc) |
| 238 | { |
| 239 | Rawimage **array, *r, *c; |
| 240 | static int inited; |
| 241 | Image *i; |
| 242 | int j, ch, outchan; |
| 243 | Biobuf b; |
| 244 | char buf[32]; |
| 245 | |
| 246 | if(Binit(&b, fd, OREAD) < 0) |
| 247 | return nil; |
| 248 | outchan = outc; |
| 249 | rpt: array = Breadjpg(&b, colorspace); |
| 250 | if(array == nil || array[0]==nil){ |
| 251 | fprint(2, "jpg: decode %s failed: %r\n", name); |
| 252 | return "decode"; |
| 253 | } |
| 254 | if (fflag) { |
| 255 | Rawimage **a; |
| 256 | |
| 257 | a = Breadjpg(&b, colorspace); |
| 258 | if(a == nil || a[0]==nil){ |
| 259 | fprint(2, "jpg: decode %s-2 failed: %r\n", name); |
| 260 | return "decode"; |
| 261 | } |
| 262 | array = vidmerge(a, array); |
| 263 | } else |
| 264 | Bterm(&b); |
| 265 | |
| 266 | r = array[0]; |
| 267 | c = nil; |
| 268 | if(jflag) |
| 269 | goto Return; |
rsc | d1e25ee | 2005-09-15 17:09:40 +0000 | [diff] [blame] | 270 | if(!dflag){ |
| 271 | if(!inited){ |
| 272 | if(initdraw(0, 0, 0) < 0){ |
| 273 | fprint(2, "jpg: initdraw failed: %r\n"); |
| 274 | return "initdraw"; |
| 275 | } |
| 276 | if(Fflag == 0) |
| 277 | einit(Ekeyboard|Emouse); |
| 278 | inited++; |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 279 | } |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 280 | if(defaultcolor && screen->depth>8 && outchan==CMAP8) |
| 281 | outchan = RGB24; |
rsc | 2899450 | 2004-04-21 22:19:33 +0000 | [diff] [blame] | 282 | } |
| 283 | if(outchan == CMAP8) |
| 284 | c = torgbv(r, !eflag); |
| 285 | else{ |
| 286 | if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){ |
| 287 | c = totruecolor(r, CY); |
| 288 | outchan = GREY8; |
| 289 | }else |
| 290 | c = totruecolor(r, CRGB24); |
| 291 | } |
| 292 | if(c == nil){ |
| 293 | fprint(2, "jpg: conversion of %s failed: %r\n", name); |
| 294 | return "torgbv"; |
| 295 | } |
| 296 | if(!dflag){ |
| 297 | if(c->chandesc == CY) |
| 298 | i = allocimage(display, c->r, GREY8, 0, 0); |
| 299 | else |
| 300 | i = allocimage(display, c->r, outchan, 0, 0); |
| 301 | if(i == nil){ |
| 302 | fprint(2, "jpg: allocimage %s failed: %r\n", name); |
| 303 | return "allocimage"; |
| 304 | } |
| 305 | if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ |
| 306 | fprint(2, "jpg: loadimage %s failed: %r\n", name); |
| 307 | return "loadimage"; |
| 308 | } |
| 309 | image = i; |
| 310 | eresized(0); |
| 311 | if (Fflag) { |
| 312 | freeimage(i); |
| 313 | for(j=0; j<r->nchans; j++) |
| 314 | free(r->chans[j]); |
| 315 | free(r->cmap); |
| 316 | free(r); |
| 317 | free(array); |
| 318 | goto rpt; |
| 319 | } |
| 320 | if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) |
| 321 | exits(nil); |
| 322 | draw(screen, screen->clipr, display->white, nil, ZP); |
| 323 | image = nil; |
| 324 | freeimage(i); |
| 325 | } |
| 326 | if(nineflag){ |
| 327 | chantostr(buf, outchan); |
| 328 | print("%11s %11d %11d %11d %11d ", buf, |
| 329 | c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y); |
| 330 | if(write(1, c->chans[0], c->chanlen) != c->chanlen){ |
| 331 | fprint(2, "jpg: %s: write error %r\n", name); |
| 332 | return "write"; |
| 333 | } |
| 334 | }else if(cflag){ |
| 335 | if(writerawimage(1, c) < 0){ |
| 336 | fprint(2, "jpg: %s: write error: %r\n", name); |
| 337 | return "write"; |
| 338 | } |
| 339 | } |
| 340 | Return: |
| 341 | for(j=0; j<r->nchans; j++) |
| 342 | free(r->chans[j]); |
| 343 | free(r->cmap); |
| 344 | free(r); |
| 345 | free(array); |
| 346 | if(c){ |
| 347 | free(c->chans[0]); |
| 348 | free(c); |
| 349 | } |
| 350 | if (Fflag) goto rpt; |
| 351 | return nil; |
| 352 | } |