diff --git a/ChangeLog.txt b/ChangeLog.txt index 4236dfe0..2ee2ca54 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -20,6 +20,9 @@ decompressed to CMYK destination images. Conversion between CMYK and RGB 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. + 1.3.1 ===== diff --git a/Makefile.am b/Makefile.am index 6096049b..a754fa37 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,6 +207,7 @@ endif ./tjunittest ./tjunittest -alloc ./tjunittest -yuv + ./tjunittest -yuv -alloc ./tjunittest -yuv -noyuvpad endif ./cjpeg -dct int -outfile testoutint.jpg $(srcdir)/testimages/testorig.ppm diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index c1df1939..d0c1303a 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -240,6 +240,9 @@ Functions DLLEXPORT int DLLCALL tjCompress2 (tjhandle handle, unsigned char *srcBuf, int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)  Compress an RGB, grayscale, or CMYK image into a JPEG image. More...
  +DLLEXPORT int DLLCALL tjCompressFromYUV (tjhandle handle, unsigned char *srcBuf, int width, int pad, int height, int subsamp, unsigned char **jpegBuf, unsigned long *jpegSize, int jpegQual, int flags) + Compress a YUV planar image into a JPEG image. More...
+  DLLEXPORT unsigned long DLLCALL tjBufSize (int width, int height, int jpegSubsamp)  The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters. More...
  @@ -1054,6 +1057,103 @@ 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 tjCompressFromYUV (tjhandle handle,
unsigned char * srcBuf,
int width,
int pad,
int height,
int subsamp,
unsigned char ** jpegBuf,
unsigned long * jpegSize,
int jpegQual,
int flags 
)
+
+ +

Compress a YUV planar image into a JPEG image.

+
Parameters
+ + + + + + + + + + + +
handlea handle to a TurboJPEG compressor or transformer instance
srcBufpointer to an image buffer containing a YUV planar image to be compressed. The Y, U (Cb), and V (Cr) image planes should be stored sequentially in the buffer, and the size of each plane is determined by the specified width, height, 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 (same goes for the height of the luminance plane, if the chrominance components are subsampled along the vertical dimension.) This is irrespective of any additional padding specified in the pad parameter.
widthwidth (in pixels) of the source image
padthe line padding used in the source image. For instance, if each line in each plane of the YUV image is padded to the nearest multiple of 4 bytes, then pad should be set to 4.
heightheight (in pixels) of the source image
subsampthe level of chrominance subsampling used in the source image (see Chrominance subsampling options.)
jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
    +
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. +
  3. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  4. +
  5. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees this.)
  6. +
+If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed.
jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.)
jpegQualthe image quality of the generated JPEG image (1 = worst, 100 = best)
flagsthe bitwise OR of one or more of the flags.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr().)
+
diff --git a/doc/html/search/all_74.js b/doc/html/search/all_74.js index 4aec5286..a32ccde8 100644 --- a/doc/html/search/all_74.js +++ b/doc/html/search/all_74.js @@ -9,6 +9,7 @@ var searchData= ['tjbufsize',['tjBufSize',['../group___turbo_j_p_e_g.html#gaccc5bca7f12fcdcc302e6e1c6d4b311b',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']]], ['tjcs',['TJCS',['../group___turbo_j_p_e_g.html#ga4f83ad3368e0e29d1957be0efa7c3720',1,'turbojpeg.h']]], ['tjcs_5fcmyk',['TJCS_CMYK',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a6c8b636152ac8195b869587db315ee53',1,'turbojpeg.h']]], ['tjcs_5fgray',['TJCS_GRAY',['../group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720ab3e7d6a87f695e45b81c1b5262b5a50a',1,'turbojpeg.h']]], @@ -65,7 +66,7 @@ var searchData= ['tjsamp_5fgray',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]], ['tjscaled',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]], ['tjscalingfactor',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]], - ['tjtransform',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#gaa29f3189c41be12ec5dee7caec318a31',1,'tjtransform(): turbojpeg.h'],['../group___turbo_j_p_e_g.html#gae403193ceb4aafb7e0f56ab587b48616',1,'tjTransform(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h']]], + ['tjtransform',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#gae403193ceb4aafb7e0f56ab587b48616',1,'tjTransform(tjhandle handle, unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags): turbojpeg.h'],['../group___turbo_j_p_e_g.html#gaa29f3189c41be12ec5dee7caec318a31',1,'tjtransform(): turbojpeg.h']]], ['tjxop',['TJXOP',['../group___turbo_j_p_e_g.html#ga2de531af4e7e6c4f124908376b354866',1,'turbojpeg.h']]], ['tjxop_5fhflip',['TJXOP_HFLIP',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aa0df69776caa30f0fa28e26332d311ce',1,'turbojpeg.h']]], ['tjxop_5fnone',['TJXOP_NONE',['../group___turbo_j_p_e_g.html#gga2de531af4e7e6c4f124908376b354866aad88c0366cd3f7d0eac9d7a3fa1c2c27',1,'turbojpeg.h']]], diff --git a/doc/html/search/functions_74.js b/doc/html/search/functions_74.js index 8f92c26d..4a42d2aa 100644 --- a/doc/html/search/functions_74.js +++ b/doc/html/search/functions_74.js @@ -4,6 +4,7 @@ var searchData= ['tjbufsize',['tjBufSize',['../group___turbo_j_p_e_g.html#gaccc5bca7f12fcdcc302e6e1c6d4b311b',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']]], ['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 b738c6c2..77ccb5da 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -295,6 +295,60 @@ 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 %s YUV ... ", pixFormatStr[pf], - (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", subNameLong[subsamp]); + if(yuv==YUVDECODE) + { + printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", jpegQual); + if((srcBuf=(unsigned char *)malloc(tjBufSizeYUV2(w, pad, h, subsamp))) + ==NULL) + _throw("Memory allocation failure"); + initBufYUV(srcBuf, w, pad, h, subsamp); + } else - printf("%s %s -> %s Q%d ... ", pixFormatStr[pf], - (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", subNameLong[subsamp], - jpegQual); + { + if(yuv==YUVENCODE) + printf("%s %s -> %s YUV ... ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", subNameLong[subsamp]); + else + printf("%s %s -> %s Q%d ... ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", 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); t=gettime(); @@ -421,14 +487,17 @@ void compTest(tjhandle handle, unsigned char **dstBuf, } else { - if(!alloc) + if(!alloc) flags|=TJFLAG_NOREALLOC; + if(yuv==YUVDECODE) { - flags|=TJFLAG_NOREALLOC; - *dstSize=(yuv==YUVENCODE? tjBufSizeYUV2(w, pad, h, subsamp) - : tjBufSize(w, h, subsamp)); + _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)); } - _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp, - jpegQual, flags)); } t=gettime()-t; @@ -550,13 +619,13 @@ 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(!alloc) - { - size=(yuv==YUVENCODE? tjBufSizeYUV2(w, pad, h, subsamp) - : tjBufSize(w, h, subsamp)); + if(yuv==YUVENCODE) + size=tjBufSizeYUV2(w, pad, h, subsamp); + else if(!alloc) + size=tjBufSize(w, h, subsamp); + if(size!=0) if((dstBuf=(unsigned char *)tjAlloc(size))==NULL) _throw("Memory allocation failure."); - } if((chandle=tjInitCompress())==NULL || (dhandle=tjInitDecompress())==NULL) _throwtj(); @@ -684,7 +753,7 @@ int main(int argc, char *argv[]) } } if(alloc) printf("Testing automatic buffer allocation\n"); - if(doyuv) {yuv=YUVENCODE; alloc=0; num4bf=4;} + if(doyuv) {yuv=YUVENCODE; 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"); diff --git a/turbojpeg-mapfile b/turbojpeg-mapfile index 46e6ca74..d8cf41c3 100755 --- a/turbojpeg-mapfile +++ b/turbojpeg-mapfile @@ -41,6 +41,7 @@ TURBOJPEG_1.4 { global: tjBufSizeYUV2; + tjCompressFromYUV; tjDecompressHeader3; tjDecompressToYUV2; tjEncodeYUV3; diff --git a/turbojpeg-mapfile.jni b/turbojpeg-mapfile.jni index 7a5f98e8..7b050cd2 100755 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -67,6 +67,7 @@ TURBOJPEG_1.4 { global: tjBufSizeYUV2; + tjCompressFromYUV; tjDecompressHeader3; tjDecompressToYUV2; tjEncodeYUV3; diff --git a/turbojpeg.c b/turbojpeg.c index bd37f575..d4ee84a8 100644 --- a/turbojpeg.c +++ b/turbojpeg.c @@ -861,6 +861,133 @@ DLLEXPORT int DLLCALL tjEncodeYUV(tjhandle handle, unsigned char *srcBuf, } +DLLEXPORT int DLLCALL tjCompressFromYUV(tjhandle handle, unsigned char *srcBuf, + int width, int pad, int height, int subsamp, unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, int flags) +{ + int i, row, retval=0, alloc=1; JSAMPROW *inbuf[MAX_COMPONENTS]; + int cw[MAX_COMPONENTS], ch[MAX_COMPONENTS], iw[MAX_COMPONENTS], + tmpbufsize=0, usetmpbuf=0, th[MAX_COMPONENTS]; + JSAMPLE *_tmpbuf=NULL, *ptr=srcBuf; JSAMPROW *tmpbuf[MAX_COMPONENTS]; + + for(i=0; iinit&COMPRESS)==0) + _throw("tjCompressFromYUV(): Instance has not been initialized for compression"); + + if(srcBuf==NULL || width<=0 || pad<1 || height<=0 || subsamp<0 + || subsamp>=NUMSUBOPT || jpegBuf==NULL || jpegSize==NULL || jpegQual<0 + || jpegQual>100) + _throw("tjCompressFromYUV(): Invalid argument"); + + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ + retval=-1; + goto bailout; + } + + cinfo->image_width=width; + cinfo->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"); + + if(flags&TJFLAG_NOREALLOC) + { + alloc=0; *jpegSize=tjBufSize(width, height, subsamp); + } + jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc); + if(setCompDefaults(cinfo, TJPF_RGB, subsamp, jpegQual, flags)==-1) + return -1; + cinfo->raw_data_in=TRUE; + + jpeg_start_compress(cinfo, TRUE); + for(i=0; inum_components; i++) + { + jpeg_component_info *compptr=&cinfo->comp_info[i]; + int ih; + iw[i]=compptr->width_in_blocks*DCTSIZE; + ih=compptr->height_in_blocks*DCTSIZE; + cw[i]=PAD(cinfo->image_width, cinfo->max_h_samp_factor) + *compptr->h_samp_factor/cinfo->max_h_samp_factor; + ch[i]=PAD(cinfo->image_height, cinfo->max_v_samp_factor) + *compptr->v_samp_factor/cinfo->max_v_samp_factor; + if(iw[i]!=cw[i] || ih!=ch[i]) usetmpbuf=1; + th[i]=compptr->v_samp_factor*DCTSIZE; + tmpbufsize+=iw[i]*th[i]; + if((inbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ch[i]))==NULL) + _throw("tjCompressFromYUV(): Memory allocation failure"); + for(row=0; rownum_components; i++) + { + if((tmpbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*th[i]))==NULL) + _throw("tjCompressFromYUV(): Memory allocation failure"); + for(row=0; rowimage_height; + row+=cinfo->max_v_samp_factor*DCTSIZE) + { + JSAMPARRAY yuvptr[MAX_COMPONENTS]; + int crow[MAX_COMPONENTS]; + for(i=0; inum_components; i++) + { + jpeg_component_info *compptr=&cinfo->comp_info[i]; + crow[i]=row*compptr->v_samp_factor/cinfo->max_v_samp_factor; + if(usetmpbuf) + { + int j, k; + for(j=0; jmax_v_samp_factor*DCTSIZE); + } + jpeg_finish_compress(cinfo); + + bailout: + if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo); + for(i=0; iimage_width; jpegheight=dinfo->image_height; if(width==0) width=jpegwidth; diff --git a/turbojpeg.h b/turbojpeg.h index 77019e0e..b6597f1c 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -655,6 +655,60 @@ DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf, unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags); +/** + * Compress a YUV planar image into a JPEG image. + * + * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param srcBuf pointer to an image buffer containing a YUV planar image + * to be compressed. The Y, U (Cb), and V (Cr) image planes should be + * stored sequentially in the buffer, and the size of each plane + * is determined by the specified width, height, 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 (same + * goes for the height of the luminance plane, if the chrominance + * components are subsampled along the vertical dimension.) This is + * irrespective of any additional padding specified in the pad + * parameter. + * @param width width (in pixels) of the source image + * @param pad the line padding used in the source image. For instance, if each + * line in each plane of the YUV image is padded to the nearest multiple + * of 4 bytes, then pad should be set to 4. + * @param height height (in pixels) of the source image + * @param subsamp the level of chrominance subsampling used in the source + * image (see @ref TJSAMP "Chrominance subsampling options".) + * @param jpegBuf address of a pointer to an image buffer that will receive the + * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer + * to accommodate the size of the JPEG image. Thus, you can choose to: + * -# pre-allocate the JPEG buffer with an arbitrary size using + * #tjAlloc() and let TurboJPEG grow the buffer as needed, + * -# set *jpegBuf to NULL to tell TurboJPEG to allocate the + * buffer for you, or + * -# pre-allocate the buffer to a "worst case" size determined by + * calling #tjBufSize(). This should ensure that the buffer never has + * to be re-allocated (setting #TJFLAG_NOREALLOC guarantees this.) + * . + * If you choose option 1, *jpegSize should be set to the + * size of your pre-allocated buffer. In any case, unless you have + * set #TJFLAG_NOREALLOC, you should always check *jpegBuf upon + * return from this function, as it may have changed. + * @param jpegSize pointer to an unsigned long variable that holds the size of + * the JPEG image buffer. If *jpegBuf points to a + * pre-allocated buffer, then *jpegSize should be set to the + * size of the buffer. Upon return, *jpegSize will contain the + * size of the JPEG image (in bytes.) + * @param jpegQual the image quality of the generated JPEG image (1 = worst, + 100 = best) + * @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 tjCompressFromYUV(tjhandle handle, unsigned char *srcBuf, + int width, int pad, int height, int subsamp, unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegQual, int flags); + + /** * The maximum size of the buffer (in bytes) required to hold a JPEG image with * the given parameters. The number of bytes returned by this function is