]> granicus.if.org Git - libjpeg-turbo/commitdiff
Implement a YUV decode function in the TurboJPEG API, to be symmetric with tjEncodeYUV().
authorDRC <dcommander@users.sourceforge.net>
Fri, 28 Feb 2014 09:17:14 +0000 (09:17 +0000)
committerDRC <dcommander@users.sourceforge.net>
Fri, 28 Feb 2014 09:17:14 +0000 (09:17 +0000)
git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@1132 632fc199-4ca6-4c93-a231-07263d6284db

ChangeLog.txt
doc/html/group___turbo_j_p_e_g.html
doc/html/search/all_74.js
doc/html/search/functions_74.js
tjunittest.c
turbojpeg-mapfile
turbojpeg-mapfile.jni
turbojpeg.c
turbojpeg.h

index 2ca98395690a6b47c538534011d667489706d4ca..ac3db63827ef0b3da2c37cdddbbf8ef762a2b597 100644 (file)
@@ -21,7 +21,8 @@ images is not supported.  Such conversion requires a color management system
 and is out of scope for a codec library.
 
 [5] The TurboJPEG API can now be used to compress JPEG images from YUV planar
-source images.
+source images and to decode YUV planar images into RGB, grayscale, or extended
+RGB images.
 
 [6] If an application attempts to decompress a Huffman-coded JPEG image whose
 header does not contain Huffman tables, libjpeg-turbo will now insert the
index 2c3395b747f01b80d1ca175ff5789886a1b0b60d..28e4926eed512c508eb424f2532996930f4d29b5 100644 (file)
@@ -255,6 +255,9 @@ Functions</h2></td></tr>
 <tr class="memitem:ga7c08b340ad7f8e85d407bd9e81d44d07"><td class="memItemLeft" align="right" valign="top">DLLEXPORT int DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga7c08b340ad7f8e85d407bd9e81d44d07">tjDecompressToYUV2</a> (<a class="el" href="group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763">tjhandle</a> handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pad, int height, int flags)</td></tr>
 <tr class="memdesc:ga7c08b340ad7f8e85d407bd9e81d44d07"><td class="mdescLeft">&#160;</td><td class="mdescRight">Decompress a JPEG image to a YUV planar image.  <a href="#ga7c08b340ad7f8e85d407bd9e81d44d07">More...</a><br/></td></tr>
 <tr class="separator:ga7c08b340ad7f8e85d407bd9e81d44d07"><td class="memSeparator" colspan="2">&#160;</td></tr>
+<tr class="memitem:ga132ae2c2cadcf64c8bb0f3bdf69da3ed"><td class="memItemLeft" align="right" valign="top">DLLEXPORT int DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga132ae2c2cadcf64c8bb0f3bdf69da3ed">tjDecodeYUV</a> (<a class="el" href="group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763">tjhandle</a> handle, unsigned char *srcBuf, int pad, int subsamp, unsigned char *dstBuf, int width, int pitch, int height, int pixelFormat, int flags)</td></tr>
+<tr class="memdesc:ga132ae2c2cadcf64c8bb0f3bdf69da3ed"><td class="mdescLeft">&#160;</td><td class="mdescRight">Decode a YUV planar image into an RGB or grayscale image.  <a href="#ga132ae2c2cadcf64c8bb0f3bdf69da3ed">More...</a><br/></td></tr>
+<tr class="separator:ga132ae2c2cadcf64c8bb0f3bdf69da3ed"><td class="memSeparator" colspan="2">&#160;</td></tr>
 <tr class="memitem:ga3155b775bfbac9dbba869b95a0367902"><td class="memItemLeft" align="right" valign="top">DLLEXPORT <a class="el" href="group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763">tjhandle</a> DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga3155b775bfbac9dbba869b95a0367902">tjInitTransform</a> (void)</td></tr>
 <tr class="memdesc:ga3155b775bfbac9dbba869b95a0367902"><td class="mdescLeft">&#160;</td><td class="mdescRight">Create a new TurboJPEG transformer instance.  <a href="#ga3155b775bfbac9dbba869b95a0367902">More...</a><br/></td></tr>
 <tr class="separator:ga3155b775bfbac9dbba869b95a0367902"><td class="memSeparator" colspan="2">&#160;</td></tr>
@@ -1086,6 +1089,100 @@ If you choose option 1, <code>*jpegSize</code> should be set to the size of your
 </dl>
 <dl class="section return"><dt>Returns</dt><dd>0 if successful, or -1 if an error occurred (see <a class="el" href="group___turbo_j_p_e_g.html#ga9af79c908ec131b1ae8d52fe40375abf" title="Returns a descriptive error message explaining why the last command failed.">tjGetErrorStr()</a>.) </dd></dl>
 
+</div>
+</div>
+<a class="anchor" id="ga132ae2c2cadcf64c8bb0f3bdf69da3ed"></a>
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">DLLEXPORT int DLLCALL tjDecodeYUV </td>
+          <td>(</td>
+          <td class="paramtype"><a class="el" href="group___turbo_j_p_e_g.html#ga758d2634ecb4949de7815cba621f5763">tjhandle</a>&#160;</td>
+          <td class="paramname"><em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">unsigned char *&#160;</td>
+          <td class="paramname"><em>srcBuf</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>pad</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>subsamp</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">unsigned char *&#160;</td>
+          <td class="paramname"><em>dstBuf</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>width</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>pitch</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>height</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>pixelFormat</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>flags</em>&#160;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td>
+        </tr>
+      </table>
+</div><div class="memdoc">
+
+<p>Decode a YUV planar image into an RGB or grayscale image. </p>
+<p>This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG decompression process. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the source buffer, and the size of each plane is determined by the width and height of the source image, as well as the specified padding and level of chrominance subsampling. If the chrominance components are subsampled along the horizontal dimension, then the width of the luminance plane should be padded to the nearest multiple of 2 in the input image (same goes for the height of the luminance plane, if the chrominance components are subsampled along the vertical dimension.) </p>
+<p>NOTE: Technically, the JPEG format uses the YCbCr colorspace, but per the convention of the digital video community, the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, and Cr image planes.</p>
+<dl class="params"><dt>Parameters</dt><dd>
+  <table class="params">
+    <tr><td class="paramname">handle</td><td>a handle to a TurboJPEG decompressor or transformer instance </td></tr>
+    <tr><td class="paramname">srcBuf</td><td>pointer to an image buffer containing a YUV planar image to be decoded. The size of this buffer should match the value returned by <a class="el" href="group___turbo_j_p_e_g.html#gaf451664a62c1f6c7cc5a6401f32908c9" title="The size of the buffer (in bytes) required to hold a YUV planar image with the given parameters...">tjBufSizeYUV2()</a> for the given image width, height, padding, and level of chrominance subsampling. </td></tr>
+    <tr><td class="paramname">pad</td><td>Use this parameter to specify that the width of each line in each plane of the YUV source image is padded to the nearest multiple of this number of bytes (must be a power of 2.) </td></tr>
+    <tr><td class="paramname">subsamp</td><td>the level of chrominance subsampling used in the YUV source image (see <a class="el" href="group___turbo_j_p_e_g.html#ga1d047060ea80bb9820d540bb928e9074">Chrominance subsampling options</a>.) </td></tr>
+    <tr><td class="paramname">dstBuf</td><td>pointer to an image buffer that will receive the decoded image. This buffer should normally be <code>pitch * height</code> bytes in size, but the <code>dstBuf</code> pointer can also be used to decode into a specific region of a larger buffer. </td></tr>
+    <tr><td class="paramname">width</td><td>width (in pixels) of the source and destination images </td></tr>
+    <tr><td class="paramname">pitch</td><td>bytes per line of the destination image. Normally, this should be <code>width * <a class="el" href="group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c" title="Pixel size (in bytes) for a given pixel format.">tjPixelSize</a>[pixelFormat]</code> if the destination image is unpadded, or <code><a class="el" href="group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511" title="Pad the given width to the nearest 32-bit boundary.">TJPAD</a>(width * <a class="el" href="group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c" title="Pixel size (in bytes) for a given pixel format.">tjPixelSize</a>[pixelFormat])</code> if each line of the destination image should be padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. You can also be clever and use the pitch parameter to skip lines, etc. Setting this parameter to 0 is the equivalent of setting it to <code>width * <a class="el" href="group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c" title="Pixel size (in bytes) for a given pixel format.">tjPixelSize</a>[pixelFormat]</code>. </td></tr>
+    <tr><td class="paramname">height</td><td>height (in pixels) of the source and destination images </td></tr>
+    <tr><td class="paramname">pixelFormat</td><td>pixel format of the destination image (see <a class="el" href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">Pixel formats</a>.) </td></tr>
+    <tr><td class="paramname">flags</td><td>the bitwise OR of one or more of the <a class="el" href="group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec">flags</a>.</td></tr>
+  </table>
+  </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>0 if successful, or -1 if an error occurred (see <a class="el" href="group___turbo_j_p_e_g.html#ga9af79c908ec131b1ae8d52fe40375abf" title="Returns a descriptive error message explaining why the last command failed.">tjGetErrorStr()</a>.) </dd></dl>
+
 </div>
 </div>
 <a class="anchor" id="gada69cc6443d1bb493b40f1626259e5e9"></a>
@@ -1422,7 +1519,7 @@ If you choose option 1, <code>*jpegSize</code> should be set to the size of your
 </div><div class="memdoc">
 
 <p>Encode an RGB or grayscale image into a YUV planar image. </p>
-<p>This function uses the accelerated color conversion routines in TurboJPEG's underlying codec but does not execute any of the other steps in the JPEG compression process. The Y, U (Cb), and V (Cr) image planes are stored sequentially into the destination buffer, and the size of each plane is determined by the width and height of the source image, as well as the specified padding and level of chrominance subsampling. If the chrominance components are subsampled along the horizontal dimension, then the width of the luminance plane is padded to the nearest multiple of 2 in the output image (same goes for the height of the luminance plane, if the chrominance components are subsampled along the vertical dimension.) </p>
+<p>This function uses the accelerated color conversion routines in the underlying codec but does not execute any of the other steps in the JPEG compression process. The Y, U (Cb), and V (Cr) image planes are stored sequentially into the destination buffer, and the size of each plane is determined by the width and height of the source image, as well as the specified padding and level of chrominance subsampling. If the chrominance components are subsampled along the horizontal dimension, then the width of the luminance plane is padded to the nearest multiple of 2 in the output image (same goes for the height of the luminance plane, if the chrominance components are subsampled along the vertical dimension.) </p>
 <p>NOTE: Technically, the JPEG format uses the YCbCr colorspace, but per the convention of the digital video community, the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, and Cr image planes.</p>
 <dl class="params"><dt>Parameters</dt><dd>
   <table class="params">
index 21e0ced8ea57a9b0fe351afe9e01f709631d0505..435cec4d31b9558e532757465261c18d3cf4589d 100644 (file)
@@ -16,6 +16,7 @@ var searchData=
   ['tjcs_5frgb',['TJCS_RGB',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a677cb7ccb85c4038ac41964a2e09e555',1,'turbojpeg.h']]],
   ['tjcs_5fycbcr',['TJCS_YCbCr',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a7389b8f65bb387ffedce3efd0d78ec75',1,'turbojpeg.h']]],
   ['tjcs_5fycck',['TJCS_YCCK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e',1,'turbojpeg.h']]],
+  ['tjdecodeyuv',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga132ae2c2cadcf64c8bb0f3bdf69da3ed',1,'turbojpeg.h']]],
   ['tjdecompress2',['tjDecompress2',['../group___turbo_j_p_e_g.html#gada69cc6443d1bb493b40f1626259e5e9',1,'turbojpeg.h']]],
   ['tjdecompressheader3',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#gacd0fac3af74b3511d39b4781b7103086',1,'turbojpeg.h']]],
   ['tjdecompresstoyuv2',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga7c08b340ad7f8e85d407bd9e81d44d07',1,'turbojpeg.h']]],
index 4a42d2aafc4c578e88119d1905a8dd4f5dc3a86b..0a0e6cdccc932041e155cdccb00a60b63c6fb55b 100644 (file)
@@ -5,6 +5,7 @@ var searchData=
   ['tjbufsizeyuv2',['tjBufSizeYUV2',['../group___turbo_j_p_e_g.html#gaf451664a62c1f6c7cc5a6401f32908c9',1,'turbojpeg.h']]],
   ['tjcompress2',['tjCompress2',['../group___turbo_j_p_e_g.html#gaba62b7a98f960839b588579898495cf2',1,'turbojpeg.h']]],
   ['tjcompressfromyuv',['tjCompressFromYUV',['../group___turbo_j_p_e_g.html#ga0b931126c7a615ddc3bbd0cca6698d67',1,'turbojpeg.h']]],
+  ['tjdecodeyuv',['tjDecodeYUV',['../group___turbo_j_p_e_g.html#ga132ae2c2cadcf64c8bb0f3bdf69da3ed',1,'turbojpeg.h']]],
   ['tjdecompress2',['tjDecompress2',['../group___turbo_j_p_e_g.html#gada69cc6443d1bb493b40f1626259e5e9',1,'turbojpeg.h']]],
   ['tjdecompressheader3',['tjDecompressHeader3',['../group___turbo_j_p_e_g.html#gacd0fac3af74b3511d39b4781b7103086',1,'turbojpeg.h']]],
   ['tjdecompresstoyuv2',['tjDecompressToYUV2',['../group___turbo_j_p_e_g.html#ga7c08b340ad7f8e85d407bd9e81d44d07',1,'turbojpeg.h']]],
index 6b14db1801ff0e42ae7b32be24b92666b81386c8..8bdd7210ef0e5339317dc7c07e5c79fcc0f8d0f1 100644 (file)
@@ -80,7 +80,7 @@ const int _onlyGray[]={TJPF_GRAY};
 const int _onlyRGB[]={TJPF_RGB};
 
 enum {YUVENCODE=1, YUVDECODE};
-int yuv=0, alloc=0, pad=4;
+int doyuv=0, alloc=0, pad=4;
 
 int exitStatus=0;
 #define bailout() {exitStatus=-1;  goto bailout;}
@@ -295,60 +295,6 @@ int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp,
 
 #define PAD(v, p) ((v+(p)-1)&(~((p)-1)))
 
-void initBufYUV(unsigned char *buf, int w, int pad, int h, int subsamp)
-{
-       int row, col;
-       int hsf=tjMCUWidth[subsamp]/8, vsf=tjMCUHeight[subsamp]/8;
-       int pw=PAD(w, hsf), ph=PAD(h, vsf);
-       int cw=pw/hsf, ch=ph/vsf;
-       int ypitch=PAD(pw, pad), uvpitch=PAD(cw, pad);
-       int halfway=16, blocksize=8;
-
-       memset(buf, 0, tjBufSizeYUV2(w, pad, h, subsamp));
-
-       for(row=0; row<ph; row++)
-       {
-               for(col=0; col<pw; col++)
-               {
-                       unsigned char *y=&buf[ypitch*row+col];
-                       if(((row/blocksize)+(col/blocksize))%2==0)
-                       {
-                               if(row<halfway) *y=255;  else *y=0;
-                       }
-                       else
-                       {
-                               if(row<halfway) *y=76;  else *y=226;
-                       }
-               }
-       }
-       if(subsamp!=TJSAMP_GRAY)
-       {
-               halfway=16/vsf;
-               for(row=0; row<ch; row++)
-               {
-                       for(col=0; col<cw; col++)
-                       {
-                               unsigned char *u=&buf[ypitch*ph + (uvpitch*row+col)],
-                                       *v=&buf[ypitch*ph + uvpitch*ch + (uvpitch*row+col)];
-                               if(((row*vsf/blocksize)+(col*hsf/blocksize))%2==0)
-                                       *u=*v=128;
-                               else
-                               {
-                                       if(row<halfway)
-                                       {
-                                               *u=85;  *v=255;
-                                       }
-                                       else
-                                       {
-                                               *u=0;  *v=149;
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-
 int checkBufYUV(unsigned char *buf, int w, int h, int subsamp,
        tjscalingfactor sf)
 {
@@ -451,71 +397,56 @@ void compTest(tjhandle handle, unsigned char **dstBuf,
        unsigned long *dstSize, int w, int h, int pf, char *basename,
        int subsamp, int jpegQual, int flags)
 {
-       char tempStr[1024];  unsigned char *srcBuf=NULL;
-       const char *pfStr=(yuv==YUVDECODE)? "YUV":pixFormatStr[pf];
+       char tempStr[1024];  unsigned char *srcBuf=NULL, *yuvBuf=NULL;
+       const char *pfStr=pixFormatStr[pf];
        const char *buStrLong=(flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ";
        const char *buStr=(flags&TJFLAG_BOTTOMUP)? "BU":"TD";
 
-       if(yuv==YUVDECODE)
-       {
-               printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong,
-                       jpegQual);
-               if((srcBuf=(unsigned char *)malloc(tjBufSizeYUV2(w, pad, h, subsamp)))
-                       ==NULL)
-                       _throw("Memory allocation failure");
-               initBufYUV(srcBuf, w, pad, h, subsamp);
-       }
-       else
-       {
-               if(yuv==YUVENCODE)
-                       printf("%s %s -> %s YUV ... ", pfStr, buStrLong, subNameLong[subsamp]);
-               else
-                       printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp],
-                               jpegQual);
-               if((srcBuf=(unsigned char *)malloc(w*h*tjPixelSize[pf]))==NULL)
-                       _throw("Memory allocation failure");
-               initBuf(srcBuf, w, h, pf, flags);
-       }
+       if((srcBuf=(unsigned char *)malloc(w*h*tjPixelSize[pf]))==NULL)
+               _throw("Memory allocation failure");
+       initBuf(srcBuf, w, h, pf, flags);
 
        if(*dstBuf && *dstSize>0) memset(*dstBuf, 0, *dstSize);
 
-       if(yuv==YUVENCODE)
+
+       if(!alloc) flags|=TJFLAG_NOREALLOC;
+       if(doyuv)
        {
-               _tj(tjEncodeYUV3(handle, srcBuf, w, 0, h, pf, *dstBuf, pad, subsamp,
+               unsigned long yuvSize=tjBufSizeYUV2(w, pad, h, subsamp);
+               tjscalingfactor sf={1, 1};
+
+               if((yuvBuf=(unsigned char *)malloc(yuvSize))==NULL)
+                       _throw("Memory allocation failure");
+
+               printf("%s %s -> YUV %s ... ", pfStr, buStrLong, subNameLong[subsamp]);
+               _tj(tjEncodeYUV3(handle, srcBuf, w, 0, h, pf, yuvBuf, pad, subsamp,
                        flags));
+               snprintf(tempStr, 1024, "%s_enc_%s_%s_%s.yuv", basename, pfStr, buStr,
+                       subName[subsamp]);
+               writeJPEG(yuvBuf, yuvSize, tempStr);
+               if(checkBufYUV(yuvBuf, w, h, subsamp, sf)) printf("Passed.\n");
+               else printf("FAILED!\n");
+
+               printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong,
+                       jpegQual);
+               _tj(tjCompressFromYUV(handle, yuvBuf, w, pad, h, subsamp, dstBuf,
+                       dstSize, jpegQual, flags));
        }
        else
        {
-               if(!alloc) flags|=TJFLAG_NOREALLOC;
-               if(yuv==YUVDECODE)
-               {
-                       _tj(tjCompressFromYUV(handle, srcBuf, w, pad, h, subsamp, dstBuf,
-                               dstSize, jpegQual, flags));
-               }
-               else
-               {
-                       _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp,
-                               jpegQual, flags));
-               }
+               printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp],
+                       jpegQual);
+               _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp,
+                       jpegQual, flags));
        }
 
-       if(yuv==YUVENCODE)
-               snprintf(tempStr, 1024, "%s_enc_%s_%s_%s.yuv", basename, pfStr, buStr,
-                       subName[subsamp]);
-       else
-               snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, pfStr, buStr,
-                       subName[subsamp], jpegQual);
+       snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, pfStr, buStr,
+               subName[subsamp], jpegQual);
        writeJPEG(*dstBuf, *dstSize, tempStr);
-       if(yuv==YUVENCODE)
-       {
-               tjscalingfactor sf={1, 1};
-               if(checkBufYUV(*dstBuf, w, h, subsamp, sf)) printf("Passed.");
-               else printf("FAILED!");
-       }
-       else printf("Done.");
-       printf("\n  Result in %s\n", tempStr);
+       printf("Done.\n  Result in %s\n", tempStr);
 
        bailout:
+       if(yuvBuf) free(yuvBuf);
        if(srcBuf) free(srcBuf);
 }
 
@@ -524,61 +455,63 @@ void _decompTest(tjhandle handle, unsigned char *jpegBuf,
        unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp,
        int flags, tjscalingfactor sf)
 {
-       unsigned char *dstBuf=NULL;
+       unsigned char *dstBuf=NULL, *yuvBuf=NULL;
        int _hdrw=0, _hdrh=0, _hdrsubsamp=-1;
        int scaledWidth=TJSCALED(w, sf);
        int scaledHeight=TJSCALED(h, sf);
        unsigned long dstSize=0;
 
-       if(yuv==YUVENCODE) return;
-
-       if(yuv==YUVDECODE)
-               printf("JPEG -> YUV %s ", subNameLong[subsamp]);
-       else
-               printf("JPEG -> %s %s ", pixFormatStr[pf],
-                       (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ");
-       if(sf.num!=1 || sf.denom!=1)
-               printf("%d/%d ... ", sf.num, sf.denom);
-       else printf("... ");
-
        _tj(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh,
                &_hdrsubsamp));
        if(_hdrw!=w || _hdrh!=h || _hdrsubsamp!=subsamp)
                _throw("Incorrect JPEG header");
 
-       if(yuv==YUVDECODE)
-               dstSize=tjBufSizeYUV2(scaledWidth, pad, scaledHeight, subsamp);
-       else dstSize=scaledWidth*scaledHeight*tjPixelSize[pf];
+       dstSize=scaledWidth*scaledHeight*tjPixelSize[pf];
        if((dstBuf=(unsigned char *)malloc(dstSize))==NULL)
                _throw("Memory allocation failure");
        memset(dstBuf, 0, dstSize);
 
-       if(yuv==YUVDECODE)
+       if(doyuv)
        {
-               _tj(tjDecompressToYUV2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth,
+               unsigned long yuvSize=tjBufSizeYUV2(scaledWidth, pad, scaledHeight,
+                       subsamp);
+
+               if((yuvBuf=(unsigned char *)malloc(yuvSize))==NULL)
+                       _throw("Memory allocation failure");
+
+               printf("JPEG -> YUV %s ", subNameLong[subsamp]);
+               if(sf.num!=1 || sf.denom!=1)
+                       printf("%d/%d ... ", sf.num, sf.denom);
+               else printf("... ");
+               _tj(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth,
                        pad, scaledHeight, flags));
+               if(checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf))
+                       printf("Passed.\n");
+               else printf("FAILED!\n");
+
+               printf("YUV %s -> %s %s ... ", subNameLong[subsamp], pixFormatStr[pf],
+                       (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ");
+               _tj(tjDecodeYUV(handle, yuvBuf, pad, subsamp, dstBuf, scaledWidth, 0,
+                       scaledHeight, pf, flags));
        }
        else
        {
+               printf("JPEG -> %s %s ", pixFormatStr[pf],
+                       (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ");
+               if(sf.num!=1 || sf.denom!=1)
+                       printf("%d/%d ... ", sf.num, sf.denom);
+               else printf("... ");
                _tj(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0,
                        scaledHeight, pf, flags));
        }
 
-       if(yuv==YUVDECODE)
-       {
-               if(checkBufYUV(dstBuf, scaledWidth, scaledHeight, subsamp, sf))
-                       printf("Passed.");
-               else printf("FAILED!");
-       }
-       else
-       {
-               if(checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags))
-                       printf("Passed.");
-               else printf("FAILED!");
-       }
+       if(checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags))
+               printf("Passed.");
+       else printf("FAILED!");
        printf("\n");
 
        bailout:
+       if(yuvBuf) free(yuvBuf);
        if(dstBuf) free(dstBuf);
 }
 
@@ -614,9 +547,7 @@ void doTest(int w, int h, const int *formats, int nformats, int subsamp,
        unsigned char *dstBuf=NULL;
        unsigned long size=0;  int pfi, pf, i;
 
-       if(yuv==YUVENCODE)
-               size=tjBufSizeYUV2(w, pad, h, subsamp);
-       else if(!alloc)
+       if(!alloc)
                size=tjBufSize(w, h, subsamp);
        if(size!=0)
                if((dstBuf=(unsigned char *)tjAlloc(size))==NULL)
@@ -633,11 +564,7 @@ void doTest(int w, int h, const int *formats, int nformats, int subsamp,
                        if(subsamp==TJSAMP_422 || subsamp==TJSAMP_420 || subsamp==TJSAMP_440 ||
                                subsamp==TJSAMP_411)
                                flags|=TJFLAG_FASTUPSAMPLE;
-                       if(i==1)
-                       {
-                               if(yuv==YUVDECODE) goto bailout;
-                               else flags|=TJFLAG_BOTTOMUP;
-                       }
+                       if(i==1) flags|=TJFLAG_BOTTOMUP;
                        pf=formats[pfi];
                        compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, 100,
                                flags);
@@ -679,9 +606,9 @@ void bufSizeTest(void)
                                if(h%100==0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h);
                                if((srcBuf=(unsigned char *)malloc(w*h*4))==NULL)
                                        _throw("Memory allocation failure");
-                               if(!alloc || yuv==YUVENCODE)
+                               if(!alloc || doyuv)
                                {
-                                       if(yuv==YUVENCODE) dstSize=tjBufSizeYUV2(w, pad, h, subsamp);
+                                       if(doyuv) dstSize=tjBufSizeYUV2(w, pad, h, subsamp);
                                        else dstSize=tjBufSize(w, h, subsamp);
                                        if((dstBuf=(unsigned char *)tjAlloc(dstSize))==NULL)
                                                _throw("Memory allocation failure");
@@ -693,7 +620,7 @@ void bufSizeTest(void)
                                        else srcBuf[i]=255;
                                }
 
-                               if(yuv==YUVENCODE)
+                               if(doyuv)
                                {
                                        _tj(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, pad,
                                                subsamp, 0));
@@ -708,9 +635,9 @@ void bufSizeTest(void)
 
                                if((srcBuf=(unsigned char *)malloc(h*w*4))==NULL)
                                        _throw("Memory allocation failure");
-                               if(!alloc || yuv==YUVENCODE)
+                               if(!alloc || doyuv)
                                {
-                                       if(yuv==YUVENCODE) dstSize=tjBufSizeYUV2(h, pad, w, subsamp);
+                                       if(doyuv) dstSize=tjBufSizeYUV2(h, pad, w, subsamp);
                                        else dstSize=tjBufSize(h, w, subsamp);
                                        if((dstBuf=(unsigned char *)tjAlloc(dstSize))==NULL)
                                                _throw("Memory allocation failure");
@@ -722,7 +649,7 @@ void bufSizeTest(void)
                                        else srcBuf[i]=255;
                                }
 
-                               if(yuv==YUVENCODE)
+                               if(doyuv)
                                {
                                        _tj(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, pad,
                                                subsamp, 0));
@@ -748,7 +675,7 @@ void bufSizeTest(void)
 
 int main(int argc, char *argv[])
 {
-       int doyuv=0, i, num4bf=5;
+       int i, num4bf=5;
        #ifdef _WIN32
        srand((unsigned int)time(NULL));
        #endif
@@ -764,7 +691,7 @@ int main(int argc, char *argv[])
                }
        }
        if(alloc) printf("Testing automatic buffer allocation\n");
-       if(doyuv) {yuv=YUVENCODE;  num4bf=4;}
+       if(doyuv) num4bf=4;
        doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test");
        doTest(39, 41, _4byteFormats, num4bf, TJSAMP_444, "test");
        doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test");
@@ -782,21 +709,13 @@ int main(int argc, char *argv[])
        if(doyuv)
        {
                printf("\n--------------------\n\n");
-               yuv=YUVDECODE;
                doTest(48, 48, _onlyRGB, 1, TJSAMP_444, "test_yuv0");
-               doTest(35, 39, _onlyRGB, 1, TJSAMP_444, "test_yuv1");
                doTest(48, 48, _onlyRGB, 1, TJSAMP_422, "test_yuv0");
-               doTest(39, 41, _onlyRGB, 1, TJSAMP_422, "test_yuv1");
                doTest(48, 48, _onlyRGB, 1, TJSAMP_420, "test_yuv0");
-               doTest(41, 35, _onlyRGB, 1, TJSAMP_420, "test_yuv1");
                doTest(48, 48, _onlyRGB, 1, TJSAMP_440, "test_yuv0");
-               doTest(35, 39, _onlyRGB, 1, TJSAMP_440, "test_yuv1");
                doTest(48, 48, _onlyRGB, 1, TJSAMP_411, "test_yuv0");
-               doTest(39, 41, _onlyRGB, 1, TJSAMP_411, "test_yuv1");
                doTest(48, 48, _onlyRGB, 1, TJSAMP_GRAY, "test_yuv0");
-               doTest(41, 35, _onlyRGB, 1, TJSAMP_GRAY, "test_yuv1");
                doTest(48, 48, _onlyGray, 1, TJSAMP_GRAY, "test_yuv0");
-               doTest(35, 39, _onlyGray, 1, TJSAMP_GRAY, "test_yuv1");
        }
 
        return exitStatus;
index d8cf41c312881364f5416de7c40241f09a60cd00..7d174ca8b81f0d93e38ee7429ca1b21b33cb94cb 100755 (executable)
@@ -42,6 +42,7 @@ TURBOJPEG_1.4
        global:
                tjBufSizeYUV2;
                tjCompressFromYUV;
+               tjDecodeYUV;
                tjDecompressHeader3;
                tjDecompressToYUV2;
                tjEncodeYUV3;
index f90f9434d9a12a93695d37d34e39b0a85a56e71d..a1be1fd6159cc37d0045435be2b3a08728515899 100755 (executable)
@@ -68,6 +68,7 @@ TURBOJPEG_1.4
        global:
                tjBufSizeYUV2;
                tjCompressFromYUV;
+               tjDecodeYUV;
                tjDecompressHeader3;
                tjDecompressToYUV2;
                tjEncodeYUV3;
index 96a0fa9819e710ec41355f960bd4841bf4e67542..ee412c1c86dfd7612c4ada67bf57e4120887a482 100644 (file)
@@ -1232,6 +1232,220 @@ DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, unsigned char *jpegBuf,
 }
 
 
+static int setDecodeDefaults(struct jpeg_decompress_struct *dinfo,
+       int pixelFormat, int subsamp, int flags)
+{
+       dinfo->scale_num=dinfo->scale_denom=1;
+
+       if(subsamp==TJSAMP_GRAY)
+       {
+               dinfo->num_components=1;
+               dinfo->jpeg_color_space=JCS_GRAYSCALE;
+       }
+       else
+       {
+               dinfo->num_components=3;
+               dinfo->jpeg_color_space=JCS_YCbCr;
+       }
+
+       dinfo->comp_info=(jpeg_component_info *)
+               (*dinfo->mem->alloc_small)((j_common_ptr)dinfo, JPOOL_IMAGE,
+                       dinfo->num_components*SIZEOF(jpeg_component_info));
+
+       dinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8;
+       dinfo->comp_info[0].v_samp_factor=tjMCUHeight[subsamp]/8;
+       dinfo->cur_comp_info[0]=&dinfo->comp_info[0];
+       if(dinfo->num_components==3)
+       {
+               dinfo->comp_info[1].h_samp_factor=1;
+               dinfo->comp_info[1].v_samp_factor=1;
+               dinfo->cur_comp_info[1]=&dinfo->comp_info[1];
+               dinfo->comp_info[2].h_samp_factor=1;
+               dinfo->comp_info[2].v_samp_factor=1;
+               dinfo->cur_comp_info[2]=&dinfo->comp_info[2];
+       }
+
+       return 0;
+}
+
+
+int my_read_markers(j_decompress_ptr dinfo)
+{
+       return JPEG_REACHED_SOS;
+}
+
+void my_reset_marker_reader(j_decompress_ptr dinfo)
+{
+}
+
+DLLEXPORT int DLLCALL tjDecodeYUV(tjhandle handle, unsigned char *srcBuf,
+       int pad, int subsamp, unsigned char *dstBuf, int width, int pitch,
+       int height, int pixelFormat, int flags)
+{
+       int i, retval=0;  JSAMPROW *row_pointer=NULL;
+       JSAMPLE *_tmpbuf[MAX_COMPONENTS];
+       JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
+       int row, pw, ph, cw[MAX_COMPONENTS], ch[MAX_COMPONENTS], useMerged=0;
+       JSAMPLE *ptr=srcBuf;
+       unsigned long yuvsize=0;
+       jpeg_component_info *compptr;
+       #ifndef JCS_EXTENSIONS
+       unsigned char *rgbBuf=NULL;
+       #endif
+       JMETHOD(int, old_read_markers, (j_decompress_ptr));
+       JMETHOD(void, old_reset_marker_reader, (j_decompress_ptr));
+
+       getinstance(handle);
+
+       for(i=0; i<MAX_COMPONENTS; i++)
+       {
+               tmpbuf[i]=NULL;  _tmpbuf[i]=NULL;  inbuf[i]=NULL;
+       }
+
+       if((this->init&DECOMPRESS)==0)
+               _throw("tjDecodeYUV(): Instance has not been initialized for compression");
+
+       if(srcBuf==NULL || pad<0 || !isPow2(pad) || subsamp<0 || subsamp>=NUMSUBOPT
+               || dstBuf==NULL || width<=0 || pitch<0 || height<=0 || pixelFormat<0
+               || pixelFormat>=TJ_NUMPF)
+               _throw("tjDecodeYUV(): Invalid argument");
+
+       if(setjmp(this->jerr.setjmp_buffer))
+       {
+               /* If we get here, the JPEG code has signaled an error. */
+               retval=-1;
+               goto bailout;
+       }
+
+       if(pixelFormat==TJPF_CMYK)
+               _throw("tjDecodeYUV(): Cannot decode YUV images into CMYK pixels.");
+
+       if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
+
+       #ifndef JCS_EXTENSIONS
+       if(pixelFormat!=TJPF_GRAY)
+       {
+               rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE);
+               if(!rgbBuf) _throw("tjDecodeYUV(): Memory allocation failure");
+               srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
+               pitch=width*RGB_PIXELSIZE;
+       }
+       #endif
+
+       dinfo->image_width=width;
+       dinfo->image_height=height;
+
+       if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
+       else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
+       else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
+
+       yuvsize=tjBufSizeYUV2(width, pad, height, subsamp);
+       if(setDecodeDefaults(dinfo, pixelFormat, subsamp, flags)==-1)
+       {
+               retval=-1;  goto bailout;
+       }
+       old_read_markers=dinfo->marker->read_markers;
+       dinfo->marker->read_markers=my_read_markers;
+       old_reset_marker_reader=dinfo->marker->reset_marker_reader;
+       dinfo->marker->reset_marker_reader=my_reset_marker_reader;
+       jpeg_read_header(dinfo, TRUE);
+       dinfo->marker->read_markers=old_read_markers;
+       dinfo->marker->reset_marker_reader=old_reset_marker_reader;
+
+       if(setDecompDefaults(dinfo, pixelFormat, flags)==-1)
+       {
+               retval=-1;  goto bailout;
+       }
+       jpeg_calc_output_dimensions(dinfo);
+       if(flags&TJFLAG_FASTUPSAMPLE)
+       {
+               dinfo->do_fancy_upsampling=FALSE;
+               if((subsamp==TJSAMP_422 || subsamp==TJSAMP_420) && pixelFormat!=TJPF_GRAY)
+                       useMerged=1;
+       }
+       if(useMerged)
+               jinit_merged_upsampler(dinfo);
+       else
+       {
+               jinit_color_deconverter(dinfo);
+               jinit_upsampler(dinfo);
+               (*dinfo->cconvert->start_pass)(dinfo);
+       }
+       (*dinfo->upsample->start_pass)(dinfo);
+
+       pw=PAD(width, dinfo->max_h_samp_factor);
+       ph=PAD(height, dinfo->max_v_samp_factor);
+
+       if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
+
+       if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ph))==NULL)
+               _throw("tjDecodeYUV(): Memory allocation failure");
+       for(i=0; i<height; i++)
+       {
+               if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&dstBuf[(height-i-1)*pitch];
+               else row_pointer[i]=&dstBuf[i*pitch];
+       }
+       if(height<ph)
+               for(i=height; i<ph; i++) row_pointer[i]=row_pointer[height-1];
+
+       for(i=0; i<dinfo->num_components; i++)
+       {
+               compptr=&dinfo->comp_info[i];
+               _tmpbuf[i]=(JSAMPLE *)malloc(PAD(compptr->width_in_blocks*DCTSIZE, 16)
+                       * compptr->v_samp_factor + 16);
+               if(!_tmpbuf[i]) _throw("tjDecodeYUV(): Memory allocation failure");
+               tmpbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*compptr->v_samp_factor);
+               if(!tmpbuf[i]) _throw("tjDecodeYUV(): Memory allocation failure");
+               for(row=0; row<compptr->v_samp_factor; row++)
+               {
+                       unsigned char *_tmpbuf_aligned=
+                               (unsigned char *)PAD((size_t)_tmpbuf[i], 16);
+                       tmpbuf[i][row]=&_tmpbuf_aligned[
+                               PAD(compptr->width_in_blocks*DCTSIZE, 16) * row];
+               }
+               cw[i]=pw*compptr->h_samp_factor/dinfo->max_h_samp_factor;
+               ch[i]=ph*compptr->v_samp_factor/dinfo->max_v_samp_factor;
+               inbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ch[i]);
+               if(!inbuf[i]) _throw("tjDecodeYUV(): Memory allocation failure");
+               for(row=0; row<ch[i]; row++)
+               {
+                       inbuf[i][row]=ptr;
+                       ptr+=PAD(cw[i], pad);
+               }
+       }
+
+       if(yuvsize!=(unsigned long)(ptr-srcBuf))
+               _throw("tjDecodeYUV(): YUV image is not the correct size");
+
+       for(row=0; row<ph; row+=dinfo->max_v_samp_factor)
+       {
+               JDIMENSION inrow=0, outrow=0;
+               for(i=0, compptr=dinfo->comp_info; i<dinfo->num_components; i++, compptr++)
+                       jcopy_sample_rows(inbuf[i],
+                               row*compptr->v_samp_factor/dinfo->max_v_samp_factor, tmpbuf[i], 0,
+                               compptr->v_samp_factor, cw[i]);
+               (dinfo->upsample->upsample)(dinfo, tmpbuf, &inrow,
+                       dinfo->max_v_samp_factor, &row_pointer[row], &outrow,
+                       dinfo->max_v_samp_factor);
+       }
+       jpeg_abort_decompress(dinfo);
+
+       bailout:
+       if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
+       #ifndef JCS_EXTENSIONS
+       if(rgbBuf) free(rgbBuf);
+       #endif
+       if(row_pointer) free(row_pointer);
+       for(i=0; i<MAX_COMPONENTS; i++)
+       {
+               if(tmpbuf[i]!=NULL) free(tmpbuf[i]);
+               if(_tmpbuf[i]!=NULL) free(_tmpbuf[i]);
+               if(inbuf[i]!=NULL) free(inbuf[i]);
+       }
+       return retval;
+}
+
+
 DLLEXPORT int DLLCALL tjDecompressToYUV2(tjhandle handle,
        unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
        int width, int pad, int height, int flags)
index 918655f4b058e79a477ae43c46452bd41b6e9074..a082fa36d537dc65f4ea288a41ebbde99c59278c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2009-2013 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2009-2014 D. R. Commander.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -733,7 +733,7 @@ DLLEXPORT unsigned long DLLCALL tjBufSizeYUV2(int width, int pad, int height,
 
 /**
  * Encode an RGB or grayscale image into a YUV planar image.  This function
- * uses the accelerated color conversion routines in TurboJPEG's underlying
+ * uses the accelerated color conversion routines in the underlying
  * codec but does not execute any of the other steps in the JPEG compression
  * process.  The Y, U (Cb), and V (Cr) image planes are stored sequentially
  * into the destination buffer, and the size of each plane is determined by the
@@ -925,6 +925,60 @@ DLLEXPORT int DLLCALL tjDecompressToYUV2(tjhandle handle,
   int width, int pad, int height, int flags);
 
 
+/**
+ * Decode a YUV planar image into an RGB or grayscale image.  This function
+ * uses the accelerated color conversion routines in the underlying
+ * codec but does not execute any of the other steps in the JPEG decompression
+ * process.  The Y, U (Cb), and V (Cr) image planes should be stored
+ * sequentially in the source buffer, and the size of each plane is determined
+ * by the width and height of the source image, as well as the specified
+ * padding and level of chrominance subsampling.  If the chrominance components
+ * are subsampled along the horizontal dimension, then the width of the
+ * luminance plane should be padded to the nearest multiple of 2 in the input
+ * image (same goes for the height of the luminance plane, if the chrominance
+ * components are subsampled along the vertical dimension.)
+ * <p>
+ * NOTE: Technically, the JPEG format uses the YCbCr colorspace, but per the
+ * convention of the digital video community, the TurboJPEG API uses "YUV" to
+ * refer to an image format consisting of Y, Cb, and Cr image planes.
+ *
+ * @param handle a handle to a TurboJPEG decompressor or transformer instance
+ * @param srcBuf pointer to an image buffer containing a YUV planar image to be
+ *        decoded.  The size of this buffer should match the value returned
+ *        by #tjBufSizeYUV2() for the given image width, height, padding, and
+ *        level of chrominance subsampling.
+ * @param pad Use this parameter to specify that the width of each line in each
+ *        plane of the YUV source image is padded to the nearest multiple of
+ *        this number of bytes (must be a power of 2.)
+ * @param subsamp the level of chrominance subsampling used in the YUV source
+ *        image (see @ref TJSAMP "Chrominance subsampling options".)
+ * @param dstBuf pointer to an image buffer that will receive the decoded
+ *        image.  This buffer should normally be <tt>pitch * height</tt>
+ *        bytes in size, but the <tt>dstBuf</tt> pointer can also be used to
+ *        decode into a specific region of a larger buffer.
+ * @param width width (in pixels) of the source and destination images
+ * @param pitch bytes per line of the destination image.  Normally, this should
+ *        be <tt>width * #tjPixelSize[pixelFormat]</tt> if the destination
+ *        image is unpadded, or <tt>#TJPAD(width *
+ *        #tjPixelSize[pixelFormat])</tt> if each line of the destination
+ *        image should be padded to the nearest 32-bit boundary, as is the case
+ *        for Windows bitmaps.  You can also be clever and use the pitch
+ *        parameter to skip lines, etc.  Setting this parameter to 0 is the
+ *        equivalent of setting it to <tt>width *
+ *        #tjPixelSize[pixelFormat]</tt>.
+ * @param height height (in pixels) of the source and destination images
+ * @param pixelFormat pixel format of the destination image (see @ref TJPF
+ *        "Pixel formats".)
+ * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
+ *        "flags".
+ *
+ * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
+ */
+DLLEXPORT int DLLCALL tjDecodeYUV(tjhandle handle, unsigned char *srcBuf,
+       int pad, int subsamp, unsigned char *dstBuf, int width, int pitch,
+       int height, int pixelFormat, int flags);
+
+
 /**
  * Create a new TurboJPEG transformer instance.
  *