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 "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include "libgen.h"

#include "cv.h"
#include "highgui.h"


void *get_ptr(SV *self, char *param)
{
	void *ptr = NULL;
	
	if ( hv_exists((HV*)SvRV(self) , param , strlen(param)) )
		ptr = SvIV(*hv_fetch( (HV*)SvRV(self) , param , strlen(param), 0));
	
	return ptr;
}

void set_ptr(SV *self, char *param, void *ptr)
{
	if (ptr) hv_store((HV*)SvRV(self), param, strlen(param), newSViv(PTR2IV(ptr)), 0);
}

char* get_file_extension(char *filename)
{
	char *ext;
	ext = strrchr(basename(filename), '.');

	return ext ? ext+1 : NULL;
}

MODULE = Image::Resize::OpenCV		PACKAGE = Image::Resize::OpenCV		

IV _init(self, filename = NULL)
	SV *self;
	char *filename;
	PREINIT:
		IplImage *img, *gray;
		AV *retval;
	CODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}
		
		if (filename)
		{
			img = cvLoadImage(filename, CV_LOAD_IMAGE_UNCHANGED);
			if (!img) croak("Can't load the image file `%s'", filename);
		
			set_ptr(self, "_img", img);
		}
		
		RETVAL = 1;
	OUTPUT:
		RETVAL

IV load(self, filename)
	SV *self;
	char *filename;
	PREINIT:
		IplImage *img, *gray;
		AV *retval;
	CODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}
		
		img = (IplImage *) get_ptr(self, "_img");
		if (img)
		{
			cvReleaseImage(&img);
			img = NULL;
		}

		img = cvLoadImage(filename, CV_LOAD_IMAGE_UNCHANGED);
		if (!img) croak("Can't load the image file `%s'", filename);

		set_ptr(self, "_img", img);
		
		RETVAL = 1;
	OUTPUT:
		RETVAL
		

void resize(self, width, height, ...)
	SV *self;
	int width;
	int height;
	PREINIT:
		IplImage *img, *small_img;
		int inter = 1;
		int keep_aspect = 0;
	PPCODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}

		if (items % 2 == 0)
		{
			croak("ERROR: resize - called with odd number of option parameters - should be of the form option => value");
		}

		int i;
		for (i = 1; i < items; i+=2)
		{

			char *key = SvPV_nolen(ST(i));
			IV value = SvIV(ST(i + 1));
			if (strcasecmp(key, "inter") == 0)
			{
				inter = value;
			}
			else if (strcasecmp(key, "keep_aspect") == 0)
			{
				keep_aspect = value;
			}
		}

		img = (IplImage *) get_ptr(self, "_img");
		if (!img) croak("image not loaded!");
		
		if (keep_aspect == 1)
		{
			double img_scale = 1;
			if (abs(img->width - width) > abs(img->height - height))
				img_scale = (double) img->width / (double) width;
			else
				img_scale = (double) img->height / (double) height;
			
			small_img = cvCreateImage(cvSize(cvRound ((double) img->width / img_scale),
			                          cvRound ((double) img->height / img_scale)), img->depth, img->nChannels );
		}
		else
		{
			small_img = cvCreateImage(cvSize(width, height), img->depth, img->nChannels );
		
		}

		cvResize(img, small_img, inter);
		cvReleaseImage(&img);
		
		set_ptr(self, "_img", small_img);
		
		img = small_img;

		EXTEND(SP, 2);
		PUSHs(sv_2mortal(newSViv(img -> width)));
		PUSHs(sv_2mortal(newSViv(img -> height)));
		

IV width(self)
	SV *self;
	PREINIT:
		IplImage *img;
	CODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}
		
		img = (IplImage *) get_ptr(self, "_img");
		if (!img) croak("image not loaded!");

		RETVAL = img->width;
	OUTPUT:
		RETVAL

IV height(self)
	SV *self;
	PREINIT:
		IplImage *img;
	CODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}
		
		img = (IplImage *) get_ptr(self, "_img");
		if (!img) croak("image not loaded!");

		RETVAL = img->height;
	OUTPUT:
		RETVAL

IV save(self, filename, compression = 25)
	SV *self;
	char *filename;
	int compression;
	PREINIT:
		IplImage *img;
		int p[3];
	CODE:
		if (!SvROK(self)) {XSRETURN_UNDEF;}
		
		img = (IplImage *) get_ptr(self, "_img");
		if (!img) croak("image not loaded!");

		char *ext = get_file_extension(filename);
		if (!ext) croak("File extension not defined");
		
		if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0)
		{
			p[0] = CV_IMWRITE_JPEG_QUALITY;
			p[1] = 100 - (compression);
			p[2] = 0;
		}
		else if (strcasecmp(ext, "png") == 0)
		{
			p[0] = CV_IMWRITE_PNG_COMPRESSION;
			p[1] = (int) compression/10;
			p[2] = 0;
		}

		cvSaveImage(filename, img, p);

		RETVAL = 1;
	OUTPUT:
		RETVAL
	

void DESTROY(self)
		SV *self;
	PREINIT:
		IplImage *img;
	CODE:
		img = (IplImage *) get_ptr(self, "_img");
		if (img) cvReleaseImage(&img);