// ----------------------------------------------------------------------------
//
// Copyright (C) Columbia University, 1994. All Rights Reserved.
// Sameer A. Nene, Shree K. Nayar, Hiroshi Murase
//
// See file LICENSE for details of software license agreement.
//
// ----------------------------------------------------------------------------
//
// image.h
//
// Author:               Sameer Nene
// Date:                 05/23/94
// Version:              1.0
// Modification History:
// Bugs:
//
// Classes:
//   Image
//
// Notes:
//   This module contains implementation of classes declared in image.h
//   
// ----------------------------------------------------------------------------

#include <memory.h>
#include <stdlib.h>
#include "image.h"

Image::Image() : d_xsize(0), d_ysize(0), d_data_p(0)
{
}

Image::Image(int xsize, int ysize) : d_xsize(xsize), d_ysize(ysize),
d_data_p(new unsigned char[xsize * ysize])
{
}

Image::Image(const Image &i) : d_xsize(i.d_xsize), d_ysize(i.d_ysize),
d_data_p(new unsigned char[i.d_xsize * i.d_ysize])
{
  memcpy(d_data_p, i.d_data_p, d_xsize * d_ysize);
}

Image::~Image()
{
  delete[] d_data_p;
}

Image& Image::operator=(const Image &i)
{
  if(&i != this) {
    if(d_xsize != i.d_xsize || d_ysize != i.d_ysize) {
      d_xsize = i.d_xsize;
      d_ysize = i.d_ysize;
      delete[] d_data_p;
      d_data_p = new unsigned char[d_xsize * d_ysize];
    }
    memcpy(d_data_p, i.d_data_p, d_xsize * d_ysize);
  }

  return *this;
}

Image& Image::operator-=(const Image &img)
{
  int i, j;
  
  for(i = 0, j = d_xsize * d_ysize; i < j; ++i)
    d_data_p[i] = abs(d_data_p[i] - img.d_data_p[i]);
  
  return *this;
}

void Image::set(int x, int y, int d)
{
  d_data_p[d_xsize * y + x] = d;
}

Image& Image::paste(const Image &img, int x1, int y1)
{
  int i;
  unsigned char *p1, *p2;
  
  for(i = 0, p1 = &d_data_p[y1 * d_xsize + x1], p2 = img.d_data_p;
      i < img.d_ysize; ++i, p1 += d_xsize, p2 += img.d_xsize)
    memcpy(p1, p2, img.d_xsize);

  return *this;
}

Image& Image::booleanOr(const Image &img, int x1, int y1)
{
  int i, j;
  unsigned char *p1, *p2;
  
  for(i = 0, p1 = &d_data_p[y1 * d_xsize + x1], p2 = img.d_data_p;
      i < img.d_ysize; ++i, p1 += d_xsize)
    for(j = 0; j < img.d_xsize; ++j, ++p2)
      p1[j] |= *p2;

  return *this;
}

Image& Image::booleanAnd(const Image &img, int x1, int y1)
{
  int i, j;
  unsigned char *p1, *p2;
  
  for(i = 0, p1 = &d_data_p[y1 * d_xsize + x1], p2 = img.d_data_p;
      i < img.d_ysize; ++i, p1 += d_xsize)
    for(j = 0; j < img.d_xsize; ++j, ++p2)
      p1[j] &= *p2;

  return *this;
}

Image& Image::toBinary(int threshold)
{
  int i, j;

  for(i = 0, j = d_xsize * d_ysize; i < j; ++i)
    d_data_p[i] = int(d_data_p[i]) < threshold ? 0 : 0xFF;

  return *this;
}

int Image::get(int x, int y) const
{
  return d_data_p[d_xsize * y + x];
}

int Image::getXSize() const
{
  return d_xsize;
}

int Image::getYSize() const
{
  return d_ysize;
}

int Image::similarity(const Image &img) const
{
  int i, j, k;
  
  if(img.d_xsize != d_xsize || img.d_ysize != d_ysize)
    return 0;

  for(i = 0, j = d_xsize * d_ysize, k = 0; i < j; ++i)
    if(abs(d_data_p[i] - img.d_data_p[i]) > 15)
      ++k;
  
  return k;
}

Image& Image::copy(Image *v, int x1, int y1, int x2, int y2) const
{
  unsigned char *p, *l;
  int i, x = x2 - x1 + 1, y = y2 - y1 + 1;

  if(v -> d_xsize != x || v -> d_ysize != y) {
    delete[] v -> d_data_p;
    v -> d_data_p = new unsigned char[(v -> d_xsize = x) * (v -> d_ysize = y)];
  }

  for(i = 0, l = v -> d_data_p, p = &d_data_p[y1 * d_xsize + x1];
      i < y; ++i, p += d_xsize, l += x)
    memcpy(l, p, x);

  return *((Image *)this); // take care of non-const reference
}

RGBPixel::RGBPixel() : d_red(0), d_blue(0), d_green(0)
{
}

RGBPixel::RGBPixel(int red, int green, int blue) : d_red(red), d_green(green),
d_blue(blue)
{
}

RGBPixel::RGBPixel(const YUVPixel &p)
{
  int y = p.y(), u = (p.u() - 128) << 1, v = (p.v() - 128) << 1;
  int t = y + u;
  if(t < 0)
    d_red = 0;
  else if(t > 255)
    d_red = 255;
  else
    d_red = t;
  t = y - .101 * u - .297 * v;
  if(t < 0)
    d_green = 0;
  else if(t > 255)
    d_green = 255;
  else
    d_green = t;
  t = y + v;
  if(t < 0)
    d_blue = 0;
  else if(t > 255)
    d_blue = 255;
  else
    d_blue = t;
}

void RGBPixel::red(int v)
{
  
  d_red = v;
}

void RGBPixel::blue(int v)
{
  d_blue = v;
}

void RGBPixel::green(int v)
{
  d_green = v;
}

int RGBPixel::red() const
{
  return d_red;
}

int RGBPixel::green() const
{
  return d_green;
}

int RGBPixel::blue() const
{
  return d_blue;
}

YUVPixel::YUVPixel() : d_y(0), d_u(0), d_v(0)
{
}

YUVPixel::YUVPixel(int y, int u, int v) : d_y(y), d_v(v), d_u(u)
{
}

YUVPixel::YUVPixel(const RGBPixel &p)
{
  d_y = p.red() * .299 + p.green() * .587 + p.blue() * .114;
  d_u = ((p.blue() - d_y) >> 1) + 128;
  d_v = ((p.red() - d_y) >> 1) + 128;
}

void YUVPixel::y(int v)
{
  
  d_y = v;
}

void YUVPixel::u(int v)
{
  d_u = v;
}

void YUVPixel::v(int v)
{
  d_v = v;
}

int YUVPixel::y() const
{
  return d_y;
}

int YUVPixel::v() const
{
  return d_v;
}

int YUVPixel::u() const
{
  return d_u;
}

ColorImage::ColorImage() : d_xsize(0), d_ysize(0), d_data_p(0)
{
}

ColorImage::ColorImage(int xsize, int ysize) : d_xsize(xsize), d_ysize(ysize),
d_data_p(new unsigned char[xsize * ysize * 3])
{
}

ColorImage::ColorImage(const ColorImage &i) : d_xsize(i.d_xsize),
d_ysize(i.d_ysize), d_data_p(new unsigned char[i.d_xsize * i.d_ysize * 3])
{
  memcpy(d_data_p, i.d_data_p, i.d_xsize * i.d_ysize * 3);
}

ColorImage::~ColorImage()
{
  delete[] d_data_p;
}

ColorImage& ColorImage::operator=(const ColorImage &i)
{
  if(&i != this) {
    if(d_xsize != i.d_xsize || d_ysize != i.d_ysize) {
      d_xsize = i.d_xsize;
      d_ysize = i.d_ysize;
      delete[] d_data_p;
      d_data_p = new unsigned char[d_xsize * d_ysize * 3];
    }
    memcpy(d_data_p, i.d_data_p, d_xsize * d_ysize * 3);
  }

  return *this;
}

void ColorImage::set(int x, int y, int v)
{
  unsigned char *p = &d_data_p[(y * d_xsize + x) * 3];
  *(p++) = v;
  *(p++) = v;
  *p = v;
}

void ColorImage::set(int x, int y, int red, int green, int blue)
{
  unsigned char *p = &d_data_p[(y * d_xsize + x) * 3];
  *(p++) = red;
  *(p++) = green;
  *p = blue;
}

void ColorImage::set(int x, int y, const RGBPixel &v)
{
  unsigned char *p = &d_data_p[(y * d_xsize + x) * 3];
  *(p++) = v.red();
  *(p++) = v.green();
  *p = v.blue();
}

ColorImage& ColorImage::paste(const ColorImage &img, int x1, int y1)
{
  int i;
  unsigned char *p1, *p2;
  
  for(i = 0, p1 = &d_data_p[(y1 * d_xsize + x1) * 3], p2 = img.d_data_p;
      i < img.d_ysize; ++i, p1 += d_xsize * 3, p2 += img.d_xsize * 3)
    memcpy(p1, p2, img.d_xsize * 3);

  return *this;
}

void ColorImage::get(int x, int y, int *red, int *green, int *blue) const
{
  unsigned char *p = &d_data_p[(y * d_xsize + x) * 3];
  *red = *(p++);
  *green = *(p++);
  *blue = *p;
}

RGBPixel ColorImage::get(int x, int y) const
{
  RGBPixel pix;
  unsigned char *p = &d_data_p[(y * d_xsize + x) * 3];
  pix.red(*(p++));
  pix.green(*(p++));
  pix.blue(*p);
  return pix;
}

int ColorImage::getXSize() const
{
  return d_xsize;
}

int ColorImage::getYSize() const
{
  return d_ysize;
}

YUVImage::YUVImage() : d_xsize(0), d_ysize(0), d_ydata_p(0), d_udata_p(0),
d_vdata_p(0)
{
}

YUVImage::YUVImage(int xsize, int ysize) : d_xsize(xsize), d_ysize(ysize),
d_ydata_p(new unsigned char[xsize * ysize]),
d_udata_p(new unsigned char[xsize * ysize]),
d_vdata_p(new unsigned char[xsize * ysize])
{
}

YUVImage::YUVImage(const YUVImage &i) : d_xsize(i.d_xsize), d_ysize(i.d_ysize),
d_ydata_p(new unsigned char[i.d_xsize * i.d_ysize]),
d_udata_p(new unsigned char[i.d_xsize * i.d_ysize]),
d_vdata_p(new unsigned char[i.d_xsize * i.d_ysize])
{
  memcpy(d_ydata_p, i.d_ydata_p, i.d_xsize * i.d_ysize);
  memcpy(d_udata_p, i.d_udata_p, i.d_xsize * i.d_ysize);
  memcpy(d_vdata_p, i.d_vdata_p, i.d_xsize * i.d_ysize);
}

YUVImage::~YUVImage()
{
  delete[] d_ydata_p;
  delete[] d_udata_p;
  delete[] d_vdata_p;
}

YUVImage& YUVImage::operator=(const YUVImage &i)
{
  if(&i != this) {
    if(d_xsize != i.d_xsize || d_ysize != i.d_ysize) {
      d_xsize = i.d_xsize;
      d_ysize = i.d_ysize;
      delete[] d_ydata_p;
      delete[] d_udata_p;
      delete[] d_vdata_p;
      d_ydata_p = new unsigned char[d_xsize * d_ysize];
      d_udata_p = new unsigned char[d_xsize * d_ysize];
      d_vdata_p = new unsigned char[d_xsize * d_ysize];
    }
    memcpy(d_ydata_p, i.d_ydata_p, d_xsize * d_ysize);
    memcpy(d_udata_p, i.d_udata_p, d_xsize * d_ysize);
    memcpy(d_vdata_p, i.d_vdata_p, d_xsize * d_ysize);
  }

  return *this;
}

void YUVImage::set(int x, int y, int v)
{
  d_ydata_p[y * d_xsize + x] = v;
  d_udata_p[y * d_xsize + x] = 0;
  d_vdata_p[y * d_xsize + x] = 0;
}

void YUVImage::set(int x, int y, int Y, int U, int V)
{
  d_ydata_p[y * d_xsize + x] = Y;
  d_udata_p[y * d_xsize + x] = U;
  d_vdata_p[y * d_xsize + x] = V;
}

void YUVImage::set(int x, int y, const YUVPixel &v)
{
  d_ydata_p[y * d_xsize + x] = v.y();
  d_udata_p[y * d_xsize + x] = v.u();
  d_vdata_p[y * d_xsize + x] = v.v();
}

void YUVImage::get(int x, int y, int *Y, int *U, int *V) const
{
  *Y = d_ydata_p[y * d_xsize + x];
  *U = d_udata_p[y * d_xsize + x];
  *V = d_vdata_p[y * d_xsize + x];
}

YUVPixel YUVImage::get(int x, int y) const
{
  YUVPixel pix;
  pix.y(d_ydata_p[y * d_xsize + x]);
  pix.u(d_udata_p[y * d_xsize + x]);
  pix.v(d_vdata_p[y * d_xsize + x]);
  return pix;
}

int YUVImage::getXSize() const
{
  return d_xsize;
}

int YUVImage::getYSize() const
{
  return d_ysize;
}
