#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;
}