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

//#define DJ_DBG
#include "djImageOpColor.h"
#include <djMath.h>

namespace dj {

ImageOpColor::ImageOpColor(bool lut) :
  ImageOpPixel(lut),
  _value(1.0)
{}

const StrList ImageOpColor::attrNameList() const
  { return imageOpColorAttrStr(); }

bool ImageOpColor::attr(const Str & in, const StrList & data)
{
  StrList tmp = data;
  if (compareCase(attrNameList()[ATTR_VALUE], in)) {
    if (! inflateStr(tmp, &_value)) return false;
  } else
    return false;
  return true;
}

const StrList ImageOpColor::attr(const Str & in) const
{
  StrList out;
  if (compareCase(attrNameList()[ATTR_VALUE], in))
    out += deflateStr(_value);
  return out;
}

ImageOpColor2::ImageOpColor2(bool lut) :
  ImageOpPixel(lut),
  _value(1.0), _value2(1.0)
{}

const StrList ImageOpColor2::attrNameList() const
  { return imageOpColor2AttrStr(); }

bool ImageOpColor2::attr(const Str & in, const StrList & data)
{
  DJ_DBG_FNC("dj::ImageOpColor2::attr");
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("data = " << data);
  
  StrList tmp = data;
  if (compareCase(attrNameList()[ATTR_VALUE], in)) {
    if (! inflateStr(tmp, &_value)) return false;
  } else if (compareCase(attrNameList()[ATTR_VALUE_2], in)) {
    if (! inflateStr(tmp, &_value2)) return false;
  } else
    return false;
  
  return true;
}

const StrList ImageOpColor2::attr(const Str & in) const
{
  StrList out;
  if (compareCase(attrNameList()[ATTR_VALUE], in))
    out += deflateStr(_value);
  else if (compareCase(attrNameList()[ATTR_VALUE_2], in))
    out += deflateStr(_value2);
  return out;
}

#define _WORK() \
  switch (channels) { \
    case 1: \
      for (int x = 0; x < size; ++x, in += channels) { \
        _OP_BEGIN() \
        _OP(0) \
        _OP_END() \
      } \
      break; \
    case 2: \
      for (int x = 0; x < size; ++x, in += channels) { \
        _OP_BEGIN() \
        _OP(0) \
        _OP(1) \
        _OP_END() \
      } \
      break; \
    case 3: \
      for (int x = 0; x < size; ++x, in += channels) { \
        _OP_BEGIN() \
        _OP(0) \
        _OP(1) \
        _OP(2) \
        _OP_END() \
      } \
      break; \
    case 4: \
      for (int x = 0; x < size; ++x, in += channels) { \
        _OP_BEGIN() \
        _OP(0) \
        _OP(1) \
        _OP(2) \
        _OP(3) \
        _OP_END() \
      } \
      break; \
    default: \
      for (int x = 0; x < size; ++x, in += channels) { \
        _OP_BEGIN() \
        for (int c = 0; c < channels; ++c) \
          _OP(c) \
        _OP_END() \
      } \
      break; \
  }

ImageOpBrightness::ImageOpBrightness() :
  ImageOpColor(true)
{}

void ImageOpBrightness::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {
  
  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32);
  convert(_value, &value);
  
  const PixelF32 * const valueP =
    reinterpret_cast<const PixelF32 *>(value.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] *= valueP[c];
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] *= valueP[C];
  _WORK()
}

ImageOpContrast::ImageOpContrast() :
  ImageOpColor(true)
{}

void ImageOpContrast::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {
  
  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32), tmp(format, PIXEL_F32);
  convert(_value, &value);
  for (int c = 0; c < channels; ++c)
    tmp.f32(0.5 * (1.0 - value.f32(c)), c);

  const PixelF32 * const valueP =
    reinterpret_cast<const PixelF32 *>(value.data());
  const PixelF32 * const tmpP =
    reinterpret_cast<PixelF32 *>(tmp.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = in[c] * valueP[c] + tmpP[c];
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = in[C] * valueP[C] + tmpP[C];
  _WORK()
}

ImageOpHue::ImageOpHue()
{
  _value = Color(0.0);
}

void ImageOpHue::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {
  if (PIXEL_L == format || PIXEL_LA == format) return;
  
  const int channels = pixelSize(format);
  
  const double value = _value.f32(0);

  double rgb [] = { 0.0, 0.0, 0.0 }, hsv [3] = { 0.0, 0.0, 0.0 };
  for (int x = 0; x < size; ++x, in += channels) {
  
    rgb[0] = in[0];
    rgb[1] = in[1];
    rgb[2] = in[2];
    rgbToHsv(rgb, hsv);
    
    hsv[0] += value;
    
    hsvToRgb(hsv, rgb);
    in[0] = rgb[0];
    in[1] = rgb[1];
    in[2] = rgb[2];
  }
}

ImageOpGamma::ImageOpGamma() :
  ImageOpColor(true)
{}

void ImageOpGamma::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {
  DJ_DBG_FNC("dj::ImageOpGamma::workF32");
  
  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32);
  convert(_value, &value);
  for (int c = 0; c < channels; ++c)
    value.f32(1.0 / max(almostZeroF, value.f32(c)), c);

  const PixelF32 * const valueP =
    reinterpret_cast<const PixelF32 *>(value.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = pow(in[c], valueP[c]);
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = pow(in[C], valueP[C]);
  _WORK()
}

ImageOpLevelsIn::ImageOpLevelsIn() :
  ImageOpColor2(true)
{
  _value = Color(0.0);
  _value2 = Color(1.0);
}

void ImageOpLevelsIn::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {

  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32), value2(format, PIXEL_F32);
  convert(_value, &value);
  convert(_value2, &value2);
  for (int c = 0; c < channels; ++c)
    value2.f32(value2.f32(c) - value.f32(c), c);
    
  const PixelF32 * const valueP = reinterpret_cast<PixelF32 *>(value.data());
  const PixelF32 * const value2P = reinterpret_cast<PixelF32 *>(value2.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = (in[c] - valueP[c]) / value2P[c];
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = (in[C] - valueP[C]) / value2P[C];
  _WORK()
}

ImageOpLevelsOut::ImageOpLevelsOut() :
  ImageOpColor2(true)
{
  _value = Color(0.0);
  _value2 = Color(1.0);
}

void ImageOpLevelsOut::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {

  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32), value2(format, PIXEL_F32);
  convert(_value, &value);
  convert(_value2, &value2);
  for (int c = 0; c < channels; ++c)
    value2.f32(value2.f32(c) - value.f32(c), c);
  
  const PixelF32 * const valueP = reinterpret_cast<PixelF32 *>(value.data());
  const PixelF32 * const value2P = reinterpret_cast<PixelF32 *>(value2.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = in[c] * value2P[c] + valueP[c];
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = in[C] * value2P[C] + valueP[C];
  _WORK()
}

ImageOpSoftClip::ImageOpSoftClip() :
  ImageOpColor(true)
{
  _value = Color(0.0);
}

void ImageOpSoftClip::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {

  const int channels = pixelSize(format);
  
  Color value(format, PIXEL_F32);
  convert(_value, &value);
  
  const PixelF32 * const valueP = reinterpret_cast<PixelF32 *>(value.data());
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = softClip(in[c], valueP[c]);
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = softClip(in[C], valueP[C]);
  _WORK()
}

ImageOpNegate::ImageOpNegate() :
  ImageOpPixel(true)
{}

void ImageOpNegate::workF32(
  PixelF32 * in, int size, PIXEL_FORMAT format
) const {

  const int channels = pixelSize(format);
  
  //for (int x = 0; x < size; ++x, in += channels)
  //  for (int c = 0; c < channels; ++c)
  //    in[c] = 1.0f - in[c];
# undef _OP_BEGIN
# undef _OP_END
# undef _OP
# define _OP_BEGIN()
# define _OP_END()
# define _OP(C) in[C] = 1.0f - in[C];
  _WORK()
}

}
