xref: /plan9/sys/src/cmd/jpg/jpg.c (revision c35931e294c3b1d49a408159df49beb0cfbb8c50)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <draw.h>
57dd7cddfSDavid du Colombier #include <event.h>
67dd7cddfSDavid du Colombier #include "imagefile.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier int		cflag = 0;
97dd7cddfSDavid du Colombier int		dflag = 0;
107dd7cddfSDavid du Colombier int		eflag = 0;
117dd7cddfSDavid du Colombier int		jflag = 0;
127dd7cddfSDavid du Colombier int		fflag = 0;
137dd7cddfSDavid du Colombier int		Fflag = 0;
147dd7cddfSDavid du Colombier int		nineflag = 0;
157dd7cddfSDavid du Colombier int		threeflag = 0;
167dd7cddfSDavid du Colombier int		colorspace = CYCbCr;	/* default for 8-bit displays: combine color rotation with dither */
177dd7cddfSDavid du Colombier int		output = 0;
187dd7cddfSDavid du Colombier ulong	outchan = CMAP8;
197dd7cddfSDavid du Colombier Image	*image;
207dd7cddfSDavid du Colombier int		defaultcolor = 1;
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier enum{
237dd7cddfSDavid du Colombier 	Border	= 2,
247dd7cddfSDavid du Colombier 	Edge		= 5
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier char	*show(int, char*, int);
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier void
eresized(int new)307dd7cddfSDavid du Colombier eresized(int new)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier 	Rectangle r;
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0){
357dd7cddfSDavid du Colombier 		fprint(2, "jpg: can't reattach to window\n");
367dd7cddfSDavid du Colombier 		exits("resize");
377dd7cddfSDavid du Colombier 	}
387dd7cddfSDavid du Colombier 	if(image == nil)
397dd7cddfSDavid du Colombier 		return;
407dd7cddfSDavid du Colombier 	r = insetrect(screen->clipr, Edge+Border);
417dd7cddfSDavid du Colombier 	r.max.x = r.min.x+Dx(image->r);
427dd7cddfSDavid du Colombier 	r.max.y = r.min.y+Dy(image->r);
437dd7cddfSDavid du Colombier 	border(screen, r, -Border, nil, ZP);
449863c128SDavid du Colombier 	drawop(screen, r, image, nil, image->r.min, S);
457dd7cddfSDavid du Colombier 	flushimage(display, 1);
467dd7cddfSDavid du Colombier }
477dd7cddfSDavid du Colombier 
487dd7cddfSDavid du Colombier void
main(int argc,char * argv[])497dd7cddfSDavid du Colombier main(int argc, char *argv[])
507dd7cddfSDavid du Colombier {
517dd7cddfSDavid du Colombier 	int fd, i, yflag;
527dd7cddfSDavid du Colombier 	char *err;
537dd7cddfSDavid du Colombier 	char buf[12+1];
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier 	yflag = 0;
567dd7cddfSDavid du Colombier 	ARGBEGIN{
577dd7cddfSDavid du Colombier 	case 'c':		/* produce encoded, compressed, bitmap file; no display by default */
587dd7cddfSDavid du Colombier 		cflag++;
597dd7cddfSDavid du Colombier 		dflag++;
607dd7cddfSDavid du Colombier 		output++;
617dd7cddfSDavid du Colombier 		if(defaultcolor)
627dd7cddfSDavid du Colombier 			outchan = CMAP8;
637dd7cddfSDavid du Colombier 		break;
647dd7cddfSDavid du Colombier 	case 'd':		/* suppress display of image */
657dd7cddfSDavid du Colombier 		dflag++;
667dd7cddfSDavid du Colombier 		break;
677dd7cddfSDavid du Colombier 	case 'e':		/* disable floyd-steinberg error diffusion */
687dd7cddfSDavid du Colombier 		eflag++;
697dd7cddfSDavid du Colombier 		break;
707dd7cddfSDavid du Colombier 	case 'F':
717dd7cddfSDavid du Colombier 		Fflag++;	/* make a movie */
727dd7cddfSDavid du Colombier 		fflag++;	/* merge two fields per image */
737dd7cddfSDavid du Colombier 		break;
747dd7cddfSDavid du Colombier 	case 'f':
757dd7cddfSDavid du Colombier 		fflag++;	/* merge two fields per image */
767dd7cddfSDavid du Colombier 		break;
777dd7cddfSDavid du Colombier 	case 'J':		/* decode jpeg only; no display or remap (for debugging, etc.) */
787dd7cddfSDavid du Colombier 		jflag++;
797dd7cddfSDavid du Colombier 		break;
807dd7cddfSDavid du Colombier 	case 'k':		/* force black and white */
817dd7cddfSDavid du Colombier 		defaultcolor = 0;
827dd7cddfSDavid du Colombier 		outchan = GREY8;
837dd7cddfSDavid du Colombier 		break;
847dd7cddfSDavid du Colombier 	case 'r':
857dd7cddfSDavid du Colombier 		colorspace = CRGB;
867dd7cddfSDavid du Colombier 		break;
877dd7cddfSDavid du Colombier 	case '3':		/* produce encoded, compressed, three-color bitmap file; no display by default */
887dd7cddfSDavid du Colombier 		threeflag++;
897dd7cddfSDavid du Colombier 		/* fall through */
907dd7cddfSDavid du Colombier 	case 't':		/* produce encoded, compressed, true-color bitmap file; no display by default */
917dd7cddfSDavid du Colombier 		cflag++;
927dd7cddfSDavid du Colombier 		dflag++;
937dd7cddfSDavid du Colombier 		output++;
947dd7cddfSDavid du Colombier 		defaultcolor = 0;
957dd7cddfSDavid du Colombier 		outchan = RGB24;
967dd7cddfSDavid du Colombier 		break;
977dd7cddfSDavid du Colombier 	case 'v':		/* force RGBV */
987dd7cddfSDavid du Colombier 		defaultcolor = 0;
997dd7cddfSDavid du Colombier 		outchan = CMAP8;
1007dd7cddfSDavid du Colombier 		break;
1017dd7cddfSDavid du Colombier 	case 'y':	/* leave it in CYCbCr; for debugging only */
1027dd7cddfSDavid du Colombier 		yflag = 1;
1037dd7cddfSDavid du Colombier 		colorspace = CYCbCr;
1047dd7cddfSDavid du Colombier 		break;
1057dd7cddfSDavid du Colombier 	case '9':		/* produce plan 9, uncompressed, bitmap file; no display by default */
1067dd7cddfSDavid du Colombier 		nineflag++;
1077dd7cddfSDavid du Colombier 		dflag++;
1087dd7cddfSDavid du Colombier 		output++;
1097dd7cddfSDavid du Colombier 		if(defaultcolor)
1107dd7cddfSDavid du Colombier 			outchan = CMAP8;
1117dd7cddfSDavid du Colombier 		break;
1127dd7cddfSDavid du Colombier 	default:
1137dd7cddfSDavid du Colombier 		fprint(2, "usage: jpg -39cdefFkJrtv [file.jpg ...]\n");
1147dd7cddfSDavid du Colombier 		exits("usage");
1157dd7cddfSDavid du Colombier 	}ARGEND;
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	if(yflag==0 && dflag==0 && colorspace==CYCbCr){	/* see if we should convert right to RGB */
1187dd7cddfSDavid du Colombier 		fd = open("/dev/screen", OREAD);
1197dd7cddfSDavid du Colombier 		if(fd > 0){
1207dd7cddfSDavid du Colombier 			buf[12] = '\0';
1217dd7cddfSDavid du Colombier 			if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
1227dd7cddfSDavid du Colombier 				colorspace = CRGB;
1237dd7cddfSDavid du Colombier 			close(fd);
1247dd7cddfSDavid du Colombier 		}
1257dd7cddfSDavid du Colombier 	}
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier 	err = nil;
1287dd7cddfSDavid du Colombier 	if(argc == 0)
1297dd7cddfSDavid du Colombier 		err = show(0, "<stdin>", outchan);
1307dd7cddfSDavid du Colombier 	else{
1317dd7cddfSDavid du Colombier 		for(i=0; i<argc; i++){
1327dd7cddfSDavid du Colombier 			fd = open(argv[i], OREAD);
1337dd7cddfSDavid du Colombier 			if(fd < 0){
1347dd7cddfSDavid du Colombier 				fprint(2, "jpg: can't open %s: %r\n", argv[i]);
1357dd7cddfSDavid du Colombier 				err = "open";
1367dd7cddfSDavid du Colombier 			}else{
1377dd7cddfSDavid du Colombier 				err = show(fd, argv[i], outchan);
1387dd7cddfSDavid du Colombier 				close(fd);
1397dd7cddfSDavid du Colombier 			}
1407dd7cddfSDavid du Colombier 			if((nineflag || cflag) && argc>1 && err==nil){
1417dd7cddfSDavid du Colombier 				fprint(2, "jpg: exiting after one file\n");
1427dd7cddfSDavid du Colombier 				break;
1437dd7cddfSDavid du Colombier 			}
1447dd7cddfSDavid du Colombier 		}
1457dd7cddfSDavid du Colombier 	}
1467dd7cddfSDavid du Colombier 	exits(err);
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier Rawimage**
vidmerge(Rawimage ** aa1,Rawimage ** aa2)1507dd7cddfSDavid du Colombier vidmerge(Rawimage **aa1, Rawimage **aa2)
1517dd7cddfSDavid du Colombier {
1527dd7cddfSDavid du Colombier 	Rawimage **aao, *ao, *a1, *a2;
1537dd7cddfSDavid du Colombier 	int i, c, row, col;
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 	aao = nil;
1567dd7cddfSDavid du Colombier 	for (i = 0; aa1[i]; i++) {
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 		a1 = aa1[i];
1597dd7cddfSDavid du Colombier 		a2 = aa2[i];
1607dd7cddfSDavid du Colombier 		if (a2 == nil){
1617dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: unequal lengths\n");
1627dd7cddfSDavid du Colombier 			return nil;
1637dd7cddfSDavid du Colombier 		}
1647dd7cddfSDavid du Colombier 		aao = realloc(aao, (i+2)*sizeof(Rawimage *));
1657dd7cddfSDavid du Colombier 		if (aao == nil){
1667dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: realloc\n");
1677dd7cddfSDavid du Colombier 			return nil;
1687dd7cddfSDavid du Colombier 		}
1697dd7cddfSDavid du Colombier 		aao[i+1] = nil;
1707dd7cddfSDavid du Colombier 		ao = aao[i] = malloc(sizeof(Rawimage));
1717dd7cddfSDavid du Colombier 		if (ao == nil){
1727dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: realloc\n");
1737dd7cddfSDavid du Colombier 			return nil;
1747dd7cddfSDavid du Colombier 		}
1757dd7cddfSDavid du Colombier 		memcpy(ao, a1, sizeof(Rawimage));
1767dd7cddfSDavid du Colombier 		if (!eqrect(a1->r , a2->r)){
1777dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: rects different in img %d\n", i);
1787dd7cddfSDavid du Colombier 			return nil;
1797dd7cddfSDavid du Colombier 		}
1807dd7cddfSDavid du Colombier 		if (a1->cmaplen != a2->cmaplen){
1817dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i);
1827dd7cddfSDavid du Colombier 			return nil;
1837dd7cddfSDavid du Colombier 		}
1847dd7cddfSDavid du Colombier 		if (a1->nchans != a2->nchans){
1857dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: nchans different in img %d\n", i);
1867dd7cddfSDavid du Colombier 			return nil;
1877dd7cddfSDavid du Colombier 		}
1887dd7cddfSDavid du Colombier 		if (a1->fields != a2->fields){
1897dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: fields different in img %d\n", i);
1907dd7cddfSDavid du Colombier 			return nil;
1917dd7cddfSDavid du Colombier 		}
1927dd7cddfSDavid du Colombier 		ao->r.max.y += Dy(ao->r);
1937dd7cddfSDavid du Colombier 		ao->chanlen += ao->chanlen;
1947dd7cddfSDavid du Colombier 		if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){
1957dd7cddfSDavid du Colombier 			fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n",
1967dd7cddfSDavid du Colombier 				ao->chanlen, Dx(ao->r), Dy(ao->r));
1977dd7cddfSDavid du Colombier 			return nil;
1987dd7cddfSDavid du Colombier 		}
1997dd7cddfSDavid du Colombier 		row = Dx(a1->r);
2007dd7cddfSDavid du Colombier 		for (c = 0; c < ao->nchans; c++) {
2017dd7cddfSDavid du Colombier 			uchar *po, *p1, *p2;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 			ao->chans[c] = malloc(ao->chanlen);
2047dd7cddfSDavid du Colombier 			po = ao->chans[c];
2057dd7cddfSDavid du Colombier 			p1 = a1->chans[c];
2067dd7cddfSDavid du Colombier 			p2 = a2->chans[c];
2077dd7cddfSDavid du Colombier 			for (col = 0; col < Dy(a1->r); col++) {
2087dd7cddfSDavid du Colombier 				memcpy(po, p1, row);
2097dd7cddfSDavid du Colombier 				po += row, p1 += row;
2107dd7cddfSDavid du Colombier 				memcpy(po, p2, row);
2117dd7cddfSDavid du Colombier 				po += row, p2 += row;
2127dd7cddfSDavid du Colombier 			}
2137dd7cddfSDavid du Colombier 			free(a1->chans[c]);
2147dd7cddfSDavid du Colombier 			free(a2->chans[c]);
2157dd7cddfSDavid du Colombier 		}
2169a747e4fSDavid du Colombier 		if(a2->cmap != nil)
2179a747e4fSDavid du Colombier 			free(a2->cmap);
2187dd7cddfSDavid du Colombier 		free(a1);
2197dd7cddfSDavid du Colombier 		free(a2);
2207dd7cddfSDavid du Colombier 	}
2219a747e4fSDavid du Colombier 	if (aa2[i] != nil)
2227dd7cddfSDavid du Colombier 		fprint(2, "jpg: vidmerge: unequal lengths\n");
2237dd7cddfSDavid du Colombier 	free(aa1);
2247dd7cddfSDavid du Colombier 	free(aa2);
2257dd7cddfSDavid du Colombier 	return aao;
2267dd7cddfSDavid du Colombier }
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier char*
show(int fd,char * name,int outc)2297dd7cddfSDavid du Colombier show(int fd, char *name, int outc)
2307dd7cddfSDavid du Colombier {
2317dd7cddfSDavid du Colombier 	Rawimage **array, *r, *c;
2327dd7cddfSDavid du Colombier 	static int inited;
2337dd7cddfSDavid du Colombier 	Image *i;
2347dd7cddfSDavid du Colombier 	int j, ch, outchan;
2357dd7cddfSDavid du Colombier 	Biobuf b;
2367dd7cddfSDavid du Colombier 	char buf[32];
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	if(Binit(&b, fd, OREAD) < 0)
2397dd7cddfSDavid du Colombier 		return nil;
2407dd7cddfSDavid du Colombier 	outchan = outc;
2417dd7cddfSDavid du Colombier rpt:	array = Breadjpg(&b, colorspace);
2427dd7cddfSDavid du Colombier 	if(array == nil || array[0]==nil){
2437dd7cddfSDavid du Colombier 		fprint(2, "jpg: decode %s failed: %r\n", name);
2447dd7cddfSDavid du Colombier 		return "decode";
2457dd7cddfSDavid du Colombier 	}
2467dd7cddfSDavid du Colombier 	if (fflag) {
2477dd7cddfSDavid du Colombier 		Rawimage **a;
2487dd7cddfSDavid du Colombier 
2497dd7cddfSDavid du Colombier 		a = Breadjpg(&b, colorspace);
2507dd7cddfSDavid du Colombier 		if(a == nil || a[0]==nil){
2517dd7cddfSDavid du Colombier 			fprint(2, "jpg: decode %s-2 failed: %r\n", name);
2527dd7cddfSDavid du Colombier 			return "decode";
2537dd7cddfSDavid du Colombier 		}
2547dd7cddfSDavid du Colombier 		array = vidmerge(a, array);
2557dd7cddfSDavid du Colombier 	} else
2567dd7cddfSDavid du Colombier 		Bterm(&b);
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	r = array[0];
2597dd7cddfSDavid du Colombier 	c = nil;
2607dd7cddfSDavid du Colombier 	if(jflag)
2617dd7cddfSDavid du Colombier 		goto Return;
262*c35931e2SDavid du Colombier 	if(!dflag){
263*c35931e2SDavid du Colombier 		if (!inited) {
2647dd7cddfSDavid du Colombier 			if(initdraw(0, 0, 0) < 0){
2657dd7cddfSDavid du Colombier 				fprint(2, "jpg: initdraw failed: %r\n");
2667dd7cddfSDavid du Colombier 				return "initdraw";
2677dd7cddfSDavid du Colombier 			}
2687dd7cddfSDavid du Colombier 			if(Fflag == 0)
2697dd7cddfSDavid du Colombier 				einit(Ekeyboard|Emouse);
270*c35931e2SDavid du Colombier 			inited++;
271*c35931e2SDavid du Colombier 		}
2727dd7cddfSDavid du Colombier 		if(defaultcolor && screen->depth>8 && outchan==CMAP8)
2737dd7cddfSDavid du Colombier 			outchan = RGB24;
2747dd7cddfSDavid du Colombier 	}
2757dd7cddfSDavid du Colombier 	if(outchan == CMAP8)
2767dd7cddfSDavid du Colombier 		c = torgbv(r, !eflag);
2777dd7cddfSDavid du Colombier 	else{
2787dd7cddfSDavid du Colombier 		if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
2797dd7cddfSDavid du Colombier 			c = totruecolor(r, CY);
2807dd7cddfSDavid du Colombier 			outchan = GREY8;
2817dd7cddfSDavid du Colombier 		}else
2827dd7cddfSDavid du Colombier 			c = totruecolor(r, CRGB24);
2837dd7cddfSDavid du Colombier 	}
2847dd7cddfSDavid du Colombier 	if(c == nil){
2857dd7cddfSDavid du Colombier 		fprint(2, "jpg: conversion of %s failed: %r\n", name);
2867dd7cddfSDavid du Colombier 		return "torgbv";
2877dd7cddfSDavid du Colombier 	}
2887dd7cddfSDavid du Colombier 	if(!dflag){
2897dd7cddfSDavid du Colombier 		if(c->chandesc == CY)
2907dd7cddfSDavid du Colombier 			i = allocimage(display, c->r, GREY8, 0, 0);
2917dd7cddfSDavid du Colombier 		else
2927dd7cddfSDavid du Colombier 			i = allocimage(display, c->r, outchan, 0, 0);
2937dd7cddfSDavid du Colombier 		if(i == nil){
2947dd7cddfSDavid du Colombier 			fprint(2, "jpg: allocimage %s failed: %r\n", name);
2957dd7cddfSDavid du Colombier 			return "allocimage";
2967dd7cddfSDavid du Colombier 		}
2977dd7cddfSDavid du Colombier 		if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
2987dd7cddfSDavid du Colombier 			fprint(2, "jpg: loadimage %s failed: %r\n", name);
2997dd7cddfSDavid du Colombier 			return "loadimage";
3007dd7cddfSDavid du Colombier 		}
3017dd7cddfSDavid du Colombier 		image = i;
3027dd7cddfSDavid du Colombier 		eresized(0);
3037dd7cddfSDavid du Colombier 		if (Fflag) {
3047dd7cddfSDavid du Colombier 			freeimage(i);
3057dd7cddfSDavid du Colombier 			for(j=0; j<r->nchans; j++)
3067dd7cddfSDavid du Colombier 				free(r->chans[j]);
3077dd7cddfSDavid du Colombier 			free(r->cmap);
3087dd7cddfSDavid du Colombier 			free(r);
3097dd7cddfSDavid du Colombier 			free(array);
3107dd7cddfSDavid du Colombier 			goto rpt;
3117dd7cddfSDavid du Colombier 		}
3127dd7cddfSDavid du Colombier 		if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
3137dd7cddfSDavid du Colombier 			exits(nil);
3147dd7cddfSDavid du Colombier 		draw(screen, screen->clipr, display->white, nil, ZP);
3157dd7cddfSDavid du Colombier 		image = nil;
3167dd7cddfSDavid du Colombier 		freeimage(i);
3177dd7cddfSDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 	if(nineflag){
3197dd7cddfSDavid du Colombier 		chantostr(buf, outchan);
3207dd7cddfSDavid du Colombier 		print("%11s %11d %11d %11d %11d ", buf,
3217dd7cddfSDavid du Colombier 			c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
3227dd7cddfSDavid du Colombier 		if(write(1, c->chans[0], c->chanlen) != c->chanlen){
3237dd7cddfSDavid du Colombier 			fprint(2, "jpg: %s: write error %r\n", name);
3247dd7cddfSDavid du Colombier 			return "write";
3257dd7cddfSDavid du Colombier 		}
3267dd7cddfSDavid du Colombier 	}else if(cflag){
3277dd7cddfSDavid du Colombier 		if(writerawimage(1, c) < 0){
3287dd7cddfSDavid du Colombier 			fprint(2, "jpg: %s: write error: %r\n", name);
3297dd7cddfSDavid du Colombier 			return "write";
3307dd7cddfSDavid du Colombier 		}
3317dd7cddfSDavid du Colombier 	}
3327dd7cddfSDavid du Colombier     Return:
3337dd7cddfSDavid du Colombier 	for(j=0; j<r->nchans; j++)
3347dd7cddfSDavid du Colombier 		free(r->chans[j]);
3357dd7cddfSDavid du Colombier 	free(r->cmap);
3367dd7cddfSDavid du Colombier 	free(r);
3377dd7cddfSDavid du Colombier 	free(array);
3387dd7cddfSDavid du Colombier 	if(c){
3397dd7cddfSDavid du Colombier 		free(c->chans[0]);
3407dd7cddfSDavid du Colombier 		free(c);
3417dd7cddfSDavid du Colombier 	}
3427dd7cddfSDavid du Colombier 	if (Fflag) goto rpt;
3437dd7cddfSDavid du Colombier 	return nil;
3447dd7cddfSDavid du Colombier }
345