Expose libjpeg lossless transform feature in TurboJPEG/OSS


git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@464 632fc199-4ca6-4c93-a231-07263d6284db
diff --git a/turbojpegl.c b/turbojpegl.c
index 010a072..4755144 100644
--- a/turbojpegl.c
+++ b/turbojpegl.c
@@ -23,6 +23,7 @@
 #include <jerror.h>
 #include <setjmp.h>
 #include "./turbojpeg.h"
+#include "transupp.h"
 
 #ifndef min
  #define min(a,b) ((a)<(b)?(a):(b))
@@ -69,6 +70,10 @@
 static const int hsampfactor[NUMSUBOPT]={1, 2, 2, 1};
 static const int vsampfactor[NUMSUBOPT]={1, 1, 2, 1};
 static const int pixelsize[NUMSUBOPT]={3, 3, 3, 1};
+static const JXFORM_CODE xformtypes[NUMXFORMOPT]={
+	JXFORM_NONE, JXFORM_FLIP_H, JXFORM_FLIP_V, JXFORM_TRANSPOSE,
+	JXFORM_TRANSVERSE, JXFORM_ROT_90, JXFORM_ROT_180, JXFORM_ROT_270
+};
 
 #define _throw(c) {sprintf(lasterror, "%s", c);  retval=-1;  goto bailout;}
 #define checkhandle(h) jpgstruct *j=(jpgstruct *)h; \
@@ -87,12 +92,8 @@
 {
 }
 
-DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
+static tjhandle _tjInitCompress(jpgstruct *j)
 {
-	jpgstruct *j=NULL;
-	if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
-		{sprintf(lasterror, "Memory allocation failure");  return NULL;}
-	memset(j, 0, sizeof(jpgstruct));
 	j->cinfo.err=jpeg_std_error(&j->jerr.pub);
 	j->jerr.pub.error_exit=my_error_exit;
 	j->jerr.pub.output_message=my_output_message;
@@ -112,6 +113,15 @@
 	return (tjhandle)j;
 }
 
+DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
+{
+	jpgstruct *j=NULL;
+	if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
+		{sprintf(lasterror, "Memory allocation failure");  return NULL;}
+	memset(j, 0, sizeof(jpgstruct));
+	return _tjInitCompress(j);
+}
+
 
 DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
 {
@@ -364,12 +374,8 @@
 {
 }
 
-DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
+static tjhandle _tjInitDecompress(jpgstruct *j)
 {
-	jpgstruct *j;
-	if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
-		{sprintf(lasterror, "Memory allocation failure");  return NULL;}
-	memset(j, 0, sizeof(jpgstruct));
 	j->dinfo.err=jpeg_std_error(&j->jerr.pub);
 	j->jerr.pub.error_exit=my_error_exit;
 	j->jerr.pub.output_message=my_output_message;
@@ -391,6 +397,15 @@
 	return (tjhandle)j;
 }
 
+DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
+{
+	jpgstruct *j;
+	if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
+		{sprintf(lasterror, "Memory allocation failure");  return NULL;}
+	memset(j, 0, sizeof(jpgstruct));
+	return _tjInitDecompress(j);
+}
+
 
 DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h,
 	unsigned char *srcbuf, unsigned long size,
@@ -682,6 +697,120 @@
 }
 
 
+// Transformation
+
+DLLEXPORT tjhandle DLLCALL tjInitTransform(void)
+{
+	jpgstruct *j=NULL;  tjhandle tj=NULL;
+	if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL)
+		{sprintf(lasterror, "Memory allocation failure");  return NULL;}
+	memset(j, 0, sizeof(jpgstruct));
+	tj=_tjInitCompress(j);
+	if(!tj) return NULL;
+	tj=_tjInitDecompress(j);
+	return tj;
+}
+
+
+DLLEXPORT int DLLCALL tjTransform(tjhandle hnd,
+	unsigned char *srcbuf, unsigned long srcsize,
+	unsigned char *dstbuf, unsigned long *dstsize,
+	int x, int y, int w, int h, int op, int options, int flags)
+{
+	jpeg_transform_info xinfo;
+	jvirt_barray_ptr *srccoefs, *dstcoefs;
+	int retval=0;
+
+	checkhandle(hnd);
+
+	if(srcbuf==NULL || srcsize<=0 || dstbuf==NULL || dstsize==NULL
+		|| x<0 || y<0 || w<0 || h<0 || op<0 || op>=NUMXFORMOPT
+		|| flags<0)
+		_throw("Invalid argument in tjTransform()");
+	if(!j->initc || !j->initd)
+		_throw("Instance has not been initialized for transformation");
+
+	if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
+	else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1");
+	else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
+
+	if(setjmp(j->jerr.jb))
+	{  // this will execute if LIBJPEG has an error
+		retval=-1;
+		goto bailout;
+	}
+
+	j->jsms.bytes_in_buffer=srcsize;
+	j->jsms.next_input_byte=srcbuf;
+
+	xinfo.transform=xformtypes[op];
+	xinfo.perfect=(options&TJXFORM_PERFECT)? 1:0;
+	xinfo.trim=(options&TJXFORM_TRIM)? 1:0;
+	xinfo.force_grayscale=(options&TJXFORM_GRAY)? 1:0;
+	xinfo.crop=(options&TJXFORM_CROP)? 1:0;
+
+	if(xinfo.crop)
+	{
+		xinfo.crop_xoffset=x;  xinfo.crop_xoffset_set=JCROP_POS;
+		xinfo.crop_yoffset=y;  xinfo.crop_yoffset_set=JCROP_POS;
+		if(w!=0)
+		{
+			xinfo.crop_width=w;  xinfo.crop_width_set=JCROP_POS;
+		}
+		if(h!=0)
+		{
+			xinfo.crop_height=h; xinfo.crop_height_set=JCROP_POS;
+		}
+	}
+
+	jcopy_markers_setup(&j->dinfo, JCOPYOPT_NONE);
+	jpeg_read_header(&j->dinfo, TRUE);
+
+	if(!jtransform_request_workspace(&j->dinfo, &xinfo))
+		_throw("Transform is not perfect");
+
+	if(!xinfo.crop)
+	{
+		w=j->dinfo.image_width;  h=j->dinfo.image_height;
+	}
+	else
+	{
+		w=xinfo.crop_width;  h=xinfo.crop_height;
+	}
+
+	j->jdms.next_output_byte=dstbuf;
+	j->jdms.free_in_buffer=TJBUFSIZE(w, h);
+
+	if(xinfo.crop)
+	{
+		if((x%xinfo.iMCU_sample_width)!=0 || (y%xinfo.iMCU_sample_height)!=0)
+		{
+			sprintf(lasterror, "To crop this JPEG image, x must be a multiple of %d and y must be a multiple\n"
+				"of %d.\n", xinfo.iMCU_sample_width, xinfo.iMCU_sample_height);
+			retval=-1;  goto bailout;
+		}
+	}
+
+	srccoefs=jpeg_read_coefficients(&j->dinfo);
+	jpeg_copy_critical_parameters(&j->dinfo, &j->cinfo);
+	dstcoefs=jtransform_adjust_parameters(&j->dinfo, &j->cinfo, srccoefs,
+		&xinfo);
+	jpeg_write_coefficients(&j->cinfo, dstcoefs);
+	jcopy_markers_execute(&j->dinfo, &j->cinfo, JCOPYOPT_ALL);
+	jtransform_execute_transformation(&j->dinfo, &j->cinfo, srccoefs, &xinfo);
+
+	jpeg_finish_compress(&j->cinfo);
+	jpeg_finish_decompress(&j->dinfo);
+
+	*dstsize=TJBUFSIZE(w, h)-(unsigned long)(j->jdms.free_in_buffer);
+
+	bailout:
+	if(j->cinfo.global_state>CSTATE_START) jpeg_abort_compress(&j->cinfo);
+	if(j->dinfo.global_state>DSTATE_START) jpeg_abort_decompress(&j->dinfo);
+	return retval;
+}
+
+
 // General
 
 DLLEXPORT char* DLLCALL tjGetErrorStr(void)