plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }