jpg.c (7459B)
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; 40 r = rectaddpt(image->clipr, subpt(screen->r.min, image->clipr.min)); 41 if(!new && !winsize) 42 drawresizewindow(r); 43 draw(screen, r, image, nil, image->r.min); 44 flushimage(display, 1); 45 } 46 47 void 48 usage(void) 49 { 50 fprint(2, "usage: jpg -39cdefFkJrtv -W winsize [file.jpg ...]\n"); 51 exits("usage"); 52 } 53 54 void 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{ 63 case 'W': 64 winsize = EARGF(usage()); 65 break; 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: 122 usage(); 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; 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++; 279 } 280 if(defaultcolor && screen->depth>8 && outchan==CMAP8) 281 outchan = RGB24; 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 }