/*
 * Image.cpp
 *
 * (c) 2002 by Thorsten Scheuermann <tscheuermann@gmx.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.
 */

#include "Image.h"
#include "Display.h"
#include <string.h>
#include <qdirectpainter_qws.h>
#include <stdio.h>

#define RED_SHIFT_FIXED			18
#define GREEN_SHIFT_FIXED		17
#define BLUE_SHIFT_FIXED		18
#define RED_SHIFT_CHAR			3
#define GREEN_SHIFT_CHAR		2
#define BLUE_SHIFT_CHAR			3
#define RED_OFFSET					11
#define GREEN_OFFSET				5
#define BLUE_OFFSET 				0

Image::Image()
: imgData(NULL)
{
	w = 0;
	h = 0;
	rowSize = 0;
}

Image::Image(int width, int height)
: imgData(NULL)
{
	w = width;
	h = height;
	rowSize = width * sizeof(unsigned short);

	imgData = new unsigned short[w*h];
	memset(imgData, 0, h * rowSize);
}

Image::~Image()
{
	if(imgData)
		delete[] imgData;
}

void Image::setImageDimension(int w, int h)
{
	newWidth = w;
	newHeight = h;
}

void Image::setNumImageChannels(int c)
{
	// make sure c=3
}

void Image::startImage()
{
	if((newWidth != w) || (newHeight != h))
	{
		if(imgData)
			delete[] imgData;
		w = newWidth;
		h = newHeight;
		rowSize = w * sizeof(unsigned short);
		imgData = new unsigned short[w*h];
	}
	memset(imgData, 0, h * rowSize);
	rowCounter = 0;
	columnCounter = 0;
}

void Image::endImage()
{
}

void Image::imageRow(unsigned char *rowData)
{
  imageRow(rowCounter, rowData);
  rowCounter++;
}

void Image::imageRow(fixed *rowData)
{
  imageRow(rowCounter, rowData);
  rowCounter++;
}

void Image::imageRow(int row, unsigned char *rowData)
{
	int i;
	unsigned short *p;

	if((row < 0) || (row >= h))
		return;

	p = imgData + w*row;

	for(i=0; i<w; i++)
	{
 		*p = (*rowData >> RED_SHIFT_CHAR) << RED_OFFSET;
		rowData++;
		*p |= (*rowData >> GREEN_SHIFT_CHAR) << GREEN_OFFSET;
		rowData++;
		*p |= (*rowData >> BLUE_SHIFT_CHAR);
		rowData++;
		p++;
	}
}

void Image::imageRow(int row, fixed *rowData)
{
	int i;
	unsigned short *p;

	if((row < 0) || (row >= h))
		return;

	p = imgData + w*row;

	for(i=0; i<w; i++)
	{
		*p = (rowData->fixedPoint() >> RED_SHIFT_FIXED) << RED_OFFSET;
		rowData++;
		*p |= (rowData->fixedPoint() >> GREEN_SHIFT_FIXED) << GREEN_OFFSET;
		rowData++;
		*p |= (rowData->fixedPoint() >> BLUE_SHIFT_FIXED);
		rowData++;
		p++;
	}
}

void Image::imageRowReverse(int row, unsigned char *rowData)
{
	int i;
	unsigned short *p;

	if((row < 0) || (row >= h))
		return;

	p = imgData + w*(row+1) - 1;

	for(i=0; i<w; i++)
	{
 		*p = (*rowData >> RED_SHIFT_CHAR) << RED_OFFSET;
		rowData++;
		*p |= (*rowData >> GREEN_SHIFT_CHAR) << GREEN_OFFSET;
		rowData++;
		*p |= (*rowData >> BLUE_SHIFT_CHAR);
		rowData++;
		p--;
	}
}

void Image::imageRowReverse(int row, fixed *rowData)
{
	int i;
	unsigned short *p;

	if((row < 0) || (row >= h))
		return;

	p = imgData + w*(row+1) - 1;

	for(i=0; i<w; i++)
	{
		*p = (rowData->fixedPoint() >> RED_SHIFT_FIXED) << RED_OFFSET;
		rowData++;
		*p |= (rowData->fixedPoint() >> GREEN_SHIFT_FIXED) << GREEN_OFFSET;
		rowData++;
		*p |= (rowData->fixedPoint() >> BLUE_SHIFT_FIXED);
		rowData++;
		p--;
	}
}

void Image::imageColumn(int col, unsigned char *colData)
{
	int i;
	unsigned short *p;

  if((col < 0) || (col >= w))
    return;

	p = imgData + col;
	for(i=0; i<h; i++)
	{
 		*p = (*colData >> RED_SHIFT_CHAR) << RED_OFFSET;
		colData++;
		*p |= (*colData >> GREEN_SHIFT_CHAR) << GREEN_OFFSET;
		colData++;
		*p |= (*colData >> BLUE_SHIFT_CHAR);
		colData++;
		p += w;
	}
}

void Image::imageColumn(int col, fixed *colData)
{
	int i;
	unsigned short *p;

  if((col < 0) || (col >= w))
    return;

  p = imgData + col;
	for(i=0; i<h; i++)
	{
 		*p = (colData->fixedPoint() >> RED_SHIFT_FIXED) << RED_OFFSET;
		colData++;
		*p |= (colData->fixedPoint() >> GREEN_SHIFT_FIXED) << GREEN_OFFSET;
		colData++;
		*p |= (colData->fixedPoint() >> BLUE_SHIFT_FIXED);
		colData++;
		p += w;
	}
}

void Image::imageColumnReverse(int col, unsigned char *colData)
{
	int i;
	unsigned short *p;

  if((col < 0) || (col >= w))
    return;

	p = imgData + w*(h-1) + col;
	for(i=0; i<h; i++)
	{
 		*p = (*colData >> RED_SHIFT_CHAR) << RED_OFFSET;
		colData++;
		*p |= (*colData >> GREEN_SHIFT_CHAR) << GREEN_OFFSET;
		colData++;
		*p |= (*colData >> BLUE_SHIFT_CHAR);
		colData++;
		p -= w;
	}
}

void Image::imageColumnReverse(int col, fixed *colData)
{
	int i;
	unsigned short *p;

  if((col < 0) || (col >= w))
    return;

	p = imgData + w*(h-1) + col;
	for(i=0; i<h; i++)
	{
 		*p = (colData->fixedPoint() >> RED_SHIFT_FIXED) << RED_OFFSET;
		colData++;
		*p |= (colData->fixedPoint() >> GREEN_SHIFT_FIXED) << GREEN_OFFSET;
		colData++;
		*p |= (colData->fixedPoint() >> BLUE_SHIFT_FIXED);
		colData++;
		p -= w;
	}
}


void Image::paint(QDirectPainter *painter, int screenXOff, int screenYOff,
                  int imgXOff, int imgYOff)
{
  int y;
  unsigned char *dest = painter->frameBuffer();
  unsigned short *src = imgData;
  int drawWidth;              // the width of the part of the image being drawn
  int drawHeight;
  int drawRowSize;
  int temp;

  fprintf(stderr, "screenXOff: %d\nscreenYOff: %d\nimgXOff: %d\nimgYOff: %d\n",
          screenXOff, screenYOff, imgXOff, imgYOff);
  printf("lineStep() = %d\n", painter->lineStep());
  printf("transformOrientation() = %d\n", painter->transformOrientation());
  printf("depth() = %d\n", painter->depth());
  printf("width() = %d\n", painter->width());
  printf("height() = %d\n", painter->height());
  printf("xOffset() %d\n", painter->xOffset());
  printf("yOffset() %d\n", painter->yOffset());

  switch(painter->transformOrientation())
  {
    case 0:   // ROTATION_NONE
    case 2:   // ROTATION 180
      if(imgXOff >= w)
        return;
      if(imgYOff >= h)
        return;
      break;
    case 1:   // ROTATION_90
    case 3:   // ROTATION_270
      if(imgXOff >= h)
        return;
      if(imgYOff >= w)
        return;
      break;
    default:
      break;
  }

  // determine visible image size
  switch(painter->transformOrientation())
  {
    case 0:   // ROTATION_NONE
    case 2:   // ROTATION 180
      drawWidth = Display::width()-screenXOff;
      temp = w-imgXOff;
      if(temp < 0)
        return;
      drawWidth = (temp < drawWidth) ? temp : drawWidth;

      drawHeight = Display::height()-screenYOff;
      temp = h-imgYOff;
      if(temp < 0)
        return;
      drawHeight = (temp < drawHeight) ? temp : drawHeight;
      drawRowSize = sizeof(unsigned short) * drawWidth;
      break;
    case 1:   // ROTATION_90
    case 3:   // ROTATION_270
      drawWidth = Display::height()-screenYOff;
      temp = w-imgYOff;
      if(temp < 0)
        return;
      drawWidth = (temp < drawWidth) ? temp : drawWidth;

      drawHeight = Display::width()-screenXOff;
      temp = h-imgXOff;
      if(temp < 0)
        return;
      drawHeight = (temp < drawHeight) ? temp : drawHeight;
      drawRowSize = sizeof(unsigned short) * drawWidth;
      break;
    default:
      break;
  }

  // get pointer to the drawing start position
  switch(painter->transformOrientation())
  {
    case 0:   // ROTATION_NONE
      dest += (painter->xOffset() + screenXOff) * painter->depth()/8;
      dest += (painter->yOffset() + screenYOff) * painter->lineStep();
      break;
    case 1:   // ROTATION_90
      dest += screenYOff * painter->depth()/8;
      dest += (Display::width()-drawHeight-screenXOff) * painter->lineStep();
      break;
    case 2:   // ROTATION 180
      dest += (Display::height()-drawHeight-screenYOff) * painter->lineStep();
      dest += (Display::width()-drawWidth-screenXOff) * painter->depth()/8;
      break;
    case 3:   // ROTATION_270
      dest += screenXOff * painter->lineStep();
      dest += (Display::height() - painter->height()) *
              painter->depth()/8;
      // compute the proper offset for screenYOff
      dest += (painter->height() - screenYOff - drawWidth) * painter->depth()/8;
      break;
    default:
      break;
  }

  // get pointer to upper left corner of image data
  switch(painter->transformOrientation())
  {
    case 0:   // ROTATION_NONE
    case 2:   // ROTATION 180
      // needs fixing for 180???
      src += imgXOff * sizeof(unsigned short) + imgYOff * w;
      break;
    case 1:   // ROTATION_90
    case 3:   // ROTATION_270
      // needs fixing for 270???
      src += imgYOff * sizeof(unsigned short) + imgXOff * w;
      break;
    default:
      break;
  }

  // copy image data to frame buffer
  for(y=0; y<drawHeight; y++)
  {
    memcpy(dest, src, drawRowSize);
    dest += painter->lineStep();
    src += w;
  }
}
