

#include "stdfx.h"
#include "tfxparam.h"

namespace {
template <typename T, typename PIXEL>
void prepare_lut(int levels, std::vector<T> &lut) {
  int i, j;
  int valuestep = PIXEL::maxChannelValue / (levels - 1);
  int step      = PIXEL::maxChannelValue / levels;
  for (j = 0; j < levels; j++)
    for (i = 0; i <= step; i++) lut[i + j * step] = j * valuestep;
}
}

//===================================================================

class PosterizeFx final : public TStandardRasterFx {
  FX_PLUGIN_DECLARATION(PosterizeFx)

  TRasterFxPort m_input;
  TDoubleParamP m_levels;

public:
  PosterizeFx() : m_levels(7.0) {
    bindParam(this, "levels", m_levels);
    addInputPort("Source", m_input);
    m_levels->setValueRange(2.0, 10.0);
  }

  ~PosterizeFx(){};

  bool doGetBBox(double frame, TRectD &bBox,
                 const TRenderSettings &info) override {
    if (m_input.isConnected()) {
      bool ret = m_input->doGetBBox(frame, bBox, info);
      return ret;
    } else {
      bBox = TRectD();
      return false;
    }
  }

  void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override;

  bool canHandle(const TRenderSettings &info, double frame) override {
    return true;
  }
};

//-------------------------------------------------------------------

template <typename PIXEL, typename CHANNEL_TYPE>
void doPosterize(TRasterPT<PIXEL> ras, int levels) {
  std::vector<CHANNEL_TYPE> solarize_lut(PIXEL::maxChannelValue + 1);

  prepare_lut<CHANNEL_TYPE, PIXEL>(levels, solarize_lut);
  int j;
  ras->lock();
  for (j = 0; j < ras->getLy(); j++) {
    PIXEL *pix    = ras->pixels(j);
    PIXEL *endPix = pix + ras->getLx();
    while (pix < endPix) {
      pix->r = (CHANNEL_TYPE)(solarize_lut[(int)(pix->r)]);
      pix->g = (CHANNEL_TYPE)(solarize_lut[(int)(pix->g)]);
      pix->b = (CHANNEL_TYPE)(solarize_lut[(int)(pix->b)]);
      *pix++;
    }
  }
  ras->unlock();
}
//-------------------------------------------------------------------

void PosterizeFx::doCompute(TTile &tile, double frame,
                            const TRenderSettings &ri) {
  if (!m_input.isConnected()) return;

  m_input->compute(tile, frame, ri);

  int levels          = (int)m_levels->getValue(frame);
  TRaster32P raster32 = tile.getRaster();
  if (raster32)
    doPosterize<TPixel32, UCHAR>(raster32, levels);
  else {
    TRaster64P raster64 = tile.getRaster();
    if (raster64)
      doPosterize<TPixel64, USHORT>(raster64, levels);
    else
      throw TException("Brightness&Contrast: unsupported Pixel Type");
  }
}

FX_PLUGIN_IDENTIFIER(PosterizeFx, "posterizeFx");
