|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <draw.h> | 
|  | #include "imagefile.h" | 
|  |  | 
|  | #include "rgbv.h" | 
|  | #include "ycbcr.h" | 
|  |  | 
|  | #define	CLAMPOFF 128 | 
|  |  | 
|  | static	int	clamp[CLAMPOFF+256+CLAMPOFF]; | 
|  | static	int	inited; | 
|  |  | 
|  | void* | 
|  | _remaperror(char *fmt, ...) | 
|  | { | 
|  | va_list arg; | 
|  | char buf[256]; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | vseprint(buf, buf+sizeof buf, fmt, arg); | 
|  | va_end(arg); | 
|  |  | 
|  | werrstr(buf); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | Rawimage* | 
|  | torgbv(Rawimage *i, int errdiff) | 
|  | { | 
|  | int j, k, rgb, x, y, er, eg, eb, col, t; | 
|  | int r, g, b, r1, g1, b1; | 
|  | int *ered, *egrn, *eblu, *rp, *gp, *bp; | 
|  | uint *map3; | 
|  | uchar *closest; | 
|  | Rawimage *im; | 
|  | int dx, dy; | 
|  | char err[ERRMAX]; | 
|  | uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic; | 
|  |  | 
|  | err[0] = '\0'; | 
|  | errstr(err, sizeof err);	/* throw it away */ | 
|  | im = malloc(sizeof(Rawimage)); | 
|  | if(im == nil) | 
|  | return nil; | 
|  | memset(im, 0, sizeof(Rawimage)); | 
|  | im->chans[0] = malloc(i->chanlen); | 
|  | if(im->chans[0] == nil){ | 
|  | free(im); | 
|  | return nil; | 
|  | } | 
|  | im->r = i->r; | 
|  | im->nchans = 1; | 
|  | im->chandesc = CRGBV; | 
|  | im->chanlen = i->chanlen; | 
|  |  | 
|  | dx = i->r.max.x-i->r.min.x; | 
|  | dy = i->r.max.y-i->r.min.y; | 
|  | cmap = i->cmap; | 
|  |  | 
|  | if(inited == 0){ | 
|  | inited = 1; | 
|  | for(j=0; j<CLAMPOFF; j++) | 
|  | clamp[j] = 0; | 
|  | for(j=0; j<256; j++) | 
|  | clamp[CLAMPOFF+j] = (j>>4); | 
|  | for(j=0; j<CLAMPOFF; j++) | 
|  | clamp[CLAMPOFF+256+j] = (255>>4); | 
|  | } | 
|  |  | 
|  | in = i->chans[0]; | 
|  | inp = in; | 
|  | out = im->chans[0]; | 
|  | outp = out; | 
|  |  | 
|  | ered = malloc((dx+1)*sizeof(int)); | 
|  | egrn = malloc((dx+1)*sizeof(int)); | 
|  | eblu = malloc((dx+1)*sizeof(int)); | 
|  | if(ered==nil || egrn==nil || eblu==nil){ | 
|  | free(im->chans[0]); | 
|  | free(im); | 
|  | free(ered); | 
|  | free(egrn); | 
|  | free(eblu); | 
|  | return _remaperror("remap: malloc failed: %r"); | 
|  | } | 
|  | memset(ered, 0, (dx+1)*sizeof(int)); | 
|  | memset(egrn, 0, (dx+1)*sizeof(int)); | 
|  | memset(eblu, 0, (dx+1)*sizeof(int)); | 
|  |  | 
|  | switch(i->chandesc){ | 
|  | default: | 
|  | return _remaperror("remap: can't recognize channel type %d", i->chandesc); | 
|  | case CRGB1: | 
|  | if(cmap == nil) | 
|  | return _remaperror("remap: image has no color map"); | 
|  | if(i->nchans != 1) | 
|  | return _remaperror("remap: can't handle nchans %d", i->nchans); | 
|  | for(j=1; j<=8; j++) | 
|  | if(i->cmaplen == 3*(1<<j)) | 
|  | break; | 
|  | if(j > 8) | 
|  | return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3); | 
|  | if(i->cmaplen != 3*256){ | 
|  | /* to avoid a range check in inner loop below, make a full-size cmap */ | 
|  | memmove(cmap1, cmap, i->cmaplen); | 
|  | cmap = cmap1; | 
|  | } | 
|  | if(errdiff == 0){ | 
|  | k = 0; | 
|  | for(j=0; j<256; j++){ | 
|  | r = cmap[k]>>4; | 
|  | g = cmap[k+1]>>4; | 
|  | b = cmap[k+2]>>4; | 
|  | k += 3; | 
|  | map[j] = closestrgb[b+16*(g+16*r)]; | 
|  | } | 
|  | for(j=0; j<i->chanlen; j++) | 
|  | out[j] = map[in[j]]; | 
|  | }else{ | 
|  | /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ | 
|  | for(y=0; y<dy; y++){ | 
|  | er = 0; | 
|  | eg = 0; | 
|  | eb = 0; | 
|  | rp = ered; | 
|  | gp = egrn; | 
|  | bp = eblu; | 
|  | for(x=0; x<dx; x++){ | 
|  | cm = &cmap[3 * *inp++]; | 
|  | r = cm[0] +*rp; | 
|  | g = cm[1] +*gp; | 
|  | b = cm[2] +*bp; | 
|  |  | 
|  | /* sanity checks are new */ | 
|  | if(r >= 256+CLAMPOFF) | 
|  | r = 0; | 
|  | if(g >= 256+CLAMPOFF) | 
|  | g = 0; | 
|  | if(b >= 256+CLAMPOFF) | 
|  | b = 0; | 
|  | r1 = clamp[r+CLAMPOFF]; | 
|  | g1 = clamp[g+CLAMPOFF]; | 
|  | b1 = clamp[b+CLAMPOFF]; | 
|  | if(r1 >= 16 || g1 >= 16 || b1 >= 16) | 
|  | col = 0; | 
|  | else | 
|  | col = closestrgb[b1+16*(g1+16*r1)]; | 
|  | *outp++ = col; | 
|  |  | 
|  | rgb = rgbmap[col]; | 
|  | r -= (rgb>>16) & 0xFF; | 
|  | t = (3*r)>>4; | 
|  | *rp++ = t+er; | 
|  | *rp += t; | 
|  | er = r-3*t; | 
|  |  | 
|  | g -= (rgb>>8) & 0xFF; | 
|  | t = (3*g)>>4; | 
|  | *gp++ = t+eg; | 
|  | *gp += t; | 
|  | eg = g-3*t; | 
|  |  | 
|  | b -= rgb & 0xFF; | 
|  | t = (3*b)>>4; | 
|  | *bp++ = t+eb; | 
|  | *bp += t; | 
|  | eb = b-3*t; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case CYCbCr: | 
|  | closest = closestycbcr; | 
|  | map3 = ycbcrmap; | 
|  | goto Threecolor; | 
|  |  | 
|  | case CRGB: | 
|  | closest = closestrgb; | 
|  | map3 = rgbmap; | 
|  |  | 
|  | Threecolor: | 
|  | if(i->nchans != 3) | 
|  | return _remaperror("remap: RGB image has %d channels", i->nchans); | 
|  | rpic = i->chans[0]; | 
|  | gpic = i->chans[1]; | 
|  | bpic = i->chans[2]; | 
|  | if(errdiff == 0){ | 
|  | for(j=0; j<i->chanlen; j++){ | 
|  | r = rpic[j]>>4; | 
|  | g = gpic[j]>>4; | 
|  | b = bpic[j]>>4; | 
|  | out[j] = closest[b+16*(g+16*r)]; | 
|  | } | 
|  | }else{ | 
|  | /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ | 
|  | for(y=0; y<dy; y++){ | 
|  | er = 0; | 
|  | eg = 0; | 
|  | eb = 0; | 
|  | rp = ered; | 
|  | gp = egrn; | 
|  | bp = eblu; | 
|  | for(x=0; x<dx; x++){ | 
|  | r = *rpic++ + *rp; | 
|  | g = *gpic++ + *gp; | 
|  | b = *bpic++ + *bp; | 
|  | /* | 
|  | * Errors can be uncorrectable if converting from YCbCr, | 
|  | * since we can't guarantee that an extremal value of one of | 
|  | * the components selects a color with an extremal value. | 
|  | * If we don't, the errors accumulate without bound.  This | 
|  | * doesn't happen in RGB because the closest table can guarantee | 
|  | * a color on the edge of the gamut, producing a zero error in | 
|  | * that component.  For the rotation YCbCr space, there may be | 
|  | * no color that can guarantee zero error at the edge. | 
|  | * Therefore we must clamp explicitly rather than by assuming | 
|  | * an upper error bound of CLAMPOFF.  The performance difference | 
|  | * is miniscule anyway. | 
|  | */ | 
|  | if(r < 0) | 
|  | r = 0; | 
|  | else if(r > 255) | 
|  | r = 255; | 
|  | if(g < 0) | 
|  | g = 0; | 
|  | else if(g > 255) | 
|  | g = 255; | 
|  | if(b < 0) | 
|  | b = 0; | 
|  | else if(b > 255) | 
|  | b = 255; | 
|  | r1 = r>>4; | 
|  | g1 = g>>4; | 
|  | b1 = b>>4; | 
|  | col = closest[b1+16*(g1+16*r1)]; | 
|  | *outp++ = col; | 
|  |  | 
|  | rgb = map3[col]; | 
|  | r -= (rgb>>16) & 0xFF; | 
|  | t = (3*r)>>4; | 
|  | *rp++ = t+er; | 
|  | *rp += t; | 
|  | er = r-3*t; | 
|  |  | 
|  | g -= (rgb>>8) & 0xFF; | 
|  | t = (3*g)>>4; | 
|  | *gp++ = t+eg; | 
|  | *gp += t; | 
|  | eg = g-3*t; | 
|  |  | 
|  | b -= rgb & 0xFF; | 
|  | t = (3*b)>>4; | 
|  | *bp++ = t+eb; | 
|  | *bp += t; | 
|  | eb = b-3*t; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case CY: | 
|  | if(i->nchans != 1) | 
|  | return _remaperror("remap: Y image has %d chans", i->nchans); | 
|  | rpic = i->chans[0]; | 
|  | if(errdiff == 0){ | 
|  | for(j=0; j<i->chanlen; j++){ | 
|  | r = rpic[j]>>4; | 
|  | *outp++ = closestrgb[r+16*(r+16*r)]; | 
|  | } | 
|  | }else{ | 
|  | /* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */ | 
|  | for(y=0; y<dy; y++){ | 
|  | er = 0; | 
|  | rp = ered; | 
|  | for(x=0; x<dx; x++){ | 
|  | r = *inp++ + *rp; | 
|  | r1 = clamp[r+CLAMPOFF]; | 
|  | col = closestrgb[r1+16*(r1+16*r1)]; | 
|  | *outp++ = col; | 
|  |  | 
|  | rgb = rgbmap[col]; | 
|  | r -= (rgb>>16) & 0xFF; | 
|  | t = (3*r)>>4; | 
|  | *rp++ = t+er; | 
|  | *rp += t; | 
|  | er = r-3*t; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | free(ered); | 
|  | free(egrn); | 
|  | free(eblu); | 
|  | return im; | 
|  | } |