From 34dca052271f4a75b3c0f7b11a2c5024159628d4 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 28 Feb 2014 09:17:14 +0000 Subject: [PATCH] Implement a YUV decode function in the TurboJPEG API, to be symmetric with tjEncodeYUV(). git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@1132 632fc199-4ca6-4c93-a231-07263d6284db --- ChangeLog.txt | 3 +- doc/html/group___turbo_j_p_e_g.html | 99 +++++++++++- doc/html/search/all_74.js | 1 + doc/html/search/functions_74.js | 1 + tjunittest.c | 233 +++++++++------------------- turbojpeg-mapfile | 1 + turbojpeg-mapfile.jni | 1 + turbojpeg.c | 214 +++++++++++++++++++++++++ turbojpeg.h | 58 ++++++- 9 files changed, 450 insertions(+), 161 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 2ca9839..ac3db63 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -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 diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 2c3395b..28e4926 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -255,6 +255,9 @@ Functions DLLEXPORT int DLLCALL tjDecompressToYUV2 (tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, int width, int pad, int height, int flags)  Decompress a JPEG image to a YUV planar image. More...
  +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) + Decode a YUV planar image into an RGB or grayscale image. More...
+  DLLEXPORT tjhandle DLLCALL tjInitTransform (void)  Create a new TurboJPEG transformer instance. More...
  @@ -1086,6 +1089,100 @@ If you choose option 1, *jpegSize should be set to the size of your
Returns
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 
)
+
+ +

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.)

+

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.

+
Parameters
+ + + + + + + + + + + +
handlea handle to a TurboJPEG decompressor or transformer instance
srcBufpointer 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.
padUse 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.)
subsampthe level of chrominance subsampling used in the YUV source image (see Chrominance subsampling options.)
dstBufpointer to an image buffer that will receive the decoded image. This buffer should normally be pitch * height bytes in size, but the dstBuf pointer can also be used to decode into a specific region of a larger buffer.
widthwidth (in pixels) of the source and destination images
pitchbytes per line of the destination image. Normally, this should be width * tjPixelSize[pixelFormat] if the destination image is unpadded, or TJPAD(width * tjPixelSize[pixelFormat]) 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 width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the source and destination images
pixelFormatpixel format of the destination image (see Pixel formats.)
flagsthe bitwise OR of one or more of the flags.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr().)
+
@@ -1422,7 +1519,7 @@ If you choose option 1, *jpegSize should be set to the size of your

Encode an RGB or grayscale image into a YUV planar image.

-

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.)

+

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.)

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.

Parameters
diff --git a/doc/html/search/all_74.js b/doc/html/search/all_74.js index 21e0ced..435cec4 100644 --- a/doc/html/search/all_74.js +++ b/doc/html/search/all_74.js @@ -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']]], diff --git a/doc/html/search/functions_74.js b/doc/html/search/functions_74.js index 4a42d2a..0a0e6cd 100644 --- a/doc/html/search/functions_74.js +++ b/doc/html/search/functions_74.js @@ -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']]], diff --git a/tjunittest.c b/tjunittest.c index 6b14db1..8bdd721 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -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 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; diff --git a/turbojpeg-mapfile b/turbojpeg-mapfile index d8cf41c..7d174ca 100755 --- a/turbojpeg-mapfile +++ b/turbojpeg-mapfile @@ -42,6 +42,7 @@ TURBOJPEG_1.4 global: tjBufSizeYUV2; tjCompressFromYUV; + tjDecodeYUV; tjDecompressHeader3; tjDecompressToYUV2; tjEncodeYUV3; diff --git a/turbojpeg-mapfile.jni b/turbojpeg-mapfile.jni index f90f943..a1be1fd 100755 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -68,6 +68,7 @@ TURBOJPEG_1.4 global: tjBufSizeYUV2; tjCompressFromYUV; + tjDecodeYUV; tjDecompressHeader3; tjDecompressToYUV2; tjEncodeYUV3; diff --git a/turbojpeg.c b/turbojpeg.c index 96a0fa9..ee412c1 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -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; iinit&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; inum_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; rowv_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; rowmax_v_samp_factor) + { + JDIMENSION inrow=0, outrow=0; + for(i=0, compptr=dinfo->comp_info; inum_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 + * 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 pitch * height + * bytes in size, but the dstBuf 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 width * #tjPixelSize[pixelFormat] if the destination + * image is unpadded, or #TJPAD(width * + * #tjPixelSize[pixelFormat]) 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 width * + * #tjPixelSize[pixelFormat]. + * @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. * -- 2.40.0