Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;
        int bpc;
        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:
                bpc = 1;
                rpic = i->chans[0];
                gpic = i->chans[1];
                bpic = i->chans[2];
                closest = closestycbcr;
                map3 = ycbcrmap;
                if(i->nchans != 3)
                        return _remaperror("remap: RGB image has %d channels", i->nchans);
                goto Threecolor;

        case CRGB:
                bpc = 1;
                rpic = i->chans[0];
                gpic = i->chans[1];
                bpic = i->chans[2];
                if(i->nchans != 3)
                        return _remaperror("remap: RGB image has %d channels", i->nchans);
                goto rgbgen;

        case CRGB24:
                bpc = 3;
                bpic = i->chans[0];
                gpic = i->chans[0] + 1;
                rpic = i->chans[0] + 2;
                goto rgbgen;

        case CRGBA32:
                bpc = 4;
                /* i->chans[0]+0 is alpha */
                bpic = i->chans[0] + 1;
                gpic = i->chans[0] + 2;
                rpic = i->chans[0] + 3;

        rgbgen:
                closest = closestrgb;
                map3 = rgbmap;

        Threecolor:

                if(errdiff == 0){
                        outp = out;
                        for(j=0; j<i->chanlen; j+=bpc){
                                r = rpic[j]>>4;
                                g = gpic[j]>>4;
                                b = bpic[j]>>4;
                                *outp++ = 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;
                                        rpic += bpc;
                                        gpic += bpc;
                                        bpic += bpc;
                                        /*
                                         * 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 CYA16:
                bpc = 2;
                /* i->chans[0] + 0 is alpha */
                rpic = i->chans[0] + 1;
                goto greygen;

        case CY:
                bpc = 1;
                rpic = i->chans[0];
                if(i->nchans != 1)
                        return _remaperror("remap: Y image has %d chans", i->nchans);

        greygen:
                if(errdiff == 0){
                        for(j=0; j<i->chanlen; j+=bpc){
                                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 = *rpic + *rp;
                                        rpic += bpc;
                                        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;
}