xref: /plan9/sys/src/cmd/jpg/torgbv.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 "imagefile.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier #include "rgbv.h"
87dd7cddfSDavid du Colombier #include "ycbcr.h"
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier #define	CLAMPOFF 128
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier static	int	clamp[CLAMPOFF+256+CLAMPOFF];
137dd7cddfSDavid du Colombier static	int	inited;
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier void*
_remaperror(char * fmt,...)167dd7cddfSDavid du Colombier _remaperror(char *fmt, ...)
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	va_list arg;
197dd7cddfSDavid du Colombier 	char buf[256];
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier 	va_start(arg, fmt);
229a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof buf, fmt, arg);
237dd7cddfSDavid du Colombier 	va_end(arg);
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier 	werrstr(buf);
267dd7cddfSDavid du Colombier 	return nil;
277dd7cddfSDavid du Colombier }
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier Rawimage*
torgbv(Rawimage * i,int errdiff)307dd7cddfSDavid du Colombier torgbv(Rawimage *i, int errdiff)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier 	int j, k, rgb, x, y, er, eg, eb, col, t;
337dd7cddfSDavid du Colombier 	int r, g, b, r1, g1, b1;
347dd7cddfSDavid du Colombier 	int *ered, *egrn, *eblu, *rp, *gp, *bp;
35*c35931e2SDavid du Colombier 	int bpc;
367dd7cddfSDavid du Colombier 	uint *map3;
377dd7cddfSDavid du Colombier 	uchar *closest;
387dd7cddfSDavid du Colombier 	Rawimage *im;
397dd7cddfSDavid du Colombier 	int dx, dy;
409a747e4fSDavid du Colombier 	char err[ERRMAX];
417dd7cddfSDavid du Colombier 	uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic;
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier 	err[0] = '\0';
449a747e4fSDavid du Colombier 	errstr(err, sizeof err);	/* throw it away */
457dd7cddfSDavid du Colombier 	im = malloc(sizeof(Rawimage));
467dd7cddfSDavid du Colombier 	if(im == nil)
477dd7cddfSDavid du Colombier 		return nil;
487dd7cddfSDavid du Colombier 	memset(im, 0, sizeof(Rawimage));
497dd7cddfSDavid du Colombier 	im->chans[0] = malloc(i->chanlen);
507dd7cddfSDavid du Colombier 	if(im->chans[0] == nil){
517dd7cddfSDavid du Colombier 		free(im);
527dd7cddfSDavid du Colombier 		return nil;
537dd7cddfSDavid du Colombier 	}
547dd7cddfSDavid du Colombier 	im->r = i->r;
557dd7cddfSDavid du Colombier 	im->nchans = 1;
567dd7cddfSDavid du Colombier 	im->chandesc = CRGBV;
577dd7cddfSDavid du Colombier 	im->chanlen = i->chanlen;
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier 	dx = i->r.max.x-i->r.min.x;
607dd7cddfSDavid du Colombier 	dy = i->r.max.y-i->r.min.y;
617dd7cddfSDavid du Colombier 	cmap = i->cmap;
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	if(inited == 0){
647dd7cddfSDavid du Colombier 		inited = 1;
657dd7cddfSDavid du Colombier 		for(j=0; j<CLAMPOFF; j++)
667dd7cddfSDavid du Colombier 			clamp[j] = 0;
677dd7cddfSDavid du Colombier 		for(j=0; j<256; j++)
687dd7cddfSDavid du Colombier 			clamp[CLAMPOFF+j] = (j>>4);
697dd7cddfSDavid du Colombier 		for(j=0; j<CLAMPOFF; j++)
707dd7cddfSDavid du Colombier 			clamp[CLAMPOFF+256+j] = (255>>4);
717dd7cddfSDavid du Colombier 	}
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier 	in = i->chans[0];
747dd7cddfSDavid du Colombier 	inp = in;
757dd7cddfSDavid du Colombier 	out = im->chans[0];
767dd7cddfSDavid du Colombier 	outp = out;
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier 	ered = malloc((dx+1)*sizeof(int));
797dd7cddfSDavid du Colombier 	egrn = malloc((dx+1)*sizeof(int));
807dd7cddfSDavid du Colombier 	eblu = malloc((dx+1)*sizeof(int));
817dd7cddfSDavid du Colombier 	if(ered==nil || egrn==nil || eblu==nil){
827dd7cddfSDavid du Colombier 		free(im->chans[0]);
837dd7cddfSDavid du Colombier 		free(im);
847dd7cddfSDavid du Colombier 		free(ered);
857dd7cddfSDavid du Colombier 		free(egrn);
867dd7cddfSDavid du Colombier 		free(eblu);
877dd7cddfSDavid du Colombier 		return _remaperror("remap: malloc failed: %r");
887dd7cddfSDavid du Colombier 	}
897dd7cddfSDavid du Colombier 	memset(ered, 0, (dx+1)*sizeof(int));
907dd7cddfSDavid du Colombier 	memset(egrn, 0, (dx+1)*sizeof(int));
917dd7cddfSDavid du Colombier 	memset(eblu, 0, (dx+1)*sizeof(int));
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	switch(i->chandesc){
947dd7cddfSDavid du Colombier 	default:
957dd7cddfSDavid du Colombier 		return _remaperror("remap: can't recognize channel type %d", i->chandesc);
967dd7cddfSDavid du Colombier 	case CRGB1:
977dd7cddfSDavid du Colombier 		if(cmap == nil)
987dd7cddfSDavid du Colombier 			return _remaperror("remap: image has no color map");
997dd7cddfSDavid du Colombier 		if(i->nchans != 1)
1007dd7cddfSDavid du Colombier 			return _remaperror("remap: can't handle nchans %d", i->nchans);
1017dd7cddfSDavid du Colombier 		for(j=1; j<=8; j++)
1027dd7cddfSDavid du Colombier 			if(i->cmaplen == 3*(1<<j))
1037dd7cddfSDavid du Colombier 				break;
1047dd7cddfSDavid du Colombier 		if(j > 8)
1057dd7cddfSDavid du Colombier 			return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
1067dd7cddfSDavid du Colombier 		if(i->cmaplen != 3*256){
1077dd7cddfSDavid du Colombier 			/* to avoid a range check in inner loop below, make a full-size cmap */
1087dd7cddfSDavid du Colombier 			memmove(cmap1, cmap, i->cmaplen);
1097dd7cddfSDavid du Colombier 			cmap = cmap1;
1107dd7cddfSDavid du Colombier 		}
1117dd7cddfSDavid du Colombier 		if(errdiff == 0){
1127dd7cddfSDavid du Colombier 			k = 0;
1137dd7cddfSDavid du Colombier 			for(j=0; j<256; j++){
1147dd7cddfSDavid du Colombier 				r = cmap[k]>>4;
1157dd7cddfSDavid du Colombier 				g = cmap[k+1]>>4;
1167dd7cddfSDavid du Colombier 				b = cmap[k+2]>>4;
1177dd7cddfSDavid du Colombier 				k += 3;
1187dd7cddfSDavid du Colombier 				map[j] = closestrgb[b+16*(g+16*r)];
1197dd7cddfSDavid du Colombier 			}
1207dd7cddfSDavid du Colombier 			for(j=0; j<i->chanlen; j++)
1217dd7cddfSDavid du Colombier 				out[j] = map[in[j]];
1227dd7cddfSDavid du Colombier 		}else{
1237dd7cddfSDavid du Colombier 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
1247dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++){
1257dd7cddfSDavid du Colombier 				er = 0;
1267dd7cddfSDavid du Colombier 				eg = 0;
1277dd7cddfSDavid du Colombier 				eb = 0;
1287dd7cddfSDavid du Colombier 				rp = ered;
1297dd7cddfSDavid du Colombier 				gp = egrn;
1307dd7cddfSDavid du Colombier 				bp = eblu;
1317dd7cddfSDavid du Colombier 				for(x=0; x<dx; x++){
1327dd7cddfSDavid du Colombier 					cm = &cmap[3 * *inp++];
1337dd7cddfSDavid du Colombier 					r = cm[0] +*rp;
1347dd7cddfSDavid du Colombier 					g = cm[1] +*gp;
1357dd7cddfSDavid du Colombier 					b = cm[2] +*bp;
136e288d156SDavid du Colombier 
137e288d156SDavid du Colombier 					/* sanity checks are new */
138e288d156SDavid du Colombier 					if(r >= 256+CLAMPOFF)
139e288d156SDavid du Colombier 						r = 0;
140e288d156SDavid du Colombier 					if(g >= 256+CLAMPOFF)
141e288d156SDavid du Colombier 						g = 0;
142e288d156SDavid du Colombier 					if(b >= 256+CLAMPOFF)
143e288d156SDavid du Colombier 						b = 0;
1447dd7cddfSDavid du Colombier 					r1 = clamp[r+CLAMPOFF];
1457dd7cddfSDavid du Colombier 					g1 = clamp[g+CLAMPOFF];
1467dd7cddfSDavid du Colombier 					b1 = clamp[b+CLAMPOFF];
147e288d156SDavid du Colombier 					if(r1 >= 16 || g1 >= 16 || b1 >= 16)
148e288d156SDavid du Colombier 						col = 0;
149e288d156SDavid du Colombier 					else
1507dd7cddfSDavid du Colombier 						col = closestrgb[b1+16*(g1+16*r1)];
1517dd7cddfSDavid du Colombier 					*outp++ = col;
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 					rgb = rgbmap[col];
1547dd7cddfSDavid du Colombier 					r -= (rgb>>16) & 0xFF;
1557dd7cddfSDavid du Colombier 					t = (3*r)>>4;
1567dd7cddfSDavid du Colombier 					*rp++ = t+er;
1577dd7cddfSDavid du Colombier 					*rp += t;
1587dd7cddfSDavid du Colombier 					er = r-3*t;
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier 					g -= (rgb>>8) & 0xFF;
1617dd7cddfSDavid du Colombier 					t = (3*g)>>4;
1627dd7cddfSDavid du Colombier 					*gp++ = t+eg;
1637dd7cddfSDavid du Colombier 					*gp += t;
1647dd7cddfSDavid du Colombier 					eg = g-3*t;
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 					b -= rgb & 0xFF;
1677dd7cddfSDavid du Colombier 					t = (3*b)>>4;
1687dd7cddfSDavid du Colombier 					*bp++ = t+eb;
1697dd7cddfSDavid du Colombier 					*bp += t;
1707dd7cddfSDavid du Colombier 					eb = b-3*t;
1717dd7cddfSDavid du Colombier 				}
1727dd7cddfSDavid du Colombier 			}
1737dd7cddfSDavid du Colombier 		}
1747dd7cddfSDavid du Colombier 		break;
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier 	case CYCbCr:
177*c35931e2SDavid du Colombier 		bpc = 1;
178*c35931e2SDavid du Colombier 		rpic = i->chans[0];
179*c35931e2SDavid du Colombier 		gpic = i->chans[1];
180*c35931e2SDavid du Colombier 		bpic = i->chans[2];
1817dd7cddfSDavid du Colombier 		closest = closestycbcr;
1827dd7cddfSDavid du Colombier 		map3 = ycbcrmap;
183*c35931e2SDavid du Colombier 		if(i->nchans != 3)
184*c35931e2SDavid du Colombier 			return _remaperror("remap: RGB image has %d channels", i->nchans);
1857dd7cddfSDavid du Colombier 		goto Threecolor;
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	case CRGB:
188*c35931e2SDavid du Colombier 		bpc = 1;
189*c35931e2SDavid du Colombier 		rpic = i->chans[0];
190*c35931e2SDavid du Colombier 		gpic = i->chans[1];
191*c35931e2SDavid du Colombier 		bpic = i->chans[2];
192*c35931e2SDavid du Colombier 		if(i->nchans != 3)
193*c35931e2SDavid du Colombier 			return _remaperror("remap: RGB image has %d channels", i->nchans);
194*c35931e2SDavid du Colombier 		goto rgbgen;
195*c35931e2SDavid du Colombier 
196*c35931e2SDavid du Colombier 	case CRGB24:
197*c35931e2SDavid du Colombier 		bpc = 3;
198*c35931e2SDavid du Colombier 		bpic = i->chans[0];
199*c35931e2SDavid du Colombier 		gpic = i->chans[0] + 1;
200*c35931e2SDavid du Colombier 		rpic = i->chans[0] + 2;
201*c35931e2SDavid du Colombier 		goto rgbgen;
202*c35931e2SDavid du Colombier 
203*c35931e2SDavid du Colombier 	case CRGBA32:
204*c35931e2SDavid du Colombier 		bpc = 4;
205*c35931e2SDavid du Colombier 		/* i->chans[0]+0 is alpha */
206*c35931e2SDavid du Colombier 		bpic = i->chans[0] + 1;
207*c35931e2SDavid du Colombier 		gpic = i->chans[0] + 2;
208*c35931e2SDavid du Colombier 		rpic = i->chans[0] + 3;
209*c35931e2SDavid du Colombier 
210*c35931e2SDavid du Colombier 	rgbgen:
2117dd7cddfSDavid du Colombier 		closest = closestrgb;
2127dd7cddfSDavid du Colombier 		map3 = rgbmap;
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier 	Threecolor:
215*c35931e2SDavid du Colombier 
2167dd7cddfSDavid du Colombier 		if(errdiff == 0){
217*c35931e2SDavid du Colombier 			outp = out;
218*c35931e2SDavid du Colombier 			for(j=0; j<i->chanlen; j+=bpc){
2197dd7cddfSDavid du Colombier 				r = rpic[j]>>4;
2207dd7cddfSDavid du Colombier 				g = gpic[j]>>4;
2217dd7cddfSDavid du Colombier 				b = bpic[j]>>4;
222*c35931e2SDavid du Colombier 				*outp++ = closest[b+16*(g+16*r)];
2237dd7cddfSDavid du Colombier 			}
2247dd7cddfSDavid du Colombier 		}else{
2257dd7cddfSDavid du Colombier 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
2267dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++){
2277dd7cddfSDavid du Colombier 				er = 0;
2287dd7cddfSDavid du Colombier 				eg = 0;
2297dd7cddfSDavid du Colombier 				eb = 0;
2307dd7cddfSDavid du Colombier 				rp = ered;
2317dd7cddfSDavid du Colombier 				gp = egrn;
2327dd7cddfSDavid du Colombier 				bp = eblu;
2337dd7cddfSDavid du Colombier 				for(x=0; x<dx; x++){
234*c35931e2SDavid du Colombier 					r = *rpic + *rp;
235*c35931e2SDavid du Colombier 					g = *gpic + *gp;
236*c35931e2SDavid du Colombier 					b = *bpic + *bp;
237*c35931e2SDavid du Colombier 					rpic += bpc;
238*c35931e2SDavid du Colombier 					gpic += bpc;
239*c35931e2SDavid du Colombier 					bpic += bpc;
2407dd7cddfSDavid du Colombier 					/*
2417dd7cddfSDavid du Colombier 					 * Errors can be uncorrectable if converting from YCbCr,
2427dd7cddfSDavid du Colombier 					 * since we can't guarantee that an extremal value of one of
2437dd7cddfSDavid du Colombier 					 * the components selects a color with an extremal value.
2447dd7cddfSDavid du Colombier 					 * If we don't, the errors accumulate without bound.  This
2457dd7cddfSDavid du Colombier 					 * doesn't happen in RGB because the closest table can guarantee
2467dd7cddfSDavid du Colombier 					 * a color on the edge of the gamut, producing a zero error in
2477dd7cddfSDavid du Colombier 					 * that component.  For the rotation YCbCr space, there may be
2487dd7cddfSDavid du Colombier 					 * no color that can guarantee zero error at the edge.
2497dd7cddfSDavid du Colombier 					 * Therefore we must clamp explicitly rather than by assuming
2507dd7cddfSDavid du Colombier 					 * an upper error bound of CLAMPOFF.  The performance difference
2517dd7cddfSDavid du Colombier 					 * is miniscule anyway.
2527dd7cddfSDavid du Colombier 					 */
2537dd7cddfSDavid du Colombier 					if(r < 0)
2547dd7cddfSDavid du Colombier 						r = 0;
2557dd7cddfSDavid du Colombier 					else if(r > 255)
2567dd7cddfSDavid du Colombier 						r = 255;
2577dd7cddfSDavid du Colombier 					if(g < 0)
2587dd7cddfSDavid du Colombier 						g = 0;
2597dd7cddfSDavid du Colombier 					else if(g > 255)
2607dd7cddfSDavid du Colombier 						g = 255;
2617dd7cddfSDavid du Colombier 					if(b < 0)
2627dd7cddfSDavid du Colombier 						b = 0;
2637dd7cddfSDavid du Colombier 					else if(b > 255)
2647dd7cddfSDavid du Colombier 						b = 255;
2657dd7cddfSDavid du Colombier 					r1 = r>>4;
2667dd7cddfSDavid du Colombier 					g1 = g>>4;
2677dd7cddfSDavid du Colombier 					b1 = b>>4;
2687dd7cddfSDavid du Colombier 					col = closest[b1+16*(g1+16*r1)];
2697dd7cddfSDavid du Colombier 					*outp++ = col;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 					rgb = map3[col];
2727dd7cddfSDavid du Colombier 					r -= (rgb>>16) & 0xFF;
2737dd7cddfSDavid du Colombier 					t = (3*r)>>4;
2747dd7cddfSDavid du Colombier 					*rp++ = t+er;
2757dd7cddfSDavid du Colombier 					*rp += t;
2767dd7cddfSDavid du Colombier 					er = r-3*t;
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 					g -= (rgb>>8) & 0xFF;
2797dd7cddfSDavid du Colombier 					t = (3*g)>>4;
2807dd7cddfSDavid du Colombier 					*gp++ = t+eg;
2817dd7cddfSDavid du Colombier 					*gp += t;
2827dd7cddfSDavid du Colombier 					eg = g-3*t;
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier 					b -= rgb & 0xFF;
2857dd7cddfSDavid du Colombier 					t = (3*b)>>4;
2867dd7cddfSDavid du Colombier 					*bp++ = t+eb;
2877dd7cddfSDavid du Colombier 					*bp += t;
2887dd7cddfSDavid du Colombier 					eb = b-3*t;
2897dd7cddfSDavid du Colombier 				}
2907dd7cddfSDavid du Colombier 			}
2917dd7cddfSDavid du Colombier 		}
2927dd7cddfSDavid du Colombier 		break;
2937dd7cddfSDavid du Colombier 
294*c35931e2SDavid du Colombier 	case CYA16:
295*c35931e2SDavid du Colombier 		bpc = 2;
296*c35931e2SDavid du Colombier 		/* i->chans[0] + 0 is alpha */
297*c35931e2SDavid du Colombier 		rpic = i->chans[0] + 1;
298*c35931e2SDavid du Colombier 		goto greygen;
299*c35931e2SDavid du Colombier 
3007dd7cddfSDavid du Colombier 	case CY:
301*c35931e2SDavid du Colombier 		bpc = 1;
302*c35931e2SDavid du Colombier 		rpic = i->chans[0];
3037dd7cddfSDavid du Colombier 		if(i->nchans != 1)
3047dd7cddfSDavid du Colombier 			return _remaperror("remap: Y image has %d chans", i->nchans);
305*c35931e2SDavid du Colombier 
306*c35931e2SDavid du Colombier 	greygen:
3077dd7cddfSDavid du Colombier 		if(errdiff == 0){
308*c35931e2SDavid du Colombier 			for(j=0; j<i->chanlen; j+=bpc){
3097dd7cddfSDavid du Colombier 				r = rpic[j]>>4;
3107dd7cddfSDavid du Colombier 				*outp++ = closestrgb[r+16*(r+16*r)];
3117dd7cddfSDavid du Colombier 			}
3127dd7cddfSDavid du Colombier 		}else{
3137dd7cddfSDavid du Colombier 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
3147dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++){
3157dd7cddfSDavid du Colombier 				er = 0;
3167dd7cddfSDavid du Colombier 				rp = ered;
3177dd7cddfSDavid du Colombier 				for(x=0; x<dx; x++){
318*c35931e2SDavid du Colombier 					r = *rpic + *rp;
319*c35931e2SDavid du Colombier 					rpic += bpc;
3207dd7cddfSDavid du Colombier 					r1 = clamp[r+CLAMPOFF];
3217dd7cddfSDavid du Colombier 					col = closestrgb[r1+16*(r1+16*r1)];
3227dd7cddfSDavid du Colombier 					*outp++ = col;
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 					rgb = rgbmap[col];
3257dd7cddfSDavid du Colombier 					r -= (rgb>>16) & 0xFF;
3267dd7cddfSDavid du Colombier 					t = (3*r)>>4;
3277dd7cddfSDavid du Colombier 					*rp++ = t+er;
3287dd7cddfSDavid du Colombier 					*rp += t;
3297dd7cddfSDavid du Colombier 					er = r-3*t;
3307dd7cddfSDavid du Colombier 				}
3317dd7cddfSDavid du Colombier 			}
3327dd7cddfSDavid du Colombier 		}
3337dd7cddfSDavid du Colombier 		break;
3347dd7cddfSDavid du Colombier 	}
3357dd7cddfSDavid du Colombier 	free(ered);
3367dd7cddfSDavid du Colombier 	free(egrn);
3377dd7cddfSDavid du Colombier 	free(eblu);
3387dd7cddfSDavid du Colombier 	return im;
3397dd7cddfSDavid du Colombier }
340