
// epimgconv: Enterprise 128 image converter utility
// Copyright (C) 2008-2016 Istvan Varga <istvanv@users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// The Enterprise 128 program files generated by this utility are not covered
// by the GNU General Public License, and can be used, modified, and
// distributed without any restrictions.

#include "epimgconv.hpp"
#include "imageconv.hpp"
#include "tvc_16.hpp"

#include <vector>

namespace Ep128ImgConv {

  void ImageConv_TVCPixel16::initializePalettes()
  {
    for (int i = 0; i < 256; i++)
      convertTVCColorToYUV(i, paletteY[i], paletteU[i], paletteV[i]);
  }

  void ImageConv_TVCPixel16::pixelStoreCallback(void *userData, int xc, int yc,
                                                float y, float u, float v)
  {
    ImageConv_TVCPixel16&   this_ =
        *(reinterpret_cast<ImageConv_TVCPixel16 *>(userData));
    xc = xc >> 2;
    yc = yc >> 1;
    if (xc < 0 || xc >= this_.width || yc < 0 || yc >= this_.height)
      return;
    limitYUVColor(y, u, v);
    this_.inputImage.y(xc, yc) += (y * 0.125f);
    this_.inputImage.u(xc, yc) += (u * 0.125f);
    this_.inputImage.v(xc, yc) += (v * 0.125f);
  }

  void ImageConv_TVCPixel16::pixelStoreCallbackI(void *userData, int xc, int yc,
                                                 float y, float u, float v)
  {
    ImageConv_TVCPixel16&   this_ =
        *(reinterpret_cast<ImageConv_TVCPixel16 *>(userData));
    xc = xc >> 2;
    if (xc < 0 || xc >= this_.width || yc < 0 || yc >= this_.height)
      return;
    limitYUVColor(y, u, v);
    this_.inputImage.y(xc, yc) += (y * 0.25f);
    this_.inputImage.u(xc, yc) += (u * 0.25f);
    this_.inputImage.v(xc, yc) += (v * 0.25f);
  }

  ImageConv_TVCPixel16::ImageConv_TVCPixel16()
    : ImageConverter(),
      width(1),
      height(1),
      inputImage(1, 1),
      ditherErrorImage(1, 1),
      convertedImage(1, 1),
      borderColor(0x00),
      ditherType(1),
      ditherDiffusion(0.95f)
  {
    initializePalettes();
  }

  ImageConv_TVCPixel16::~ImageConv_TVCPixel16()
  {
  }

  bool ImageConv_TVCPixel16::processImage(
      ImageData& imgData, const char *infileName,
      YUVImageConverter& imgConv, const ImageConvConfig& config)
  {
    width = config.width << 2;
    height = ((imgData[5] & 0x80) == 0 ? config.height : (config.height << 1));
    borderColor = (config.borderColor | (config.borderColor >> 1)) & 0x55;
    float   borderY = 0.0f;
    float   borderU = 0.0f;
    float   borderV = 0.0f;
    convertTVCColorToYUV(borderColor, borderY, borderU, borderV);
    inputImage.setBorderColor(borderY, borderU, borderV);
    ditherType = config.ditherType;
    limitValue(ditherType, 0, 5);
    ditherDiffusion = float(config.ditherDiffusion);

    inputImage.resize(width, height);
    ditherErrorImage.resize(width, height);
    convertedImage.resize(width, height);
    inputImage.clear();
    ditherErrorImage.clear();
    convertedImage.clear();

    initializePalettes();

    if (!(imgData[5] & 0x80))
      imgConv.setPixelStoreCallback(&pixelStoreCallback, (void *) this);
    else
      imgConv.setPixelStoreCallback(&pixelStoreCallbackI, (void *) this);
    if (!imgConv.convertImageFile(infileName))
      return false;

    progressMessage("Converting image");
    setProgressPercentage(0);
    unsigned char palette[16];
    for (int i = 0; i < 16; i++) {
      palette[i] = (unsigned char) ((i & 1) | ((i & 2) << 1)
                                    | ((i & 4) << 2) | ((i & 8) << 3));
    }
    for (int yc = 0; yc < height; yc++) {
      if (ditherType < 4) {
        ditherLine(convertedImage, inputImage, ditherErrorImage, yc,
                   ditherType, ditherDiffusion,
                   float(config.colorErrorScale), &(palette[0]), 16,
                   paletteY, paletteU, paletteV);
      }
      else {
        ditherLine_ordered_TVC16(convertedImage, inputImage, yc, ditherType);
      }
    }
    imgData.setBorderColor(borderColor);
    for (int yc = 0; yc < height; yc++) {
      for (int xc = 0; xc < width; xc++) {
        unsigned char c = convertedImage[yc][xc];
        c = (c != 8 ? c : 0);
        c = (c & 6) | ((c & 1) << 3) | ((c & 8) >> 3);
        imgData.setPixel(xc, yc, c);
      }
    }
    setProgressPercentage(100);
    progressMessage("");
    return true;
  }

}       // namespace Ep128ImgConv

