The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "imgGlyph.h"

imgGlyph::imgGlyph()
{
  _touched = 0;
  _glyphPtr = NULL;
  _width = _height = 0;
  _curX = _curY = 0;

  _error = 0;
  _errno = 0;
}

imgGlyph::~imgGlyph()
{
  deleteGlyphPtr();
  if (_error > 0) {
    delete _error;
  }
}

void
imgGlyph::setError(const char *str, short errno)
{
  if (_error > 0) {
    printf("WARNING: imgGlyph::setError() called twice (%s,%s)\n",_error,str);
    delete _error;
  }
  _error = new char[strlen(str)+1];
  strcpy(_error,str);
  _errno = errno;
}

void
imgGlyph::printError()
{
  printf("%s\n",getErrorString());
}

char *
imgGlyph::getErrorString()
{
  static char str[2048];
  sprintf(str,"imgArray: %.2007s (%d)",_error, _errno);
  return str;
}

short
imgGlyph::getStatus()
{
  return _errno;
}

int
imgGlyph::getWidth()
{
  checkTouched();
  return _width;
}

int
imgGlyph::getHeight()
{
  checkTouched();
  return _height;
}

int
imgGlyph::getCurX()
{
  checkTouched();
  return _curX;
}

int
imgGlyph::getCurY()
{
  checkTouched();
  return _curY;
}

short
imgGlyph::addGlyph(Glyph *glyph)
{
  if (glyph == NULL) {
    setError("No glyh to add (NULL)",2);
    return -1;
  }

  _glyphList.push_back(glyph);

  _touched = 1;

  return 1;
}

imgProcess *
imgGlyph::createImg()
{
  checkTouched();// update BBX

  int count = _glyphList.size();

  if (count == 0) {
    setError("No glyph to create image",3);
    return NULL;
  }
  if (_width <= 0 || _height <= 0) {
    setError("Width or Height equal zero",4);
    return NULL;
  }

  imgProcess *tmpImage = new imgProcess(_width, _height, 4);
  
  if (tmpImage->getStatus() != ilOKAY) {
    setError(tmpImage->getErrorString(),5);
    delete tmpImage;
    return NULL;
  }

  int destX, destY;
  int cur_x = _curX;
  int cur_y = _curY;
  int lastX=0;

  for (int i =0; i < count; i++) {
    int offX = 0;

    destX = _glyphPtr[i]->getBBXXO() + cur_x;
    destY = _height - (_glyphPtr[i]->getBBXYO() + cur_y + _glyphPtr[i]->getBBXH());

    if (lastX < destX) offX = destX - lastX;

    for (int line=0; line < _height; line++) {

      int Y = destY + line;

      for (int bits= -offX; bits < _glyphPtr[i]->getBBXW(); bits++) {

	int X = destX + bits;

	if (line >= _glyphPtr[i]->getBBXH() ||
	    bits < 0) {
	  //get rid of memory thrash
	  tmpImage->setPixel(X, Y, 
			     _glyphPtr[i]->getBackgroundR(),
			     _glyphPtr[i]->getBackgroundG(),
			     _glyphPtr[i]->getBackgroundB(),
			     _glyphPtr[i]->getBackgroundA());
	} else {
	  int srcBit, srcByte;
	  srcByte  = bits >> 3;
	  srcBit   = 7 - (bits - (srcByte * 8));
	  
	  const char *bitmap = _glyphPtr[i]->getBitmap();
	  if (bitmap[line* _glyphPtr[i]->getStride() + srcByte] &(1<<srcBit)) {
	    tmpImage->setPixel(X, Y, 
			       _glyphPtr[i]->getForegroundR(),
			       _glyphPtr[i]->getForegroundG(),
			       _glyphPtr[i]->getForegroundB(),
			       _glyphPtr[i]->getForegroundA());
	  } else {
	    tmpImage->setPixel(X, Y, 
			       _glyphPtr[i]->getBackgroundR(),
			       _glyphPtr[i]->getBackgroundG(),
			       _glyphPtr[i]->getBackgroundB(),
			       _glyphPtr[i]->getBackgroundA());
	  }
	  
	}
      }
      
    }

    cur_x += _glyphPtr[i]->getOffsetToNextX();
    cur_y += _glyphPtr[i]->getOffsetToNextY();
    lastX = destX + _glyphPtr[i]->getBBXW();
  }
  if (tmpImage->getStatus() != ilOKAY) {
    setError(tmpImage->getErrorString(),6);
    delete tmpImage;
    return NULL;
  }
  return tmpImage;
}

short
imgGlyph::blittInImage(imgProcess *image, int x, int y)
{
  checkTouched(); // update BBX
  int count = _glyphList.size();

  if (count == 0) {
    setError("No glyph to blitt",8);
    return -1;
  }
  
  if (x >= image->getWidth() ||
      y >= image->getHeight() ||
      x+ _width < 0 ||
      y+ _height < 0) {
    
    setError("Blitting position is not in image",9);
    return -1;
  }

  int destX, destY;
  int drawFore, drawBack;

  int cur_x = _curX;
  int cur_y = _curY;

  for (int i =0; i < count; i++) {

    if (_glyphPtr[i]->getForegroundA() == 255) {
      drawFore = 0;
    } else {
      drawFore = 1;      
    }
    if (_glyphPtr[i]->getBackgroundA() == 255) {
      drawBack = 0;
    } else {
      drawBack = 1;      
    }
    
    destX = _glyphPtr[i]->getBBXXO() + cur_x + x;
    destY = _height - (_glyphPtr[i]->getBBXYO() + cur_y + _glyphPtr[i]->getBBXH()) + y;
    
    for (int line=0; line < _glyphPtr[i]->getBBXH(); line++) {

      int Y = destY + line;
      if (Y < 0 || Y >= image->getHeight()) continue;

      for (int bits=0; bits < _glyphPtr[i]->getBBXW(); bits++) {

	int X = destX + bits;
	if (X < 0 || X >= image->getWidth()) continue;

	int srcBit, srcByte;
	srcByte  = bits >> 3;
	srcBit   = 7 - (bits - (srcByte * 8));
       
	const char *bitmap = _glyphPtr[i]->getBitmap();
	if (bitmap[ line * _glyphPtr[i]->getStride() + srcByte] &(1<<srcBit)) {
	  if (drawFore)
	    if (image->setPixel(X, Y, 
				_glyphPtr[i]->getForegroundR(),
				_glyphPtr[i]->getForegroundG(),
				_glyphPtr[i]->getForegroundB(),
				_glyphPtr[i]->getForegroundA()) == -1) {
	      setError("set pixel foreground failed",10);
	      return -1;
	    }
	} else {
	   if (drawBack)
	    if (image->setPixel(X, Y, 
				_glyphPtr[i]->getBackgroundR(),
				_glyphPtr[i]->getBackgroundG(),
				_glyphPtr[i]->getBackgroundB(),
				_glyphPtr[i]->getBackgroundA()) == -1) {
	      setError("set pixel background failed",11);
	      return -1;
	    }
	}
	
      }
      
    }

    cur_x += _glyphPtr[i]->getOffsetToNextX();
    cur_y += _glyphPtr[i]->getOffsetToNextY();
  }

  return 1;
}

void
imgGlyph::checkTouched()
{
  int count = _glyphList.size();
  if (!_touched || !count) return;

  int xmin, xmax;
  int ymin, ymax;

  _curX = _curY = 0;

  deleteGlyphPtr();

  _glyphPtr = new Glyph*[count];

  for (int i=0; i < count; i++) {
    _glyphPtr[i] = _glyphList[i];
  }

  Glyph::calcBBX(_glyphPtr, count, _curX, _curY, xmax, ymax, xmin, ymin);

  // initialize new glyph
  _width = xmax - xmin;
  _height = ymax - ymin;

  _touched = 0;
}

void
imgGlyph::deleteGlyphPtr()
{
  if (_glyphPtr == NULL) return;

  delete[] _glyphPtr;

  _glyphPtr = NULL;
}