/*------------------------------------------------------------------------------
 src/lib/djImage/pixelLut.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djPixel.h"

namespace dj {

#define _PIXEL_LUT_INIT(IN_TYPE, OUT_TYPE, CHANNELS) \
  const IN_TYPE * inP = reinterpret_cast<const IN_TYPE *>(in); \
  const IN_TYPE * end = inP + size * CHANNELS; \
  OUT_TYPE * outP = reinterpret_cast<OUT_TYPE *>(out); \
  const OUT_TYPE * const lut0P = \
    reinterpret_cast<const OUT_TYPE *>(lutPlanar);

#define _PIXEL_LUT1(IN_TYPE, OUT_TYPE, LUT_SIZE) \
  _PIXEL_LUT_INIT(IN_TYPE, OUT_TYPE, 1) \
  while (inP != end) \
    *outP++ = lut0P[*inP++];

#define _PIXEL_LUT2(IN_TYPE, OUT_TYPE, LUT_SIZE) \
  _PIXEL_LUT_INIT(IN_TYPE, OUT_TYPE, 2) \
  const OUT_TYPE * const lut1P = lut0P + LUT_SIZE; \
  for (; inP != end; inP += 2, outP += 2) { \
    outP[0] = lut0P[inP[0]]; \
    outP[1] = lut1P[inP[1]]; \
  }

#define _PIXEL_LUT3(IN_TYPE, OUT_TYPE, LUT_SIZE) \
  _PIXEL_LUT_INIT(IN_TYPE, OUT_TYPE, 3) \
  const OUT_TYPE * const lut1P = lut0P + LUT_SIZE; \
  const OUT_TYPE * const lut2P = lut1P + LUT_SIZE; \
  for (; inP != end; inP += 3, outP += 3) { \
    outP[0] = lut0P[inP[0]]; \
    outP[1] = lut1P[inP[1]]; \
    outP[2] = lut2P[inP[2]]; \
  }

#define _PIXEL_LUT4(IN_TYPE, OUT_TYPE, LUT_SIZE) \
  _PIXEL_LUT_INIT(IN_TYPE, OUT_TYPE, 4) \
  const OUT_TYPE * const lut1P = lut0P + LUT_SIZE; \
  const OUT_TYPE * const lut2P = lut1P + LUT_SIZE; \
  const OUT_TYPE * const lut3P = lut2P + LUT_SIZE; \
  for (; inP != end; inP += 4, outP += 4) { \
    outP[0] = lut0P[inP[0]]; \
    outP[1] = lut1P[inP[1]]; \
    outP[2] = lut2P[inP[2]]; \
    outP[3] = lut3P[inP[3]]; \
  }

void pixelLut(
  const void * in, void * out, int size,
  const void * lutPlanar, int lutSize,
  PIXEL_FORMAT format, PIXEL_TYPE inType, PIXEL_TYPE outType
) {
  if (size <= 0) return;
  
  DJ_DBG_FNC("dj::pixelLut");
  DJ_DBG_PRN("size = " << size);
  DJ_DBG_PRN("lut size = " << lutSize);
  DJ_DBG_PRN("format = " << format);
  DJ_DBG_PRN("type = " << inType << " " << outType);

  const int channels = pixelSize(format);
  switch (inType) {
  
    case PIXEL_U8:
      switch (outType) {
        case PIXEL_U8: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU8, PixelU8, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU8, PixelU8, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU8, PixelU8, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU8, PixelU8, lutSize) } break;
          }
        } break;
        case PIXEL_U16: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU8, PixelU16, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU8, PixelU16, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU8, PixelU16, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU8, PixelU16, lutSize) } break;
          }
        } break;
        case PIXEL_F32: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU8, PixelF32, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU8, PixelF32, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU8, PixelF32, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU8, PixelF32, lutSize) } break;
          }
        } break;
      }
      break;
  
    case PIXEL_U16:
      switch (outType) {
        case PIXEL_U8: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU16, PixelU8, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU16, PixelU8, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU16, PixelU8, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU16, PixelU8, lutSize) } break;
          }
        } break;
        case PIXEL_U16: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU16, PixelU16, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU16, PixelU16, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU16, PixelU16, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU16, PixelU16, lutSize) } break;
          }
        } break;
        case PIXEL_F32: {
          switch (channels) {
            case 1: { _PIXEL_LUT1(PixelU16, PixelF32, lutSize) } break;
            case 2: { _PIXEL_LUT2(PixelU16, PixelF32, lutSize) } break;
            case 3: { _PIXEL_LUT3(PixelU16, PixelF32, lutSize) } break;
            case 4: { _PIXEL_LUT4(PixelU16, PixelF32, lutSize) } break;
          }
        } break;
      }
      break;
      
    case PIXEL_F32: break;
  }
}

}
