text: // Copyright (C) 2002-2012 Nikolaus Gebhardt
  // This file is part of the "Irrlicht Engine".
  // For conditions of distribution and use, see copyright notice in irrlicht.h
  
  #include "CColorConverter.h"
  #include "SColor.h"
  #include "os.h"
  #include "irrString.h"
  
  namespace irr
  {
  namespace video
  {
  
  //! converts a monochrome bitmap to A1R5G5B5 data
  void CColorConverter::convert1BitTo16Bit(const u8* in, s16* out, s32 width, s32 height, s32 linepad, bool flip)
  {
      if (!in || !out)
          return;
  
      if (flip)
          out += width * height;
  
      for (s32 y=0; y<height; ++y)
      {
          s32 shift = 7;
          if (flip)
              out -= width;
  
          for (s32 x=0; x<width; ++x)
          {
              out[x] = *in>>shift & 0x01 ? (s16)0xffff : (s16)0x8000;
  
              if ((--shift)<0) // 8 pixel done
              {
                  shift=7;
                  ++in;
              }
          }
  
          if (shift != 7) // width did not fill last byte
              ++in;
  
          if (!flip)
              out += width;
          in += linepad;
      }
  }
  
  
  
  //! converts a 4 bit palettized image to A1R5G5B5
  void CColorConverter::convert4BitTo16Bit(const u8* in, s16* out, s32 width, s32 height, const s32* palette, s32 linepad, bool flip)
  {
      if (!in || !out || !palette)
          return;
  
      if (flip)
          out += width*height;
  
      for (s32 y=0; y<height; ++y)
      {
          s32 shift = 4;
          if (flip)
              out -= width;
  
          for (s32 x=0; x<width; ++x)
          {
              out[x] = X8R8G8B8toA1R5G5B5(palette[(u8)((*in >> shift) & 0xf)]);
  
              if (shift==0)
              {
                  shift = 4;
                  ++in;
              }
              else
                  shift = 0;
          }
  
          if (shift == 0) // odd width
              ++in;
  
          if (!flip)
              out += width;
          in += linepad;
      }
  }
  
  
  
  //! converts a 8 bit palettized image into A1R5G5B5
  void CColorConverter::convert8BitTo16Bit(const u8* in, s16* out, s32 width, s32 height, const s32* palette, s32 linepad, bool flip)
  {
      if (!in || !out || !palette)
          return;
  
      if (flip)
          out += width * height;
  
      for (s32 y=0; y<height; ++y)
      {
          if (flip)
              out -= width; // one line back
          for (s32 x=0; x<width; ++x)
          {
              out[x] = X8R8G8B8toA1R5G5B5(palette[(u8)(*in)]);
              ++in;
          }
          if (!flip)
              out += width;
          in += linepad;
      }
  }
  
  //! converts a 8 bit palettized or non palettized image (A8) into R8G8B8
  void CColorConverter::convert8BitTo24Bit(const u8* in, u8* out, s32 width, s32 height, const u8* palette, s32 linepad, bool flip)
  {
      if (!in || !out )
          return;
  
      const s32 lineWidth = 3 * width;
      if (flip)
          out += lineWidth * height;
  
      for (s32 y=0; y<height; ++y)
      {
          if (flip)
              out -= lineWidth; // one line back
          for (s32 x=0; x< lineWidth; x += 3)
          {
              if ( palette )
              {
  #ifdef __BIG_ENDIAN__
                  out[x+0] = palette[ (in[0] << 2 ) + 0];
                  out[x+1] = palette[ (in[0] << 2 ) + 1];
                  out[x+2] = palette[ (in[0] << 2 ) + 2];
  #else
                  out[x+0] = palette[ (in[0] << 2 ) + 2];
                  out[x+1] = palette[ (in[0] << 2 ) + 1];
                  out[x+2] = palette[ (in[0] << 2 ) + 0];
  #endif
              }
              else
              {
                  out[x+0] = in[0];
                  out[x+1] = in[0];
                  out[x+2] = in[0];
              }
              ++in;
          }
          if (!flip)
              out += lineWidth;
          in += linepad;
      }
  }
  
  //! converts a 8 bit palettized or non palettized image (A8) into R8G8B8
  void CColorConverter::convert8BitTo32Bit(const u8* in, u8* out, s32 width, s32 height, const u8* palette, s32 linepad, bool flip)
  {
      if (!in || !out )
          return;
  
      const u32 lineWidth = 4 * width;
      if (flip)
          out += lineWidth * height;
  
      u32 x;
      register u32 c;
      for (u32 y=0; y < (u32) height; ++y)
      {
          if (flip)
              out -= lineWidth; // one line back
  
          if ( palette )
          {
              for (x=0; x < (u32) width; x += 1)
              {
                  c = in[x];
                  ((u32*)out)[x] = ((u32*)palette)[ c ];
              }
          }
          else
          {
              for (x=0; x < (u32) width; x += 1)
              {
                  c = in[x];
  #ifdef __BIG_ENDIAN__
                  ((u32*)out)[x] = c << 24 | c << 16 | c << 8 | 0x000000FF;
  #else
                  ((u32*)out)[x] = 0xFF000000 | c << 16 | c << 8 | c;
  #endif
              }
  
          }
  
          if (!flip)
              out += lineWidth;
          in += width + linepad;
      }
  }
  
  
  
  //! converts 16bit data to 16bit data
  void CColorConverter::convert16BitTo16Bit(const s16* in, s16* out, s32 width, s32 height, s32 linepad, bool flip)
  {
      if (!in || !out)
          return;
  
      if (flip)
          out += width * height;
  
      for (s32 y=0; y<height; ++y)
      {
          if (flip)
              out -= width;
  #ifdef __BIG_ENDIAN__
          for (s32 x=0; x<width; ++x)
              out[x]=os::Byteswap::byteswap(in[x]);
  #else
          memcpy(out, in, width*sizeof(s16));
  #endif
          if (!flip)
              out += width;
          in += width;
          in += linepad;
      }
  }
  
  
  
  //! copies R8G8B8 24bit data to 24bit data
  void CColorConverter::convert24BitTo24Bit(const u8* in, u8* out, s32 width, s32 height, s32 linepad, bool flip, bool bgr)
  {
      if (!in || !out)
          return;
  
      const s32 lineWidth = 3 * width;
      if (flip)
          out += lineWidth * height;
  
      for (s32 y=0; y<height; ++y)
      {
          if (flip)
              out -= lineWidth;
          if (bgr)
          {
              for (s32 x=0; x<lineWidth; x+=3)
              {
                  out[x+0] = in[x+2];
                  out[x+1] = in[x+1];
                  out[x+2] = in[x+0];
              }
          }
          else
          {
              memcpy(out,in,lineWidth);
          }
          if (!flip)
              out += lineWidth;
          in += lineWidth;
          in += linepad;
      }
  }
  
  
  
  //! Resizes the surface to a new size and converts it at the same time
  //! to an A8R8G8B8 format, returning the pointer to the new buffer.
  void CColorConverter::convert16bitToA8R8G8B8andResize(const s16* in, s32* out, s32 newWidth, s32 newHeight, s32 currentWidth, s32 currentHeight)
  {
      if (!newWidth || !newHeight)
          return;
  
      // note: this is very very slow. (i didn't want to write a fast version.
      // but hopefully, nobody wants to convert surfaces every frame.    
  
      f32 sourceXStep = (f32)currentWidth / (f32)newWidth;
      f32 sourceYStep = (f32)currentHeight / (f32)newHeight;
      f32 sy;
      s32 t;
  
      for (s32 x=0; x<newWidth; ++x)
      {
          sy = 0.0f;
  
          for (s32 y=0; y<newHeight; ++y)
          {
              t = in[(s32)(((s32)sy)*currentWidth + x*sourceXStep)];
              t = (((t >> 15)&0x1)<<31) |    (((t >> 10)&0x1F)<<19) |
                  (((t >> 5)&0x1F)<<11) |    (t&0x1F)<<3;
              out[(s32)(y*newWidth + x)] = t;
  
              sy+=sourceYStep;
          }
      }
  }
  
  
  
  //! copies X8R8G8B8 32 bit data
  void CColorConverter::convert32BitTo32Bit(const s32* in, s32* out, s32 width, s32 height, s32 linepad, bool flip)
  {
      if (!in || !out)
          return;
  
      if (flip)
          out += width * height;
  
      for (s32 y=0; y<height; ++y)
      {
          if (flip)
              out -= width;
  #ifdef __BIG_ENDIAN__
          for (s32 x=0; x<width; ++x)
              out[x]=os::Byteswap::byteswap(in[x]);
  #else
          memcpy(out, in, width*sizeof(s32));
  #endif
          if (!flip)
              out += width;
          in += width;
          in += linepad;
      }
  }
  
  
  
  void CColorConverter::convert_A1R5G5B5toR8G8B8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u8 * dB = (u8 *)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          dB[2] = (*sB & 0x7c00) >> 7;
          dB[1] = (*sB & 0x03e0) >> 2;
          dB[0] = (*sB & 0x1f) << 3;
  
          sB += 1;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_A1R5G5B5toB8G8R8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u8 * dB = (u8 *)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          dB[0] = (*sB & 0x7c00) >> 7;
          dB[1] = (*sB & 0x03e0) >> 2;
          dB[2] = (*sB & 0x1f) << 3;
  
          sB += 1;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_A1R5G5B5toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u32* dB = (u32*)dP;
  
      for (s32 x = 0; x < sN; ++x)
          *dB++ = A1R5G5B5toA8R8G8B8(*sB++);
  }
  
  void CColorConverter::convert_A1R5G5B5toA1R5G5B5(const void* sP, s32 sN, void* dP)
  {
      memcpy(dP, sP, sN * 2);
  }
  
  void CColorConverter::convert_A1R5G5B5toR5G6B5(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
          *dB++ = A1R5G5B5toR5G6B5(*sB++);
  }
  
  void CColorConverter::convert_A8R8G8B8toR8G8B8(const void* sP, s32 sN, void* dP)
  {
      u8* sB = (u8*)sP;
      u8* dB = (u8*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          // sB[3] is alpha
          dB[0] = sB[2];
          dB[1] = sB[1];
          dB[2] = sB[0];
  
          sB += 4;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_A8R8G8B8toB8G8R8(const void* sP, s32 sN, void* dP)
  {
      u8* sB = (u8*)sP;
      u8* dB = (u8*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          // sB[3] is alpha
          dB[0] = sB[0];
          dB[1] = sB[1];
          dB[2] = sB[2];
  
          sB += 4;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_A8R8G8B8toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      memcpy(dP, sP, sN * 4);
  }
  
  void CColorConverter::convert_A8R8G8B8toA1R5G5B5(const void* sP, s32 sN, void* dP)
  {
      u32* sB = (u32*)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
          *dB++ = A8R8G8B8toA1R5G5B5(*sB++);
  }
  
  void CColorConverter::convert_A8R8G8B8toR5G6B5(const void* sP, s32 sN, void* dP)
  {
      u8 * sB = (u8 *)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          s32 r = sB[2] >> 3;
          s32 g = sB[1] >> 2;
          s32 b = sB[0] >> 3;
  
          dB[0] = (r << 11) | (g << 5) | (b);
  
          sB += 4;
          dB += 1;
      }
  }
  
  void CColorConverter::convert_A8R8G8B8toR3G3B2(const void* sP, s32 sN, void* dP)
  {
      u8* sB = (u8*)sP;
      u8* dB = (u8*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          u8 r = sB[2] & 0xe0;
          u8 g = (sB[1] & 0xe0) >> 3;
          u8 b = (sB[0] & 0xc0) >> 6;
  
          dB[0] = (r | g | b);
  
          sB += 4;
          dB += 1;
      }
  }
  
  void CColorConverter::convert_R8G8B8toR8G8B8(const void* sP, s32 sN, void* dP)
  {
      memcpy(dP, sP, sN * 3);
  }
  
  void CColorConverter::convert_R8G8B8toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      u8*  sB = (u8* )sP;
      u32* dB = (u32*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          *dB = 0xff000000 | (sB[0]<<16) | (sB[1]<<8) | sB[2];
  
          sB += 3;
          ++dB;
      }
  }
  
  void CColorConverter::convert_R8G8B8toA1R5G5B5(const void* sP, s32 sN, void* dP)
  {
      u8 * sB = (u8 *)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          s32 r = sB[0] >> 3;
          s32 g = sB[1] >> 3;
          s32 b = sB[2] >> 3;
  
          dB[0] = (0x8000) | (r << 10) | (g << 5) | (b);
  
          sB += 3;
          dB += 1;
      }
  }
  
  void CColorConverter::convert_B8G8R8toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      u8*  sB = (u8* )sP;
      u32* dB = (u32*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          *dB = 0xff000000 | (sB[2]<<16) | (sB[1]<<8) | sB[0];
  
          sB += 3;
          ++dB;
      }
  }
  
  void CColorConverter::convert_B8G8R8A8toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      u8* sB = (u8*)sP;
      u8* dB = (u8*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          dB[0] = sB[3];
          dB[1] = sB[2];
          dB[2] = sB[1];
          dB[3] = sB[0];
  
          sB += 4;
          dB += 4;
      }
  
  }
  
  void CColorConverter::convert_R8G8B8toR5G6B5(const void* sP, s32 sN, void* dP)
  {
      u8 * sB = (u8 *)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          s32 r = sB[0] >> 3;
          s32 g = sB[1] >> 2;
          s32 b = sB[2] >> 3;
  
          dB[0] = (r << 11) | (g << 5) | (b);
  
          sB += 3;
          dB += 1;
      }
  }
  
  void CColorConverter::convert_R5G6B5toR5G6B5(const void* sP, s32 sN, void* dP)
  {
      memcpy(dP, sP, sN * 2);
  }
  
  void CColorConverter::convert_R5G6B5toR8G8B8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u8 * dB = (u8 *)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          dB[0] = (*sB & 0xf800) >> 8;
          dB[1] = (*sB & 0x07e0) >> 3;
          dB[2] = (*sB & 0x001f) << 3;
  
          sB += 1;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_R5G6B5toB8G8R8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u8 * dB = (u8 *)dP;
  
      for (s32 x = 0; x < sN; ++x)
      {
          dB[2] = (*sB & 0xf800) >> 8;
          dB[1] = (*sB & 0x07e0) >> 3;
          dB[0] = (*sB & 0x001f) << 3;
  
          sB += 1;
          dB += 3;
      }
  }
  
  void CColorConverter::convert_R5G6B5toA8R8G8B8(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u32* dB = (u32*)dP;
  
      for (s32 x = 0; x < sN; ++x)
          *dB++ = R5G6B5toA8R8G8B8(*sB++);
  }
  
  void CColorConverter::convert_R5G6B5toA1R5G5B5(const void* sP, s32 sN, void* dP)
  {
      u16* sB = (u16*)sP;
      u16* dB = (u16*)dP;
  
      for (s32 x = 0; x < sN; ++x)
          *dB++ = R5G6B5toA1R5G5B5(*sB++);
  }
  
  
  void CColorConverter::convert_viaFormat(const void* sP, ECOLOR_FORMAT sF, s32 sN,
                  void* dP, ECOLOR_FORMAT dF)
  {
      switch (sF)
      {
          case ECF_A1R5G5B5:
              switch (dF)
              {
                  case ECF_A1R5G5B5:
                      convert_A1R5G5B5toA1R5G5B5(sP, sN, dP);
                  break;
                  case ECF_R5G6B5:
                      convert_A1R5G5B5toR5G6B5(sP, sN, dP);
                  break;
                  case ECF_A8R8G8B8:
                      convert_A1R5G5B5toA8R8G8B8(sP, sN, dP);
                  break;
                  case ECF_R8G8B8:
                      convert_A1R5G5B5toR8G8B8(sP, sN, dP);
                  break;
  #ifndef _DEBUG
                  default:
                      break;
  #endif
              }
          break;
          case ECF_R5G6B5:
              switch (dF)
              {
                  case ECF_A1R5G5B5:
                      convert_R5G6B5toA1R5G5B5(sP, sN, dP);
                  break;
                  case ECF_R5G6B5:
                      convert_R5G6B5toR5G6B5(sP, sN, dP);
                  break;
                  case ECF_A8R8G8B8:
                      convert_R5G6B5toA8R8G8B8(sP, sN, dP);
                  break;
                  case ECF_R8G8B8:
                      convert_R5G6B5toR8G8B8(sP, sN, dP);
                  break;
  #ifndef _DEBUG
                  default:
                      break;
  #endif
              }
          break;
          case ECF_A8R8G8B8:
              switch (dF)
              {
                  case ECF_A1R5G5B5:
                      convert_A8R8G8B8toA1R5G5B5(sP, sN, dP);
                  break;
                  case ECF_R5G6B5:
                      convert_A8R8G8B8toR5G6B5(sP, sN, dP);
                  break;
                  case ECF_A8R8G8B8:
                      convert_A8R8G8B8toA8R8G8B8(sP, sN, dP);
                  break;
                  case ECF_R8G8B8:
                      convert_A8R8G8B8toR8G8B8(sP, sN, dP);
                  break;
  #ifndef _DEBUG
                  default:
                      break;
  #endif
              }
          break;
          case ECF_R8G8B8:
              switch (dF)
              {
                  case ECF_A1R5G5B5:
                      convert_R8G8B8toA1R5G5B5(sP, sN, dP);
                  break;
                  case ECF_R5G6B5:
                      convert_R8G8B8toR5G6B5(sP, sN, dP);
                  break;
                  case ECF_A8R8G8B8:
                      convert_R8G8B8toA8R8G8B8(sP, sN, dP);
                  break;
                  case ECF_R8G8B8:
                      convert_R8G8B8toR8G8B8(sP, sN, dP);
                  break;
  #ifndef _DEBUG
                  default:
                      break;
  #endif
              }
          break;
      }
  }
  
  
  } // end namespace video
  } // end namespace irr
  