Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"

/* Convert image to a single channel, one byte per pixel */

static
int
notrans(ulong chan)
{
        switch(chan){
        case GREY1:
        case GREY2:
        case GREY4:
        case    CMAP8:
        case GREY8:
                return 1;
        }
        return 0;
}

static
int
easycase(ulong chan)
{
        switch(chan){
        case RGB16:
        case RGB24:
        case RGBA32:
        case ARGB32:
                return 1;
        }
        return 0;
}

/*
 * Convert to one byte per pixel, RGBV or grey, depending
 */

static
uchar*
load(Image *image, Memimage *memimage)
{
        uchar *data, *p, *q0, *q1, *q2;
        uchar *rgbv;
        int depth, ndata, dx, dy, i, v;
        ulong chan, pixel;
        Rectangle r;
        Rawimage ri, *nri;

        if(memimage == nil){
                r = image->r;
                depth = image->depth;
                chan = image->chan;
        }else{
                r = memimage->r;
                depth = memimage->depth;
                chan = memimage->chan;
        }
        dx = Dx(r);
        dy = Dy(r);

        /* 
         * Read image data into memory
         * potentially one extra byte on each end of each scan line.
         */
        ndata = dy*(2+bytesperline(r, depth));
        data = malloc(ndata);
        if(data == nil)
                return nil;
        if(memimage != nil)
                ndata = unloadmemimage(memimage, r, data, ndata);
        else
                ndata = unloadimage(image, r, data, ndata);
        if(ndata < 0){
                werrstr("onechan: %r");
                free(data);
                return nil;
        }

        /*
         * Repack
         */
        memset(&ri, 0, sizeof(ri));
        ri.r = r;
        ri.cmap = nil;
        ri.cmaplen = 0;
        ri.nchans = 3;
        ri.chanlen = dx*dy;
        ri.chans[0] = malloc(ri.chanlen);
        ri.chans[1] = malloc(ri.chanlen);
        ri.chans[2] = malloc(ri.chanlen);
        if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){
    Err:
                free(ri.chans[0]);
                free(ri.chans[1]);
                free(ri.chans[2]);
                free(data);
                return nil;
        }
        ri.chandesc = CRGB;

        p = data;
        q0 = ri.chans[0];
        q1 = ri.chans[1];
        q2 = ri.chans[2];

        switch(chan){
        default:
                werrstr("can't handle image type 0x%lux", chan);
                goto Err;
        case RGB16:
                for(i=0; i<ri.chanlen; i++, p+=2){
                        pixel = (p[1]<<8)|p[0]; /* rrrrrggg gggbbbbb */
                        v = (pixel & 0xF800) >> 8;
                        *q0++ = v | (v>>5);
                        v = (pixel & 0x07E0) >> 3;
                        *q1++ = v | (v>>6);
                        v = (pixel & 0x001F) << 3;
                        *q2++ = v | (v>>5);
                }
                break;
        case RGB24:
                for(i=0; i<ri.chanlen; i++){
                        *q2++ = *p++;
                        *q1++ = *p++;
                        *q0++ = *p++;
                }
                break;
        case RGBA32:
                for(i=0; i<ri.chanlen; i++){
                        *q2++ = *p++;
                        *q1++ = *p++;
                        *q0++ = *p++;
                        p++;
                }
                break;
        case ARGB32:
                for(i=0; i<ri.chanlen; i++){
                        p++;
                        *q2++ = *p++;
                        *q1++ = *p++;
                        *q0++ = *p++;
                }
                break;
        }

        rgbv = nil;
        nri = torgbv(&ri, 1);
        if(nri != nil){
                rgbv = nri->chans[0];
                free(nri);
        }

        free(ri.chans[0]);
        free(ri.chans[1]);
        free(ri.chans[2]);
        free(data);
        return rgbv;
}

Image*
onechan(Image *i)
{
        uchar *data;
        Image *ni;

        if(notrans(i->chan))
                return i;

        if(easycase(i->chan))
                data = load(i, nil);
        else{
                ni = allocimage(display, i->r, RGB24, 0, DNofill);
                if(ni == nil)
                        return ni;
                draw(ni, ni->r, i, nil, i->r.min);
                data = load(ni, nil);
                freeimage(ni);
        }

        if(data == nil)
                return nil;

        ni = allocimage(display, i->r, CMAP8, 0, DNofill);
        if(ni != nil)
                if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
                        freeimage(ni);
                        ni = nil;
                }
        free(data);
        return ni;
}

Memimage*
memonechan(Memimage *i)
{
        uchar *data;
        Memimage *ni;

        if(notrans(i->chan))
                return i;

        if(easycase(i->chan))
                data = load(nil, i);
        else{
                ni = allocmemimage(i->r, RGB24);
                if(ni == nil)
                        return ni;
                memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S);
                data = load(nil, ni);
                freememimage(ni);
        }

        if(data == nil)
                return nil;

        ni = allocmemimage(i->r, CMAP8);
        if(ni != nil)
                if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
                        freememimage(ni);
                        ni = nil;
                }
        free(data);
        return ni;
}