plan9port

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

torgbv.c (6443B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <draw.h>
      5 #include "imagefile.h"
      6 
      7 #include "rgbv.h"
      8 #include "ycbcr.h"
      9 
     10 #define	CLAMPOFF 128
     11 
     12 static	int	clamp[CLAMPOFF+256+CLAMPOFF];
     13 static	int	inited;
     14 
     15 void*
     16 _remaperror(char *fmt, ...)
     17 {
     18 	va_list arg;
     19 	char buf[256];
     20 
     21 	va_start(arg, fmt);
     22 	vseprint(buf, buf+sizeof buf, fmt, arg);
     23 	va_end(arg);
     24 
     25 	werrstr(buf);
     26 	return nil;
     27 }
     28 
     29 Rawimage*
     30 torgbv(Rawimage *i, int errdiff)
     31 {
     32 	int j, k, rgb, x, y, er, eg, eb, col, t;
     33 	int r, g, b, r1, g1, b1;
     34 	int *ered, *egrn, *eblu, *rp, *gp, *bp;
     35 	uint *map3;
     36 	uchar *closest;
     37 	Rawimage *im;
     38 	int dx, dy;
     39 	char err[ERRMAX];
     40 	uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic;
     41 
     42 	err[0] = '\0';
     43 	errstr(err, sizeof err);	/* throw it away */
     44 	im = malloc(sizeof(Rawimage));
     45 	if(im == nil)
     46 		return nil;
     47 	memset(im, 0, sizeof(Rawimage));
     48 	im->chans[0] = malloc(i->chanlen);
     49 	if(im->chans[0] == nil){
     50 		free(im);
     51 		return nil;
     52 	}
     53 	im->r = i->r;
     54 	im->nchans = 1;
     55 	im->chandesc = CRGBV;
     56 	im->chanlen = i->chanlen;
     57 
     58 	dx = i->r.max.x-i->r.min.x;
     59 	dy = i->r.max.y-i->r.min.y;
     60 	cmap = i->cmap;
     61 
     62 	if(inited == 0){
     63 		inited = 1;
     64 		for(j=0; j<CLAMPOFF; j++)
     65 			clamp[j] = 0;
     66 		for(j=0; j<256; j++)
     67 			clamp[CLAMPOFF+j] = (j>>4);
     68 		for(j=0; j<CLAMPOFF; j++)
     69 			clamp[CLAMPOFF+256+j] = (255>>4);
     70 	}
     71 
     72 	in = i->chans[0];
     73 	inp = in;
     74 	out = im->chans[0];
     75 	outp = out;
     76 
     77 	ered = malloc((dx+1)*sizeof(int));
     78 	egrn = malloc((dx+1)*sizeof(int));
     79 	eblu = malloc((dx+1)*sizeof(int));
     80 	if(ered==nil || egrn==nil || eblu==nil){
     81 		free(im->chans[0]);
     82 		free(im);
     83 		free(ered);
     84 		free(egrn);
     85 		free(eblu);
     86 		return _remaperror("remap: malloc failed: %r");
     87 	}
     88 	memset(ered, 0, (dx+1)*sizeof(int));
     89 	memset(egrn, 0, (dx+1)*sizeof(int));
     90 	memset(eblu, 0, (dx+1)*sizeof(int));
     91 
     92 	switch(i->chandesc){
     93 	default:
     94 		return _remaperror("remap: can't recognize channel type %d", i->chandesc);
     95 	case CRGB1:
     96 		if(cmap == nil)
     97 			return _remaperror("remap: image has no color map");
     98 		if(i->nchans != 1)
     99 			return _remaperror("remap: can't handle nchans %d", i->nchans);
    100 		for(j=1; j<=8; j++)
    101 			if(i->cmaplen == 3*(1<<j))
    102 				break;
    103 		if(j > 8)
    104 			return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
    105 		if(i->cmaplen != 3*256){
    106 			/* to avoid a range check in inner loop below, make a full-size cmap */
    107 			memmove(cmap1, cmap, i->cmaplen);
    108 			cmap = cmap1;
    109 		}
    110 		if(errdiff == 0){
    111 			k = 0;
    112 			for(j=0; j<256; j++){
    113 				r = cmap[k]>>4;
    114 				g = cmap[k+1]>>4;
    115 				b = cmap[k+2]>>4;
    116 				k += 3;
    117 				map[j] = closestrgb[b+16*(g+16*r)];
    118 			}
    119 			for(j=0; j<i->chanlen; j++)
    120 				out[j] = map[in[j]];
    121 		}else{
    122 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
    123 			for(y=0; y<dy; y++){
    124 				er = 0;
    125 				eg = 0;
    126 				eb = 0;
    127 				rp = ered;
    128 				gp = egrn;
    129 				bp = eblu;
    130 				for(x=0; x<dx; x++){
    131 					cm = &cmap[3 * *inp++];
    132 					r = cm[0] +*rp;
    133 					g = cm[1] +*gp;
    134 					b = cm[2] +*bp;
    135 
    136 					/* sanity checks are new */
    137 					if(r >= 256+CLAMPOFF)
    138 						r = 0;
    139 					if(g >= 256+CLAMPOFF)
    140 						g = 0;
    141 					if(b >= 256+CLAMPOFF)
    142 						b = 0;
    143 					r1 = clamp[r+CLAMPOFF];
    144 					g1 = clamp[g+CLAMPOFF];
    145 					b1 = clamp[b+CLAMPOFF];
    146 					if(r1 >= 16 || g1 >= 16 || b1 >= 16)
    147 						col = 0;
    148 					else
    149 						col = closestrgb[b1+16*(g1+16*r1)];
    150 					*outp++ = col;
    151 
    152 					rgb = rgbmap[col];
    153 					r -= (rgb>>16) & 0xFF;
    154 					t = (3*r)>>4;
    155 					*rp++ = t+er;
    156 					*rp += t;
    157 					er = r-3*t;
    158 
    159 					g -= (rgb>>8) & 0xFF;
    160 					t = (3*g)>>4;
    161 					*gp++ = t+eg;
    162 					*gp += t;
    163 					eg = g-3*t;
    164 
    165 					b -= rgb & 0xFF;
    166 					t = (3*b)>>4;
    167 					*bp++ = t+eb;
    168 					*bp += t;
    169 					eb = b-3*t;
    170 				}
    171 			}
    172 		}
    173 		break;
    174 
    175 	case CYCbCr:
    176 		closest = closestycbcr;
    177 		map3 = ycbcrmap;
    178 		goto Threecolor;
    179 
    180 	case CRGB:
    181 		closest = closestrgb;
    182 		map3 = rgbmap;
    183 
    184 	Threecolor:
    185 		if(i->nchans != 3)
    186 			return _remaperror("remap: RGB image has %d channels", i->nchans);
    187 		rpic = i->chans[0];
    188 		gpic = i->chans[1];
    189 		bpic = i->chans[2];
    190 		if(errdiff == 0){
    191 			for(j=0; j<i->chanlen; j++){
    192 				r = rpic[j]>>4;
    193 				g = gpic[j]>>4;
    194 				b = bpic[j]>>4;
    195 				out[j] = closest[b+16*(g+16*r)];
    196 			}
    197 		}else{
    198 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
    199 			for(y=0; y<dy; y++){
    200 				er = 0;
    201 				eg = 0;
    202 				eb = 0;
    203 				rp = ered;
    204 				gp = egrn;
    205 				bp = eblu;
    206 				for(x=0; x<dx; x++){
    207 					r = *rpic++ + *rp;
    208 					g = *gpic++ + *gp;
    209 					b = *bpic++ + *bp;
    210 					/*
    211 					 * Errors can be uncorrectable if converting from YCbCr,
    212 					 * since we can't guarantee that an extremal value of one of
    213 					 * the components selects a color with an extremal value.
    214 					 * If we don't, the errors accumulate without bound.  This
    215 					 * doesn't happen in RGB because the closest table can guarantee
    216 					 * a color on the edge of the gamut, producing a zero error in
    217 					 * that component.  For the rotation YCbCr space, there may be
    218 					 * no color that can guarantee zero error at the edge.
    219 					 * Therefore we must clamp explicitly rather than by assuming
    220 					 * an upper error bound of CLAMPOFF.  The performance difference
    221 					 * is miniscule anyway.
    222 					 */
    223 					if(r < 0)
    224 						r = 0;
    225 					else if(r > 255)
    226 						r = 255;
    227 					if(g < 0)
    228 						g = 0;
    229 					else if(g > 255)
    230 						g = 255;
    231 					if(b < 0)
    232 						b = 0;
    233 					else if(b > 255)
    234 						b = 255;
    235 					r1 = r>>4;
    236 					g1 = g>>4;
    237 					b1 = b>>4;
    238 					col = closest[b1+16*(g1+16*r1)];
    239 					*outp++ = col;
    240 
    241 					rgb = map3[col];
    242 					r -= (rgb>>16) & 0xFF;
    243 					t = (3*r)>>4;
    244 					*rp++ = t+er;
    245 					*rp += t;
    246 					er = r-3*t;
    247 
    248 					g -= (rgb>>8) & 0xFF;
    249 					t = (3*g)>>4;
    250 					*gp++ = t+eg;
    251 					*gp += t;
    252 					eg = g-3*t;
    253 
    254 					b -= rgb & 0xFF;
    255 					t = (3*b)>>4;
    256 					*bp++ = t+eb;
    257 					*bp += t;
    258 					eb = b-3*t;
    259 				}
    260 			}
    261 		}
    262 		break;
    263 
    264 	case CY:
    265 		if(i->nchans != 1)
    266 			return _remaperror("remap: Y image has %d chans", i->nchans);
    267 		rpic = i->chans[0];
    268 		if(errdiff == 0){
    269 			for(j=0; j<i->chanlen; j++){
    270 				r = rpic[j]>>4;
    271 				*outp++ = closestrgb[r+16*(r+16*r)];
    272 			}
    273 		}else{
    274 			/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
    275 			for(y=0; y<dy; y++){
    276 				er = 0;
    277 				rp = ered;
    278 				for(x=0; x<dx; x++){
    279 					r = *inp++ + *rp;
    280 					r1 = clamp[r+CLAMPOFF];
    281 					col = closestrgb[r1+16*(r1+16*r1)];
    282 					*outp++ = col;
    283 
    284 					rgb = rgbmap[col];
    285 					r -= (rgb>>16) & 0xFF;
    286 					t = (3*r)>>4;
    287 					*rp++ = t+er;
    288 					*rp += t;
    289 					er = r-3*t;
    290 				}
    291 			}
    292 		}
    293 		break;
    294 	}
    295 	free(ered);
    296 	free(egrn);
    297 	free(eblu);
    298 	return im;
    299 }