diff options
Diffstat (limited to 'src/cmd/jpg/torgbv.c')
-rw-r--r-- | src/cmd/jpg/torgbv.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/src/cmd/jpg/torgbv.c b/src/cmd/jpg/torgbv.c new file mode 100644 index 00000000..e83f82cd --- /dev/null +++ b/src/cmd/jpg/torgbv.c @@ -0,0 +1,299 @@ +#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; +} |