The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#include <string.h>

#include <il/ilAddImg.h>
#include <il/ilFileImg.h>
#include <il/ilGBlurImg.h>
#include <il/ilBlendImg.h>
#include <il/ilMemoryImg.h>
#include <il/ilSharpenImg.h>
#include <il/ilCompassImg.h>
#include <il/ilRotZoomImg.h>
#include <il/ilLaplaceImg.h>
#include <il/ilRobertsImg.h>

#include "imgProcess.h"
#include "smpVideo.h"

imgProcess::imgProcess()
{
  _client = 0;
  _image       = NULL;
  _imageFormat = NULL;
  _errno       = ilOKAY;
  _error[0]    = '\0';
}

imgProcess::imgProcess(int width, int height, short channels)
{
  iflSize size(width,height,channels);

  _image = new ilMemoryImg(size,iflUChar);
  _image->setOrientation(iflUpperLeftOrigin);
  switch (channels) {
  case 1:_image->setColorModel(iflRGBPalette);break;
  case 3:_image->setColorModel(iflRGB);break;
  case 4:_image->setColorModel(iflRGBA);break;
  default:delete _image;
    _image = NULL;
    setError((ilStatus)ilABORTED, "Channel count not allowed");    
  }

  _imageFormat = NULL;
  _errno       = ilOKAY;
  _error[0]    = '\0';
}

imgProcess::imgProcess(imgProcess *image)
{
  common_init(image->_image);
}


imgProcess::imgProcess(ilImage *image)
{
  common_init(image);
}

void
imgProcess::common_init(ilImage *image)
{
  _imageFormat = NULL;
  _errno       = ilOKAY;
  _error[0]    = '\0';

  if (image == NULL) {
    _image = NULL;
    setError((ilStatus)ilABORTED, "No imagedata");
    return;
  }

  iflSize size;
  iflOrder order = image->getOrder();
  iflDataType dataType = image->getDataType();
  image->getSize(size);

  ilMemoryImg *img = new ilMemoryImg(size,dataType,order);
  img->setOrientation(iflUpperLeftOrigin);

  img->copy(image);
  _image = img;

}

imgProcess::~imgProcess()
{
  if (_client) return ;//damn il bug, dont delete my shared arena
  if (_image > 0) {
    delete _image;
    _image = NULL;
  }  
  deleteQue();
}

void
imgProcess::deleteQue()
{
  while (_que.begin() != _que.end()) {
    ilImage *image = _que.back();

    delete image;

    _que.pop_back();
  }
}

void 
imgProcess::setError(ilStatus status)
{
  ilStatusToString(status, _error, ERRORSTR_LENGTH);
  _errno = status;
}

void 
imgProcess::setError(iflStatus status)
{
  setError(ilStatusFromIflStatus(status));
}

void 
imgProcess::setError(ilStatus status, const char *str)
{
  _errno = status;
  strncpy(_error, str, ERRORSTR_LENGTH);
}

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

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

ilStatus
imgProcess::getStatus()
{
  return _errno;
}

int 
imgProcess::getWidth()
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for getX");
    return -1;
  }
  return _image->getXsize();
}

int 
imgProcess::getHeight()
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for getY");
    return -1;
  }
  return _image->getYsize();
}

int 
imgProcess::getChannels()
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for getC");
    return -1;
  }
  return _image->getCsize();
}

const char *
imgProcess::getImageFormatName()
{
  return _imageFormat;
}

short 
imgProcess::load(int fd)
{
  return (load( fd, NULL));
}

short 
imgProcess::load(const char *filename)
{
  return (load( -1, filename));
}

short 
imgProcess::load(int fd, const char *filename)
{
  ilFileImg *file = new ilFileImg(fd, filename, O_RDONLY);

  if (file == NULL || file->getStatus() != ilOKAY) {
    setError(file->getStatus());
    delete file;
    return -1;
  }
  
  if (_image > 0) {
    delete _image;
  }
  deleteQue();

  _imageFormat = (char *)file->getImageFormatName();
  _image = file;

  return 1;
}

short 
imgProcess::save(int fd, const char *format)
{
  return (save(fd, NULL, format));
}

short 
imgProcess::save(const char *filename, const char *format)
{
  return (save( -1, filename, format));
}

short 
imgProcess::save(int fd, const char *filename, const char *format)
{
  ilStatus error;
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata to save");
    return -1;
  }

  iflFormat* ilformat = iflFormat::findByFormatName(format);
  if (! format) {
    setError((ilStatus)ilABORTED, "iflFormat not found");
    return -1;
  }
  
  // hurra, hurra, there is a il bug, can not save a jpg from a (A)BGR model
  if ((_image->getColorModel() == iflABGR ||
      _image->getColorModel() == iflBGR) &&
      !strcmp(format, "JFIF")) {

    ilImage *img = copyImg(_image, iflRGBA);

    if (img == NULL) {
      setError((ilStatus)ilABORTED, "cant copy img for JFIF bug");
      return -1;
    }
    //damn an other il bug, dont delete my shared arena
    if (!_client) {
      delete _image;
      deleteQue();
    }
    _image = img;
  }

  ilFileImg *file;

  if (_image->getColorModel() != iflRGBA &&
      _image->getColorModel() != iflRGB &&
      !strcmp(format, "TIFF")) {
    // TIFF standard color model should me RGB(A)
    iflColorModel clMdl;
    iflFileConfig *cfg = new iflFileConfig();

    switch (_image->getColorModel()) {
    case iflCMYK:
    case iflABGR:clMdl=iflRGBA;break;
    default:clMdl=iflRGB;
    }
    cfg->setColorModel(clMdl);
    file = new ilFileImg(fd, filename, _image, cfg, ilformat);
    delete cfg;
  } else {
    file = new ilFileImg(fd, filename, _image, NULL, ilformat);
  }

  if (file <= 0 || file->getStatus() != ilOKAY) {
    setError(file->getStatus());
    delete file;
    return -1;
  }

  file->copy(_image);
  //file->copyTileCfg(0,0,0, _image->getXsize(), _image->getYsize(), 1, _image, 0,0,0);

  if (file->getStatus() != ilOKAY) {
    setError(file->getStatus());
    delete file;
    return -1;
  }

  error = file->flush();
  if (error != iflOKAY) {
    setError(error);
    delete file;
    return -1;
  }

  //_imageFormat = (char *)file->getImageFormatName();
  delete file;

  return 1;
}

short 
imgProcess::copyTile(int destX, int destY, int width, int height, imgProcess *srcImage, int srcX, int srcY)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No dst imagedata to copy");
    return -1;
  }
  if (srcImage->_image <= 0) {
    setError((ilStatus)ilABORTED, "No src imagedata to copy");
    return -1;
  }

  ilStatus status =_image->copyTile(destX, destY, width, height, srcImage->_image, srcX, srcY, NULL);

  if (status != ilOKAY) {
    setError(status);
    return -1;
  }

  return 1;
}

short
imgProcess::setPixel(int x, int y, char r, char g, char b, char a)
{
  char map[4];
  int count;

  if (_image->isWritable() != TRUE) {
    ilImage *tmpImage = copyImg(_image, _image->getColorModel());

    if (tmpImage == NULL) {
      setError((ilStatus)ilABORTED, "Cant copy image, have to coz image is not writable");
      return -1;
    }
    delete _image;
    deleteQue();
    _image = tmpImage;
    _errno = ilOKAY;
  }

  if (_image->getColorModel() == iflRGB) {
    map[0] = r;
    map[1] = g;
    map[2] = b;
    count = 3;
  } else if (_image->getColorModel() == iflRGBA) {
    map[0] = r;
    map[1] = g;
    map[2] = b;
    map[3] = a;
    count = 4;
  } else if (_image->getColorModel() == iflBGR) {
    map[0] = b;
    map[1] = g;
    map[2] = r;
    count = 3;
  } else if (_image->getColorModel() == iflABGR) {
    map[0] = a;
    map[1] = b;
    map[2] = g;
    map[3] = r;
    count = 4;
  } else {
    // todo
    map[0] = r;
    map[1] = g;
    map[2] = b;
    //map[3] = a;
    count = 3;
    //puts("ELSE");
  }

  iflPixel pixel(iflChar, count, map);
  ilStatus status = _image->setPixel(x, y, pixel);
  if (status != ilOKAY) {
    setError(status);
    return -1;
  }
  return 1;
}

short 
imgProcess::getVideoSnapshot()
{

  char *dataPtr;
  int xsize, ysize;
  char errorString[ERRORSTR_LENGTH];
  smpVideo *vid = new smpVideo;

  if ((vid->openVideo()) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((vid->setVideoInPath()) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((vid->createBuffer(1)) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((vid->beginTransfer()) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((vid->waitTransferDone()) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((vid->getVideoSize(&xsize, &ysize)) < 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  if ((dataPtr = (char *)vid->getFramePointer(1)) <= 0) {
    vid->getErrorStrFmt(errorString);
    setError((ilStatus)ilABORTED, errorString);
    delete vid;
    return -1;
  }

  iflSize imgSize(xsize, ysize, 1, 4);

  ilMemoryImg *tmpImage = new ilMemoryImg(NULL, imgSize, iflUChar);
  tmpImage->setColorModel(iflABGR);
  tmpImage->setOrientation(iflUpperLeftOrigin);

  if (tmpImage->getStatus() != ilOKAY) {
    setError(tmpImage->getStatus());
    delete tmpImage;
    delete vid;
    return -1;
  }

  tmpImage->setTile(0,0,xsize,ysize,dataPtr);

  if (_image > 0) {
    delete _image;
  }
  deleteQue();

  _image = tmpImage;
  _imageFormat = "RGB";

  delete vid;

  return 1;
}

short 
imgProcess::rotateZoom(float angle, float zoomX, float zoomY, unsigned short resampleType)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata to rotate/zoom");
    return -1;
  }

  ilRotZoomImg *rotateZoom = new ilRotZoomImg(_image, angle, zoomX, zoomY, (ilResampType)resampleType);

  if (rotateZoom->getStatus() != ilOKAY) {
    setError(rotateZoom->getStatus());
    delete rotateZoom;

    return -1;
  }

  _que.push_back(_image);
  _image = rotateZoom;

  return 1;
}

short 
imgProcess::blur(float blur, int width, int height, double biasValue, unsigned short edgeMode)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for blur");
    return -1;
  }

  ilGBlurImg *blurImg = new ilGBlurImg(_image, blur, width, height, 
				       biasValue, (ilEdgeMode)edgeMode);
  if (blurImg->getStatus() != ilOKAY) {
    setError(blurImg->getStatus());
    delete blurImg;

    return -1;
  }
  _que.push_back(_image);
  _image = blurImg;

  return 1;
}

short 
imgProcess::sharp(float sharpness, float radius, unsigned short edgeMode)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata to draw sharp");
    return -1;
  }

  ilSharpenImg *sharpImg = new ilSharpenImg(_image, sharpness, radius, 
					    (ilEdgeMode)edgeMode);
  if (sharpImg->getStatus() != ilOKAY) {
    setError(sharpImg->getStatus());
    delete sharpImg;

    return -1;
  }

  _que.push_back(_image);
  _image = sharpImg;

  return 1;
}

short 
imgProcess::compass(float radius, double biasVal, int kernSize, unsigned short edgeMode)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for compass");
    return -1;
  }

  ilCompassImg *compassImg = new ilCompassImg(_image, radius, 
					      biasVal, kernSize, (ilEdgeMode)edgeMode);
  if (compassImg->getStatus() != ilOKAY) {
    setError(compassImg->getStatus());
    delete compassImg;

    return -1;
  }

  _que.push_back(_image);
  _image = compassImg;

  return 1;
}

short 
imgProcess::laplace(double biasVal, unsigned short edgeMode, int kernSize)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for laplace");
    return -1;
  }

  ilLaplaceImg *laplaceImg = new ilLaplaceImg(_image,
					      biasVal, (ilEdgeMode)edgeMode, kernSize);
  if (laplaceImg->getStatus() != ilOKAY) {
    setError(laplaceImg->getStatus());
    delete laplaceImg;

    return -1;
  }

  _que.push_back(_image);
  _image = laplaceImg;

  return 1;
}

short 
imgProcess::edgeDetection(double biasVal, unsigned short edgeMode)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No imagedata for edge detection");
    return -1;
  }

  ilRobertsImg *edgeDetectionImg = new ilRobertsImg(_image,
					      biasVal, (ilEdgeMode)edgeMode);
  if (edgeDetectionImg->getStatus() != ilOKAY) {
    setError(edgeDetectionImg->getStatus());
    delete edgeDetectionImg;

    return -1;
  }

  _que.push_back(_image);
  _image = edgeDetectionImg;

  return 1;
}

short 
imgProcess::blendImg(imgProcess *dstimg, float alpha)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No src imagedata for blending images");
    return -1;
  }

  if (dstimg->_image <= 0) {
    setError((ilStatus)ilABORTED, "No dst imagedata for blending images");
    return -1;
  }
  ilBlendImg *blendImg = new ilBlendImg(_image, dstimg->_image, alpha);

  if (blendImg->getStatus() != ilOKAY) {
    setError(blendImg->getStatus());
    delete blendImg;

    return -1;
  }

  _que.push_back(_image);
  _image = blendImg;
  
  return 1;
}

short 
imgProcess::blendImg(imgProcess *image, imgProcess *alphaimg, ilCompose compose)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No src imagedata for blending images");
    return -1;
  }

  if (image->_image <= 0) {
    setError((ilStatus)ilABORTED, "No dst imagedata for blending images");
    return -1;
  }

  if (alphaimg->_image <= 0) {
    setError((ilStatus)ilABORTED, "No dst imagedata for blending images");
    return -1;
  }

  ilImage *useAlpha = alphaimg->_image;

  // convert an image with only one channel to a 4 channel image
  if (alphaimg->_image->getCsize() == 1) {

    ilImage *tmpImage = copyImg(alphaimg->_image);
    if (tmpImage == NULL) {
      return -1;
    }
    // remember the img
    useAlpha = tmpImage;
    _que.push_back(tmpImage);
  }

  ilBlendImg *blendImg = new ilBlendImg(_image, image->_image, useAlpha, NULL, compose);

  if (blendImg->getStatus() != ilOKAY) {
    setError(blendImg->getStatus());
    delete blendImg;
    delete _que.back();
    _que.pop_back();
    return -1;
  }

  _que.push_back(_image);
  _image = blendImg;
  
  return 1;
}

short 
imgProcess::add(imgProcess *addImg, double bias)
{
  if (_image <= 0) {
    setError((ilStatus)ilABORTED, "No src imagedata for adding images");
    return -1;
  }

  if (addImg->_image <= 0) {
    setError((ilStatus)ilABORTED, "No addImg imagedata for adding images");
    return -1;
  }

  ilImage *addImgCopy = copyImg(addImg->_image, addImg->_image->getColorModel());
  if (addImgCopy == NULL) {
    return -1;
  }

  ilAddImg *addi = new ilAddImg(_image, addImgCopy, bias);
				
  if (addi->getStatus() != ilOKAY) {
    setError(addi->getStatus());
    delete addi;
    delete addImgCopy;

    return -1;
  }

  _que.push_back(_image);
  _que.push_back(addImgCopy);
  _image = addi;

  return 1;  
}

ilImage *
imgProcess::copyImg(ilImage *src, iflColorModel colorModel)
{
  int channels;
  switch (colorModel) {
  case iflCMYK:
  case iflRGBA:
  case iflABGR:channels=4;break;
  case iflRGBPalette:
    // damn an other bug, cant copy a RGBPalette pic
    //channels=1;
    channels=3;
    colorModel=iflRGB;
    break;
  case iflRGB:
  case iflBGR:channels=3;break;
  default:channels=3;puts("WARNING: unsupported color model\n");
  }

  iflSize imgSize(src->getXsize(), src->getYsize(), 1, channels);

  ilMemoryImg *tmpImage = new ilMemoryImg(imgSize, src->getDataType(),
					  src->getOrder());
  tmpImage->setOrientation(iflUpperLeftOrigin);
  if (tmpImage->setColorModel(colorModel) != iflOKAY) {
    puts("aaaaaargh cant set color model");
    delete tmpImage;
    return NULL;
  }
  if (tmpImage->getStatus() != ilOKAY) {
    //setError(tmpImage->getStatus());
    delete tmpImage;
    return NULL;
  }
  if (src->getColorModel() == iflRGBPalette &&
      colorModel == iflRGBPalette)
    tmpImage->setColormap(*(src->getColormap()));

  if (tmpImage->getStatus() != ilOKAY) {
    //setError(tmpImage->getStatus());
    delete tmpImage;
    return NULL;
  }

  tmpImage->copy(src);
  if (tmpImage->getStatus() != ilOKAY) {
    //setError(tmpImage->getStatus());
    delete tmpImage;
    return NULL;
  }

  return tmpImage;
}

void imgProcess::setClient(short st)
{
  _client = st;
}