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
<tr class="memitem:ga7c08b340ad7f8e85d407bd9e81d44d07"><td class="memItemLeft" align="right" valign="top">DLLEXPORT int DLLCALL </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"> </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"> </td></tr>
+<tr class="memitem:ga132ae2c2cadcf64c8bb0f3bdf69da3ed"><td class="memItemLeft" align="right" valign="top">DLLEXPORT int DLLCALL </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"> </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"> </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 </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"> </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"> </td></tr>
</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> </td>
+ <td class="paramname"><em>handle</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">unsigned char * </td>
+ <td class="paramname"><em>srcBuf</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>pad</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>subsamp</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">unsigned char * </td>
+ <td class="paramname"><em>dstBuf</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>width</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>pitch</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>height</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>pixelFormat</em>, </td>
+ </tr>
+ <tr>
+ <td class="paramkey"></td>
+ <td></td>
+ <td class="paramtype">int </td>
+ <td class="paramname"><em>flags</em> </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>
</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">
['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']]],
['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']]],
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;}
#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)
{
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);
}
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);
}
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)
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);
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");
else srcBuf[i]=255;
}
- if(yuv==YUVENCODE)
+ if(doyuv)
{
_tj(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, pad,
subsamp, 0));
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");
else srcBuf[i]=255;
}
- if(yuv==YUVENCODE)
+ if(doyuv)
{
_tj(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, pad,
subsamp, 0));
int main(int argc, char *argv[])
{
- int doyuv=0, i, num4bf=5;
+ int i, num4bf=5;
#ifdef _WIN32
srand((unsigned int)time(NULL));
#endif
}
}
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");
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;
global:
tjBufSizeYUV2;
tjCompressFromYUV;
+ tjDecodeYUV;
tjDecompressHeader3;
tjDecompressToYUV2;
tjEncodeYUV3;
global:
tjBufSizeYUV2;
tjCompressFromYUV;
+ tjDecodeYUV;
tjDecompressHeader3;
tjDecompressToYUV2;
tjEncodeYUV3;
}
+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)
/*
- * 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:
/**
* 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
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.
*