]> granicus.if.org Git - libjpeg-turbo/commitdiff
Since lossless transforms have such a high fixed performance cost, implement a mechan...
authorDRC <dcommander@users.sourceforge.net>
Wed, 2 Mar 2011 09:22:41 +0000 (09:22 +0000)
committerDRC <dcommander@users.sourceforge.net>
Wed, 2 Mar 2011 09:22:41 +0000 (09:22 +0000)
git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@488 632fc199-4ca6-4c93-a231-07263d6284db

jpgtest.c
turbojpeg.h
turbojpegl.c

index f70ecd90be0fb3e7cb58761d49c80208efc95c24..6b9cc4cbc6c0433c1b4d53d4e4df9219828cd5f4 100644 (file)
--- a/jpgtest.c
+++ b/jpgtest.c
@@ -170,8 +170,14 @@ int decomptest(unsigned char *srcbuf, unsigned char **jpegbuf,
                                snprintf(tempstr, 1024, "%s_%s%s_full.%s", filename,
                                        _subnames[jpegsub], qualstr, useppm?"ppm":"bmp");
                }
-               else snprintf(tempstr, 1024, "%s_%s%s_%dx%d.%s", filename,
-                       _subnames[jpegsub], qualstr, tilesizex, tilesizey, useppm?"ppm":"bmp");
+               else
+               {
+                       if(decomponly)
+                               snprintf(tempstr, 1024, "%s_%dx%d.%s", filename, tilesizex, tilesizey,
+                                       useppm?"ppm":"bmp");
+                       else snprintf(tempstr, 1024, "%s_%s%s_%dx%d.%s", filename,
+                               _subnames[jpegsub], qualstr, tilesizex, tilesizey, useppm?"ppm":"bmp");
+               }
                if(savebmp(tempstr, rgbbuf, scaledw, scaledh, pf, pitch, bu)==-1)
                        _throwbmp("saving bitmap");
                ptr=strrchr(tempstr, '.');
@@ -387,6 +393,7 @@ void dodecomptest(char *filename)
        FILE *file=NULL;  tjhandle hnd=NULL;
        unsigned char **jpegbuf=NULL, *srcbuf=NULL;
        unsigned long *comptilesize=NULL, srcbufsize, jpgbufsize;
+       tjtransform *t=NULL;
        int w=0, h=0, jpegsub=-1;
        char *temp=NULL;
        int i, j, tilesizex, tilesizey, numtilesx, numtilesy, retval=0;
@@ -420,7 +427,7 @@ void dodecomptest(char *filename)
 
        if(dotile)
        {
-               tilesizex=tilesizey=512;
+               tilesizex=tilesizey=8;
                if(quiet==1)
                {
                        printf("All performance values in Mpixels/sec\n\n");
@@ -429,8 +436,8 @@ void dodecomptest(char *filename)
                }
                else if(!quiet)
                {
-                       printf(">>>>>  JPEG --> %s (%s)  <<<<<\n", _pfname[pf],
-                               bu?"Bottom-up":"Top-down");
+                       printf(">>>>>  JPEG %s --> %s (%s)  <<<<<\n", _subnamel[jpegsub],
+                               _pfname[pf], bu?"Bottom-up":"Top-down");
                }
                do
                {
@@ -438,11 +445,16 @@ void dodecomptest(char *filename)
                        tilesizey*=2;  if(tilesizey>h) tilesizey=h;
                        numtilesx=(w+tilesizex-1)/tilesizex;
                        numtilesy=(h+tilesizey-1)/tilesizey;
+
+                       if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)
+                               *numtilesx*numtilesy))==NULL)
+                               _throwunix("allocating image buffer array");
                        if((comptilesize=(unsigned long *)malloc(sizeof(unsigned long)
-                               *numtilesx*numtilesy))==NULL
-                               || (jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)
                                *numtilesx*numtilesy))==NULL)
-                               _throwunix("allocating image buffers");
+                               _throwunix("allocating image size array");
+                       if((t=(tjtransform *)malloc(sizeof(tjtransform)*numtilesx*numtilesy))
+                               ==NULL)
+                               _throwunix("allocating image transform array");
                        memset(jpegbuf, 0, sizeof(unsigned char *)*numtilesx*numtilesy);
 
                        for(i=0; i<numtilesx*numtilesy; i++)
@@ -459,24 +471,30 @@ void dodecomptest(char *filename)
                                else printf("%-4d %-4d\t", tilesizex, tilesizey);
                        }
 
-                       start=rrtime();
-                       jpgbufsize=0;  tilen=0;
-                       for(i=0; i<h; i+=tilesizey)
+                       for(i=0, tilen=0; i<h; i+=tilesizey)
                        {
-                               for(j=0; j<w; j+=tilesizex)
-                                       {
-                                               int tempw=min(tilesizex, w-j), temph=min(tilesizey, h-i);
-                                               if(tjTransform(hnd, srcbuf, srcbufsize, jpegbuf[tilen],
-                                                       &comptilesize[tilen], j, i, tempw, temph, TJXFORM_NONE,
-                                                       TJXFORM_CROP, flags)
-                                                       ==-1)
-                                               _throwtj("executing tjTransform()");
-                                       jpgbufsize+=comptilesize[tilen];
-                                       tilen++;
+                               for(j=0; j<w; j+=tilesizex, tilen++)
+                               {
+                                       t[tilen].r.x=j;
+                                       t[tilen].r.y=i;
+                                       t[tilen].r.w=min(tilesizex, w-j);
+                                       t[tilen].r.h=min(tilesizey, h-i);
+                                       t[tilen].op=TJXFORM_NONE;
+                                       t[tilen].options=TJXFORM_CROP;
                                }
                        }
+
+                       start=rrtime();
+                       if(tjTransform(hnd, srcbuf, srcbufsize, numtilesx*numtilesy, jpegbuf,
+                               comptilesize, t, flags)==-1)
+                               _throwtj("executing tjTransform()");
                        elapsed=rrtime()-start;
 
+                       for(tilen=0, jpgbufsize=0; tilen<numtilesx*numtilesy; tilen++)
+                       {
+                               jpgbufsize+=comptilesize[tilen];
+                       }
+
                        if(quiet)
                        {
                                printsigfig((double)(w*h)/1000000./elapsed, 4);
@@ -524,8 +542,8 @@ void dodecomptest(char *filename)
                        if(yuv==YUVDECODE)
                                printf(">>>>>  JPEG --> YUV %s  <<<<<\n", _subnamel[jpegsub]);
                        else
-                               printf(">>>>>  JPEG --> %s (%s)  <<<<<\n", _pfname[pf],
-                                       bu?"Bottom-up":"Top-down");
+                               printf(">>>>>  JPEG %s --> %s (%s)  <<<<<\n", _subnamel[jpegsub],
+                                       _pfname[pf], bu?"Bottom-up":"Top-down");
                        printf("\nImage size: %d x %d", w, h);
                        if(scale_num!=1 || scale_denom!=1)
                                printf(" --> %d x %d", (w*scale_num+scale_denom-1)/scale_denom,
index 77c387ddf73635b0e8fefab3a4854cb0e832ed7a..8a69316998f5e740dd43c4b54ed6f690bf8bd33d 100644 (file)
@@ -28,6 +28,9 @@ enum {TJ_444=0, TJ_422, TJ_420, TJ_GRAYSCALE};
 #define TJ_411 TJ_420  /* for backward compatibility with VirtualGL <= 2.1.x,
                           TurboVNC <= 0.6, and TurboJPEG/IPP */
 
+static const int tjmcuw[NUMSUBOPT]={8, 16, 16, 8};
+static const int tjmcuh[NUMSUBOPT]={8, 8, 16, 8};
+
 /* Flags */
 #define TJ_BGR             1
   /* The components of each pixel in the uncompressed source/destination image
@@ -413,41 +416,64 @@ DLLEXPORT tjhandle DLLCALL tjInitTransform(void);
 
 
 /*
-  int tjTransform(tjhandle j,
+  int 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)
+     int n, unsigned char **dstbufs, unsigned long *dstsizes,
+     tjtransform *transforms, int flags);
+
+  This function can losslessly transform a JPEG image into another JPEG image.
+  Lossless transforms work by moving the raw coefficients from one JPEG image
+  structure to another without altering the values of the coefficients.  While
+  this is typically faster than decompressing the image, transforming it, and
+  re-compressing it, lossless transforms are not free.  Each lossless transform
+  requires reading and Huffman decoding all of the coefficients in the source
+  image, regardless of the size of the destination image.  Thus, this function
+  provides a means of generating multiple transformed images from the same
+  source or of applying multiple transformations simultaneously, in order to
+  eliminate the need to read the source coefficients multiple times.
 
-  [INPUT] j = instance handle previously returned from a call to
+  [INPUT] hnd = instance handle previously returned from a call to
      tjInitTransform()
   [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image
      to transform
   [INPUT] srcsize = size of the source JPEG image buffer (in bytes)
-  [INPUT] dstbuf = pointer to user-allocated image buffer which will receive
-     the transformed JPEG image.  Use the TJBUFSIZE(width, height) function to
-     determine the appropriate size for this buffer based on the cropped width
-     and height.
-  [OUTPUT] dstsize = pointer to unsigned long which receives the size (in
-     bytes) of the transformed image
-  [INPUT] x, y, w, h = the left edge, top edge, width, and height of the
-     cropping region.  If (x, y) does not fall on an MCU boundary, then x and
-     y will be silently moved left and/or up to the nearest MCU boundary.  You
-     can call tjGetCroppedSize() to determine how (or if) x, y, w, and h will
-     be modified ahead of time, so you can allocate the output buffer
-     appropriately.
-  [INPUT] op = one of the transform operations described in the "Transform
-     operations" section above.
-  [INPUT] options = the bitwise OR of one or more of the transform options
-     described in the "Transform options" section above.
+  [INPUT] n = the number of transformed JPEG images to generate
+  [INPUT] dstbufs = pointer to an array of n user-allocated image buffers.
+     dstbufs[i] will receive a JPEG image that has been transformed using the
+     parameters in transforms[i].  Use the TJBUFSIZE(width, height) function to
+     determine the maximum size for each buffer based on the cropped width and
+     height.
+  [OUTPUT] dstsizes = pointer to an array of n unsigned longs which will
+     receive the actual sizes (in bytes) of each transformed JPEG image
+  [INPUT] transforms = pointer to an array of n tjtransform structures, each of
+     which specifies the transform parameters and/or cropping region for the
+     corresponding transformed output image.  The structure members are as
+     follows:
+
+     r.x = the left boundary of the cropping region.  This must be evenly
+        divisible by tjmcuw[subsamp] (the MCU block width corresponding to the
+        level of chrominance subsampling used in the source image)
+     r.y = the upper boundary of the cropping region.  This must be evenly
+        divisible by tjmcuh[subsamp] (the MCU block height corresponding to the
+        level of chrominance subsampling used in the source image)
+     r.w = the width of the cropping region.  Setting this to 0 is the
+        equivalent of setting it to the width of the source JPEG image.
+     r.h = the height of the cropping region.  Setting this to 0 is the
+        equivalent of setting it to the height of the source JPEG image.
+     op = one of the transform operations described in the
+        "Transform operations" section above
+     options = the bitwise OR of one or more of the transform options described
+        in the "Transform options" section above.
+
   [INPUT] flags = the bitwise OR of one or more of the flags described in the
      "Flags" section above.
 
   RETURNS: 0 on success, -1 on error
 */
-DLLEXPORT int DLLCALL tjTransform(tjhandle j,
-       unsigned char *srcbuf, unsigned long size,
-       unsigned char *dstbuf, unsigned long *dstsize,
-       int x, int y, int w, int h, int op, int options, int flags);
+DLLEXPORT int DLLCALL tjTransform(tjhandle hnd,
+       unsigned char *srcbuf, unsigned long srcsize,
+       int n, unsigned char **dstbufs, unsigned long *dstsizes,
+       tjtransform *transforms, int flags);
 
 
 /*
index dc1578d17caa6af5f3f3848cb006df65fc3ca71a..c2ca1fcb204edc0d9d6dcef884b088d73cc2626e 100644 (file)
@@ -68,8 +68,6 @@ typedef struct _jpgstruct
        int initc, initd;
 } jpgstruct;
 
-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,
@@ -85,7 +83,7 @@ static const tjscalingfactor sf[NUMSF]={
 
 #define _throw(c) {snprintf(lasterror, JMSG_LENGTH_MAX, "%s", c);  \
        retval=-1;  goto bailout;}
-#define checkhandle(h) jpgstruct *j=(jpgstruct *)h; \
+#define checkhandle(hnd) jpgstruct *j=(jpgstruct *)hnd; \
        if(!j) {snprintf(lasterror, JMSG_LENGTH_MAX, "Invalid handle");  return -1;}
 
 
@@ -158,9 +156,9 @@ DLLEXPORT unsigned long DLLCALL TJBUFSIZEYUV(int width, int height,
        int pw, ph, cw, ch;
        if(width<1 || height<1 || subsamp<0 || subsamp>=NUMSUBOPT)
                _throw("Invalid argument in TJBUFSIZEYUV()");
-       pw=PAD(width, hsampfactor[subsamp]);
-       ph=PAD(height, vsampfactor[subsamp]);
-       cw=pw/hsampfactor[subsamp];  ch=ph/vsampfactor[subsamp];
+       pw=PAD(width, tjmcuw[subsamp]/8);
+       ph=PAD(height, tjmcuh[subsamp]/8);
+       cw=pw*8/tjmcuw[subsamp];  ch=ph*8/tjmcuh[subsamp];
        retval=PAD(pw, 4)*ph + (subsamp==TJ_GRAYSCALE? 0:PAD(cw, 4)*ch*2);
 
        bailout:
@@ -168,7 +166,7 @@ DLLEXPORT unsigned long DLLCALL TJBUFSIZEYUV(int width, int height,
 }
 
 
-DLLEXPORT int DLLCALL tjCompress(tjhandle h,
+DLLEXPORT int DLLCALL tjCompress(tjhandle hnd,
        unsigned char *srcbuf, int width, int pitch, int height, int ps,
        unsigned char *dstbuf, unsigned long *size,
        int jpegsub, int qual, int flags)
@@ -178,7 +176,7 @@ DLLEXPORT int DLLCALL tjCompress(tjhandle h,
        JSAMPROW *tmpbuf[MAX_COMPONENTS], *tmpbuf2[MAX_COMPONENTS];
        JSAMPROW *outbuf[MAX_COMPONENTS];
 
-       checkhandle(h);
+       checkhandle(hnd);
 
        for(i=0; i<MAX_COMPONENTS; i++)
        {
@@ -237,10 +235,10 @@ DLLEXPORT int DLLCALL tjCompress(tjhandle h,
        if(qual>=96) j->cinfo.dct_method=JDCT_ISLOW;
        else j->cinfo.dct_method=JDCT_FASTEST;
 
-       j->cinfo.comp_info[0].h_samp_factor=hsampfactor[jpegsub];
+       j->cinfo.comp_info[0].h_samp_factor=tjmcuw[jpegsub]/8;
        j->cinfo.comp_info[1].h_samp_factor=1;
        j->cinfo.comp_info[2].h_samp_factor=1;
-       j->cinfo.comp_info[0].v_samp_factor=vsampfactor[jpegsub];
+       j->cinfo.comp_info[0].v_samp_factor=tjmcuh[jpegsub]/8;
        j->cinfo.comp_info[1].v_samp_factor=1;
        j->cinfo.comp_info[2].v_samp_factor=1;
 
@@ -358,12 +356,12 @@ DLLEXPORT int DLLCALL tjCompress(tjhandle h,
 }
 
 
-DLLEXPORT int DLLCALL tjEncodeYUV(tjhandle h,
+DLLEXPORT int DLLCALL tjEncodeYUV(tjhandle hnd,
        unsigned char *srcbuf, int width, int pitch, int height, int ps,
        unsigned char *dstbuf, int subsamp, int flags)
 {
        unsigned long size;
-       return tjCompress(h, srcbuf, width, pitch, height, ps, dstbuf, &size,
+       return tjCompress(hnd, srcbuf, width, pitch, height, ps, dstbuf, &size,
                subsamp, 0, flags|TJ_YUV);
 }
 
@@ -422,13 +420,13 @@ DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
 }
 
 
-DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h,
+DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle hnd,
        unsigned char *srcbuf, unsigned long size,
        int *width, int *height, int *jpegsub)
 {
        int i, k, retval=0;
 
-       checkhandle(h);
+       checkhandle(hnd);
 
        if(srcbuf==NULL || size<=0 || width==NULL || height==NULL || jpegsub==NULL)
                _throw("Invalid argument in tjDecompressHeader2()");
@@ -450,8 +448,8 @@ DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h,
        {
                if(j->dinfo.num_components==pixelsize[i])
                {
-                       if(j->dinfo.comp_info[0].h_samp_factor==hsampfactor[i]
-                               && j->dinfo.comp_info[0].v_samp_factor==vsampfactor[i])
+                       if(j->dinfo.comp_info[0].h_samp_factor==tjmcuw[i]/8
+                               && j->dinfo.comp_info[0].v_samp_factor==tjmcuh[i]/8)
                        {
                                int match=0;
                                for(k=1; k<j->dinfo.num_components; k++)
@@ -478,12 +476,12 @@ DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h,
 }
 
 
-DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle h,
+DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle hnd,
        unsigned char *srcbuf, unsigned long size,
        int *width, int *height)
 {
        int jpegsub;
-       return tjDecompressHeader2(h, srcbuf, size, width, height, &jpegsub);
+       return tjDecompressHeader2(hnd, srcbuf, size, width, height, &jpegsub);
 }
 
 
@@ -501,7 +499,7 @@ DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors)
 }
 
 
-DLLEXPORT int DLLCALL tjDecompress(tjhandle h,
+DLLEXPORT int DLLCALL tjDecompress(tjhandle hnd,
        unsigned char *srcbuf, unsigned long size,
        unsigned char *dstbuf, int width, int pitch, int height, int ps,
        int flags)
@@ -512,7 +510,7 @@ DLLEXPORT int DLLCALL tjDecompress(tjhandle h,
        JSAMPLE *_tmpbuf=NULL;  JSAMPROW *tmpbuf[MAX_COMPONENTS];
        int jpegwidth, jpegheight, scaledw, scaledh;
 
-       checkhandle(h);
+       checkhandle(hnd);
 
        for(i=0; i<MAX_COMPONENTS; i++)
        {
@@ -686,11 +684,11 @@ DLLEXPORT int DLLCALL tjDecompress(tjhandle h,
 }
 
 
-DLLEXPORT int DLLCALL tjDecompressToYUV(tjhandle h,
+DLLEXPORT int DLLCALL tjDecompressToYUV(tjhandle hnd,
        unsigned char *srcbuf, unsigned long size,
        unsigned char *dstbuf, int flags)
 {
-       return tjDecompress(h, srcbuf, size, dstbuf, 1, 0, 1, 3, flags|TJ_YUV);
+       return tjDecompress(hnd, srcbuf, size, dstbuf, 1, 0, 1, 3, flags|TJ_YUV);
 }
 
 
@@ -714,18 +712,17 @@ DLLEXPORT tjhandle DLLCALL tjInitTransform(void)
 
 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)
+       int n, unsigned char **dstbufs, unsigned long *dstsizes,
+       tjtransform *t, int flags)
 {
-       jpeg_transform_info xinfo;
+       jpeg_transform_info *xinfo=NULL;
        jvirt_barray_ptr *srccoefs, *dstcoefs;
-       int retval=0;
+       int retval=0, i;
 
        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)
+       if(srcbuf==NULL || srcsize<=0 || n<1 || dstbufs==NULL || dstsizes==NULL
+               || t==NULL || flags<0)
                _throw("Invalid argument in tjTransform()");
        if(!j->initc || !j->initd)
                _throw("Instance has not been initialized for transformation");
@@ -743,71 +740,88 @@ DLLEXPORT int DLLCALL tjTransform(tjhandle hnd,
        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=(jpeg_transform_info *)malloc(sizeof(jpeg_transform_info)*n))
+               ==NULL)
+               _throw("Memory allocation failed in tjTransform()");
 
-       if(xinfo.crop)
+       for(i=0; i<n; i++)
        {
-               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[i].transform=xformtypes[t[i].op];
+               xinfo[i].perfect=(t[i].options&TJXFORM_PERFECT)? 1:0;
+               xinfo[i].trim=(t[i].options&TJXFORM_TRIM)? 1:0;
+               xinfo[i].force_grayscale=(t[i].options&TJXFORM_GRAY)? 1:0;
+               xinfo[i].crop=(t[i].options&TJXFORM_CROP)? 1:0;
+
+               if(xinfo[i].crop)
                {
-                       xinfo.crop_height=h; xinfo.crop_height_set=JCROP_POS;
+                       xinfo[i].crop_xoffset=t[i].r.x;  xinfo[i].crop_xoffset_set=JCROP_POS;
+                       xinfo[i].crop_yoffset=t[i].r.y;  xinfo[i].crop_yoffset_set=JCROP_POS;
+                       if(t[i].r.w!=0)
+                       {
+                               xinfo[i].crop_width=t[i].r.w;  xinfo[i].crop_width_set=JCROP_POS;
+                       }
+                       if(t[i].r.h!=0)
+                       {
+                               xinfo[i].crop_height=t[i].r.h;  xinfo[i].crop_height_set=JCROP_POS;
+                       }
                }
        }
 
-       jcopy_markers_setup(&j->dinfo, JCOPYOPT_NONE);
+       jcopy_markers_setup(&j->dinfo, JCOPYOPT_ALL);
        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
+       for(i=0; i<n; i++)
        {
-               w=xinfo.crop_width;  h=xinfo.crop_height;
+               if(!jtransform_request_workspace(&j->dinfo, &xinfo[i]))
+                       _throw("Transform is not perfect");
+
+               if(xinfo[i].crop)
+               {
+                       if((t[i].r.x%xinfo[i].iMCU_sample_width)!=0
+                               || (t[i].r.y%xinfo[i].iMCU_sample_height)!=0)
+                       {
+                               snprintf(lasterror, JMSG_LENGTH_MAX,
+                                       "To crop this JPEG image, x must be a multiple of %d\n"
+                                       "and y must be a multiple of %d.\n",
+                                       xinfo[i].iMCU_sample_width, xinfo[i].iMCU_sample_height);
+                               retval=-1;  goto bailout;
+                       }
+               }
        }
 
-       j->jdms.next_output_byte=dstbuf;
-       j->jdms.free_in_buffer=TJBUFSIZE(w, h);
+       srccoefs=jpeg_read_coefficients(&j->dinfo);
 
-       if(xinfo.crop)
+       for(i=0; i<n; i++)
        {
-               if((x%xinfo.iMCU_sample_width)!=0 || (y%xinfo.iMCU_sample_height)!=0)
+               int w, h;
+               j->jdms.next_output_byte=dstbufs[i];
+               if(!xinfo[i].crop)
                {
-                       snprintf(lasterror, JMSG_LENGTH_MAX,
-                               "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;
+                       w=j->dinfo.image_width;  h=j->dinfo.image_height;
                }
+               else
+               {
+                       w=xinfo[i].crop_width;  h=xinfo[i].crop_height;
+               }
+               j->jdms.free_in_buffer=TJBUFSIZE(w, h);
+               jpeg_copy_critical_parameters(&j->dinfo, &j->cinfo);
+               dstcoefs=jtransform_adjust_parameters(&j->dinfo, &j->cinfo, srccoefs,
+                       &xinfo[i]);
+               jpeg_write_coefficients(&j->cinfo, dstcoefs);
+               jcopy_markers_execute(&j->dinfo, &j->cinfo, JCOPYOPT_ALL);
+               jtransform_execute_transformation(&j->dinfo, &j->cinfo, srccoefs,
+                       &xinfo[i]);
+               jpeg_finish_compress(&j->cinfo);
+
+               dstsizes[i]=TJBUFSIZE(w, h)-(unsigned long)(j->jdms.free_in_buffer);
        }
 
-       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);
+       if(xinfo) free(xinfo);
        return retval;
 }
 
@@ -819,9 +833,9 @@ DLLEXPORT char* DLLCALL tjGetErrorStr(void)
        return lasterror;
 }
 
-DLLEXPORT int DLLCALL tjDestroy(tjhandle h)
+DLLEXPORT int DLLCALL tjDestroy(tjhandle hnd)
 {
-       checkhandle(h);
+       checkhandle(hnd);
        if(setjmp(j->jerr.jb)) return -1;
        if(j->initc) jpeg_destroy_compress(&j->cinfo);
        if(j->initd) jpeg_destroy_decompress(&j->dinfo);