/*
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
*/

static const unsigned char swap_bits[256] =
{ 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,
  240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,
  120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,
  180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,
  60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,
  210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,
  90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,
  150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,
  30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,
  225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,
  105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,
  165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,
  45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,
  195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,
  75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,
  135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,
  15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255};


static void encode_truecolor_24_rgb_swapped( rgb_group *s,
                                             unsigned char *d,
                                             int q, int w )
{
  while(q--)
  {
    *(d++) = s->b;
    *(d++) = s->g;
    *(d++) = (s++)->r;
  }
}

static void encode_truecolor_24_rgb( rgb_group *s,
                                     unsigned char *d,
                                     int q, int w )
{
  memcpy(d,s,q);
}

static void encode_truecolor_24_rgb_al32_swapped( rgb_group *s,
                                                  unsigned char *d,
                                                  int q, int w )
{
  int l;
  while(q)
  {
    l=w;
    while(l--)
    {
      *(d++) = s->b;
      *(d++) = s->g;
      *(d++) = (s++)->r;
      q--;
    }
    d += (4-((w*3)&3))&3;
  }
}

static void encode_truecolor_24_rgb_al32( rgb_group *s,
                                          unsigned char *d,
                                          int q, int w )
{
  int l;
  while(q)
  {
    for(l=0; l<(q/w)/3; l++)
    {
      memcpy(d,s,w*3);
      d += (w*3+3)&~3;
      q--;
    }
  }
}


#define encode_truecolor_24_bgr encode_truecolor_24_rgb_swapped
#define encode_truecolor_24_bgr_swapped encode_truecolor_24_rgb
#define encode_truecolor_24_bgr_al32 encode_truecolor_24_rgb_al32_swapped
#define encode_truecolor_24_bgr_al32_swapped encode_truecolor_24_rgb_al32

static void encode_truecolor_32_argb_swapped( rgb_group *s,
                                              unsigned char *d,
                                              int q,int w )
{
  int *dp = (int *)d;
#if PIKE_BYTEORDER==1234
  while(q--){ *(dp++) = (s->r<<16) | (s->g<<8) | (s)->b;s++;}
#else
  while(q--){ *(dp++) = ((s->r) | (s->g<<8) | (s)->b<<16)<<8;s++;}
#endif
}

static void encode_truecolor_32_argb( rgb_group *s,
                                      unsigned char *d,
                                      int q,int w )
{
  int *dp = (int *)d;
#if PIKE_BYTEORDER==4321
  while(q--) {*(dp++) = (s->r<<16) | (s->g<<8) | (s)->b;s++;}
#else
  while(q--) {*(dp++) = ((s->r) | (s->g<<8) | (s)->b<<16)<<8;s++;}
#endif
}

static void encode_truecolor_32_abgr( rgb_group *s,
                                      unsigned char *d,
                                      int q,int w )
{
  while(q--)
  {
    *(d++) = 0;
    *(d++) = s->b;
    *(d++) = s->g;
    *(d++) = (s++)->r;
  }
}


static void encode_truecolor_32_abgr_swapped( rgb_group *s,
                                              unsigned char *d,
                                              int q,int w )
{
  while(q--)
  {
    *(d++) = s->r;
    *(d++) = s->g;
    *(d++) = (s++)->b;
    *(d++) = 0;
  }
}

#define ENCODE_8(X,Y,Z)                                                              \
static void encode_truecolor_##X##_##Y( rgb_group *s, unsigned char *d, int q,int w )           \
{                                                                       \
  while(q--)                                                            \
    {*(d++)=(Z);s++;}                                                     \
}                                                                       \
                                                                        \
static void encode_truecolor_##X##_##Y##_swapped( rgb_group *s, unsigned char *d, int q ,int w) \
{                                                                       \
  while(q--)                                                        \
    {*(d++)=(Z);s++;}                                                     \
}


#if PIKE_BYTEORDER == 1234
#define ENCODE_16(X,Y,Z)                                                              \
static void encode_truecolor_##X##_##Y( rgb_group *s, unsigned char *d, int q,int w )           \
{                                                                       \
  unsigned short *dp = (unsigned short *)d;                             \
  while(q--)                                                            \
    {*(dp++) = (Z);s++;}                                                 \
}                                                                       \
                                                                        \
static void encode_truecolor_##X##_##Y##_swapped( rgb_group *s, unsigned char *d, int q ,int w) \
{                                                                       \
  while(q--)                                                            \
  {                                                                     \
    unsigned short v = (Z);                                             \
    *(d++)=v&255;                                                       \
    *(d++)=v>>8;s++;                                                    \
  }                                                                     \
}
#else
#define ENCODE_16(X,Y,Z)                                                              \
static void encode_truecolor_##X##_##Y( rgb_group *s, unsigned char *d, int q,int w )           \
{                                                                       \
  while(q--)                                                            \
  {                                                                     \
    unsigned short v = (Z);                                             \
    *(d++)=v>>8;                                                        \
    *(d++)=v&255;s++;                                                   \
  }                                                                     \
}                                                                       \
                                                                        \
static void encode_truecolor_##X##_##Y##_swapped( rgb_group *s, unsigned char *d, int q ,int w) \
{                                                                       \
  unsigned short *dp = (unsigned short *)d;                             \
  while(q--)                                                            \
    {*(dp++) = (Z);s++;}                                                      \
}
#endif

ENCODE_16(565,rgb,(((s->r>>3)<<6) | (s->g>>2))<<5 | (s->b>>3));
ENCODE_16(565,bgr,(((s->b>>3)<<6) | (s->g>>2))<<5 | (s->r>>3));
ENCODE_16(555,rgb,(((s->r>>3)<<5) | (s->g>>3))<<5 | (s->b>>3));
ENCODE_16(555,bgr,(((s->b>>3)<<5) | (s->g>>3))<<5 | (s->r>>3));

ENCODE_8(232,rgb,(((s->r>>6)<<3) | (s->g>>5))<<2 | (s->b>>6));
ENCODE_8(332,rgb,(((s->r>>5)<<3) | (s->g>>5))<<2 | (s->b>>6));
ENCODE_8(242,rgb,(((s->r>>5)<<4) | (s->g>>4))<<2 | (s->b>>6));
ENCODE_8(233,rgb,(((s->r>>5)<<3) | (s->g>>4))<<3 | (s->b>>5));
ENCODE_8(232,bgr,(((s->b>>6)<<3) | (s->g>>5))<<2 | (s->r>>6));
ENCODE_8(332,bgr,(((s->b>>5)<<3) | (s->g>>5))<<2 | (s->r>>6));
ENCODE_8(242,bgr,(((s->b>>5)<<4) | (s->g>>4))<<2 | (s->r>>6));
ENCODE_8(233,bgr,(((s->b>>5)<<3) | (s->g>>4))<<3 | (s->r>>5));


static void encode_truecolor_generic(int rbits, int rshift, int gbits, 
                                     int gshift, int bbits, int bshift, 
                                     int bpp, int alignbits,int swap_bytes, 
                                     struct image *img, unsigned char *dest,
                                     int len)
{
   unsigned char *d = dest;
   int x,y;
   rgb_group *s;
   s=img->img;

   if(!alignbits) alignbits=1;

#define OPTIMIZE(rb,rs,gb,gs,bb,bs,al,fun) do { \
      if(rbits==rb && rshift==rs && gbits==gb && gshift==gs && \
    	 bbits==bb && bshift==bs && alignbits==al) { \
    	DEBUG_PF(("Optimized case %s%s\n", #fun, swap_bytes?"_swapped":"")); \
    	if (swap_bytes) \
    	  fun##_swapped(s, (void *)dest, img->xsize*img->ysize, img->xsize); \
    	else \
    	  fun(s,(void *)dest,img->xsize*img->ysize,img->xsize); \
        return; \
      } \
    } while(0)

   switch(bpp)
   {
    case 32: /* 4 alternatives */
      OPTIMIZE(8,16,8,8,8,0,1,encode_truecolor_32_argb);
      OPTIMIZE(8,0,8,8,8,16,1,encode_truecolor_32_abgr);
      break;
    case 24: /* 8 alternatives */
      OPTIMIZE(8,16,8,8,8,0,32,encode_truecolor_24_rgb_al32);
      OPTIMIZE(8,0,8,8,8,16,32,encode_truecolor_24_bgr_al32);
      OPTIMIZE(8,16,8,8,8,0,1,encode_truecolor_24_rgb);
      OPTIMIZE(8,0,8,8,8,16,1,encode_truecolor_24_bgr);
      break;
    case 16: /* 8 alternatives */
      OPTIMIZE(5,11,6,5,5,0,1,encode_truecolor_565_rgb);
      OPTIMIZE(5,10,5,5,5,0,1,encode_truecolor_555_rgb);
      OPTIMIZE(5,0,6,5,5,11,1,encode_truecolor_565_bgr);
      OPTIMIZE(5,0,5,5,5,10,1,encode_truecolor_555_bgr);
      break;
    case 8: /* 16 alternatives */
      OPTIMIZE(2,5,3,2,2,0,1,encode_truecolor_232_rgb);
      OPTIMIZE(3,5,3,2,2,0,1,encode_truecolor_332_rgb);
      OPTIMIZE(2,6,4,2,2,0,1,encode_truecolor_242_rgb);
      OPTIMIZE(2,6,3,3,3,0,1,encode_truecolor_233_rgb);
      OPTIMIZE(2,0,3,2,2,5,1,encode_truecolor_232_bgr);
      OPTIMIZE(3,0,3,2,2,5,1,encode_truecolor_332_bgr);
      OPTIMIZE(2,0,4,2,2,6,1,encode_truecolor_242_bgr);
      OPTIMIZE(2,0,3,3,3,6,1,encode_truecolor_233_bgr);
      break;
   }
#undef OPTIMIZE

   DEBUG_PF(("Generic encode_truecolor for r%dxg%dxb%d (%d bpp, shift=r%d,g%d,b%d)\n",
             rbits,gbits,bbits,bpp,rshift,gshift,bshift));
   {
     int rfmask,gfmask,bfmask;
     int rfshift,gfshift,bfshift,rzshift,gzshift,bzshift;
     int bpshift,blinemod,bit;

     *d=0;

     rfshift=rshift-(sizeof(COLORTYPE)*8-rbits);
     gfshift=gshift-(sizeof(COLORTYPE)*8-gbits);
     bfshift=bshift-(sizeof(COLORTYPE)*8-bbits);
     if (rfshift<0) rzshift=-rfshift,rfshift=0; else rzshift=0;
     if (gfshift<0) gzshift=-gfshift,gfshift=0; else gzshift=0;
     if (bfshift<0) bzshift=-bfshift,bfshift=0; else bzshift=0;

     rfmask=(((1<<rbits)-1)<<(sizeof(COLORTYPE)*8-rbits));
     gfmask=(((1<<gbits)-1)<<(sizeof(COLORTYPE)*8-gbits));
     bfmask=(((1<<bbits)-1)<<(sizeof(COLORTYPE)*8-bbits));
     bpshift=sizeof(unsigned long)*8-bpp;
     blinemod=alignbits-(((img->xsize*bpp+7)&~7)%alignbits);
     if(blinemod==alignbits)
       blinemod=0;
     else
       blinemod>>=3;

     bit=0;
     y = img->ysize;
     while (y--)
     {
       int bp;
       x=img->xsize;
       while (x--)
       {
         register unsigned long b =
           ((unsigned long)((((s->r&rfmask)>>rzshift)<<rfshift)|
			    (((s->g&gfmask)>>gzshift)<<gfshift)|
			    (((s->b&bfmask)>>bzshift)<<bfshift)))<<bpshift;
         bp = bpp;
         while (bp>8-bit)
         {
	   if(bit)
	     *d|=(unsigned char)(b>>(24+bit));
	   else
	     *d=(unsigned char)(b>>24);
           b<<=8-bit;
           bp-=8-bit;
           bit=0;
	   d++;
         }
	 if(bit)
	   *d|=b>>(24+bit); 
	 else
	   *d=b>>24;
         bit+=bp;
         if (bit==8) ++d,bit=0;
         s++;
       }
       if(!y)
	 break;
       if(bit) ++d,bit=0;
       bp=blinemod;
       while (bp--) *(++d)=0;
     }
   }

   if (swap_bytes)
   {
     d=dest;
     x=len;

     switch (bpp)
     {
      case 32:
        while (x>=4)
        {
          d[0]^=d[3],d[3]^=d[0],d[0]^=d[3];
          d[1]^=d[2],d[2]^=d[1],d[1]^=d[2];
          d+=4;
          x-=4;
        }
        break;
      case 24:
        while (x>=3)
        {
          d[0]^=d[2],d[2]^=d[0],d[0]^=d[2];
          d+=3;
          x-=3;
        }
        break;
      case 16:
        while (x>=2)
        {
          d[0]^=d[1],d[1]^=d[0],d[0]^=d[1];
          d+=2;
          x-=2;
        }
        break;
      case 1: 
        while (x--) {*(d)=swap_bits[*d]; d++; }
        break;
     }
   }
}

static INLINE void examine_mask(unsigned int x, int *bits,int *shift)
{
   *bits=0; 
   *shift=0; 
   if (!x) return;
   while (!(x&1)) x>>=1,(*shift)++;
   while (x&1) x>>=1,(*bits)++;
}

void pgtk2_encode_grey(struct image *i, unsigned char *dest, int bpp, int bpl )
{
  int x, y;
  rgb_group *s = i->img;
  switch(bpp)
  {
   case 1:
     for(y=0; y<i->ysize; y++)
     {
       unsigned char *d = dest+y*bpl;
       for(x=0; x<i->xsize; x++,s++)
         *d = (s->r + (s->g<<1) + s->b)>>2;
     }
     return;
   case 2:
     for(y=0; y<i->ysize; y++)
     {
       unsigned short *d = (unsigned short *)(dest+y*bpl);
       for(x=0; x<i->xsize; x++,s++)
         *d = (s->r + (s->g<<1) + s->b)<<6;
     }
     return;
   default:
     Pike_error("This greyscale is to wide for me!\n");
  }
}

void pgtk2_encode_truecolor_masks(struct image *i,
				  int bitspp,
				  int pad,
				  int byteorder,
				  unsigned int red_mask,
				  unsigned int green_mask,
				  unsigned int blue_mask,
				  unsigned char *buffer, 
				  int debug_len)
{
   int rbits,rshift,gbits,gshift,bbits,bshift;

  examine_mask(red_mask,&rbits,&rshift);
  examine_mask(green_mask,&gbits,&gshift);
  examine_mask(blue_mask,&bbits,&bshift);
  encode_truecolor_generic( rbits, rshift, gbits, gshift, bbits, bshift,
                            bitspp, pad, byteorder, i, buffer, debug_len );

}
