#define PAD(v, p) ((v+(p)-1)&(~((p)-1)))
int checkbufyuv(unsigned char *buf, unsigned long size, int w, int h,
- int subsamp, int decode)
+ int subsamp)
{
int i, j;
int hsf=_hsf[subsamp], vsf=_vsf[subsamp];
int pw=PAD(w, hsf), ph=PAD(h, vsf);
- int cw=PAD(pw/hsf, decode? 8:1), ch=PAD(ph/vsf, decode? 8:1);
- int ypitch=PAD(pw, decode? 8:4), uvpitch=PAD(cw, decode? 8:4);
+ int cw=pw/hsf, ch=ph/vsf;
+ int ypitch=PAD(pw, 4), uvpitch=PAD(cw, 4);
int retval=1;
unsigned long correctsize=ypitch*ph + (subsamp==TJ_GRAYSCALE? 0:uvpitch*ch*2);
writejpeg(jpegbuf, *size, tempstr);
if(yuv==YUVENCODE)
{
- if(checkbufyuv(jpegbuf, *size, w, h, subsamp, 0)) printf("Passed.");
+ if(checkbufyuv(jpegbuf, *size, w, h, subsamp)) printf("Passed.");
else printf("FAILED!");
}
else printf("Done.");
const char *pixformat; int _w=0, _h=0; double t;
unsigned long size=0;
int hsf=_hsf[subsamp], vsf=_vsf[subsamp];
- int pw=PAD(w, 8), ph=PAD(h, 8), cw=PAD(pw/hsf, 8), ch=PAD(ph/vsf, 8);
+ int pw=PAD(w, hsf), ph=PAD(h, vsf);
+ int cw=pw/hsf, ch=ph/vsf;
+ int ypitch=PAD(pw, 4), uvpitch=PAD(cw, 4);
if(yuv==YUVDECODE) flags|=TJ_YUV;
else if(yuv==YUVENCODE) return;
}
if(yuv==YUVDECODE)
- size=pw*ph + (subsamp==TJ_GRAYSCALE? 0:cw*ch*(ps-1));
+ size=ypitch*ph + (subsamp==TJ_GRAYSCALE? 0:uvpitch*ch*2);
else
size=w*h*ps;
if((bmpbuf=(unsigned char *)malloc(size+1))==NULL)
if(yuv==YUVDECODE)
{
- if(checkbufyuv(bmpbuf, size, pw, ph, subsamp, 1))
+ if(checkbufyuv(bmpbuf, size, pw, ph, subsamp))
printf("Passed.");
else printf("FAILED!");
}
if(doyuv)
{
yuv=YUVDECODE;
+ dotest(48, 48, 3, TJ_444, "test");
dotest(35, 39, 3, TJ_444, "test");
+ dotest(48, 48, 1, TJ_GRAYSCALE, "test");
dotest(39, 41, 1, TJ_GRAYSCALE, "test");
}
int ps=_ps[pf];
int pitch=w*ps, yuvsize;
int hsf=_hsf[jpegsub], vsf=_vsf[jpegsub];
- int pw=PAD(w, 8), ph=PAD(h, 8), cw=PAD(pw/hsf, 8), ch=PAD(ph/vsf, 8);
+ int pw=PAD(w, hsf), ph=PAD(h, vsf);
+ int cw=pw/hsf, ch=ph/vsf;
+ int ypitch=PAD(pw, 4), uvpitch=PAD(cw, 4);
flags |= _flags[pf];
if(bu) flags |= TJ_BOTTOMUP;
if(yuv==YUVENCODE) flags |= TJ_YUV;
- yuvsize=pw*ph + (jpegsub==TJ_GRAYSCALE? 0:cw*ch*2);
+ yuvsize=ypitch*ph + (jpegsub==TJ_GRAYSCALE? 0:uvpitch*ch*2);
if((rgbbuf=(unsigned char *)malloc(max(yuvsize, pitch*h))) == NULL)
_throwunix("allocating image buffer");
#define TJ_YUV 512
/* If passed to tjCompress(), this causes TurboJPEG/OSS to use the
accelerated color conversion routines in libjpeg-turbo to produce a planar
- YUV image that is suitable for X Video. Specifically, if a component is
- subsampled along the horizontal dimension, then the width of the plane for
- that component is padded to 2 in the output image (same goes for the
- height, if the component is subsampled along the vertical dimension.)
- Also, each line of each plane in the output image is padded to 4 bytes.
- Although this will work with any subsampling option, it is really only
- useful in combination with TJ_420, which produces an image compatible with
- the I420 (AKA "YUV420P") format.
+ YUV image that is suitable for X Video. Specifically, if the chrominance
+ components are subsampled along the horizontal dimension, then the width
+ of the luminance plane is padded to 2 in the output image (same goes for
+ the height of the luminance plane, if the chrominance components are
+ subsampled along the vertical dimension.) Also, each line of each plane
+ in the output image is padded to 4 bytes. Although this will work with
+ any subsampling option, it is really only useful in combination with
+ TJ_420, which produces an image compatible with the I420 (AKA "YUV420P")
+ format.
If passed to tjDecompress(), this tells TurboJPEG/OSS to perform JPEG
decompression but to leave out the color conversion step, so a planar YUV
- image is generated instead of an RGB image. In this case, the width and
- height of all planes are padded to 8 in the output image.
+ image is generated instead of an RGB image. The padding of the planes in
+ this image is the same as in the above case. Note that, if the width or
+ height of the output image is not a multiple of 8 (or a multiple of 16
+ along any dimension in which chrominance subsampling is used), then an
+ intermediate buffer copy will be performed within TurboJPEG/OSS.
*/
typedef void* tjhandle;
int flags)
{
int i, row; JSAMPROW *row_pointer=NULL, *outbuf[MAX_COMPONENTS];
- int cw[MAX_COMPONENTS], ch[MAX_COMPONENTS];
+ int cw[MAX_COMPONENTS], ch[MAX_COMPONENTS], iw[MAX_COMPONENTS],
+ tmpbufsize=0, usetmpbuf=0, th[MAX_COMPONENTS];
+ JSAMPLE *_tmpbuf=NULL; JSAMPROW *tmpbuf[MAX_COMPONENTS];
checkhandle(h);
- for(i=0; i<MAX_COMPONENTS; i++) outbuf[i]=NULL;
+ for(i=0; i<MAX_COMPONENTS; i++)
+ {
+ tmpbuf[i]=NULL; outbuf[i]=NULL;
+ }
if(srcbuf==NULL || size<=0
|| dstbuf==NULL || width<=0 || pitch<0 || height<=0)
if(setjmp(j->jerr.jb))
{ // this will execute if LIBJPEG has an error
for(i=0; i<MAX_COMPONENTS; i++)
+ {
+ if(tmpbuf[i]!=NULL) free(tmpbuf[i]);
if(outbuf[i]!=NULL) free(outbuf[i]);
+ }
+ if(_tmpbuf) free(_tmpbuf);
if(row_pointer) free(row_pointer);
return -1;
}
for(i=0; i<dinfo->num_components; i++)
{
jpeg_component_info *compptr=&dinfo->comp_info[i];
- cw[i]=compptr->width_in_blocks*DCTSIZE;
- ch[i]=compptr->height_in_blocks*DCTSIZE;
+ int ih;
+ iw[i]=compptr->width_in_blocks*DCTSIZE;
+ ih=compptr->height_in_blocks*DCTSIZE;
+ cw[i]=PAD(width, dinfo->max_h_samp_factor)*compptr->h_samp_factor
+ /dinfo->max_h_samp_factor;
+ ch[i]=PAD(height, dinfo->max_v_samp_factor)*compptr->v_samp_factor
+ /dinfo->max_v_samp_factor;
+ if(iw[i]!=cw[i] || ih!=ch[i])
+ {
+ usetmpbuf=1;
+ th[i]=compptr->v_samp_factor*DCTSIZE/dinfo->max_v_samp_factor;
+ tmpbufsize+=iw[i]*th[i];
+ }
if((outbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ch[i]))==NULL)
_throw("Memory allocation failed in tjInitDecompress()");
for(row=0; row<ch[i]; row++)
{
outbuf[i][row]=ptr;
- ptr+=cw[i];
+ ptr+=PAD(cw[i], 4);
+ }
+ }
+ if(usetmpbuf)
+ {
+ if((_tmpbuf=(JSAMPLE *)malloc(sizeof(JSAMPLE)*tmpbufsize))==NULL)
+ _throw("Memory allocation failed in tjInitDecompress()");
+ ptr=_tmpbuf;
+ for(i=0; i<dinfo->num_components; i++)
+ {
+ jpeg_component_info *compptr=&dinfo->comp_info[i];
+ if((tmpbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*th[i]))==NULL)
+ _throw("Memory allocation failed in tjInitDecompress()");
+ for(row=0; row<th[i]; row++)
+ {
+ tmpbuf[i][row]=ptr;
+ ptr+=iw[i];
+ }
}
}
}
jpeg_start_decompress(&j->dinfo);
if(flags&TJ_YUV)
{
- for(row=0; row<j->dinfo.output_height;
- row+=j->dinfo.max_v_samp_factor*DCTSIZE)
+ j_decompress_ptr dinfo=&j->dinfo;
+ for(row=0; row<dinfo->output_height;
+ row+=dinfo->max_v_samp_factor*DCTSIZE)
{
JSAMPARRAY yuvptr[MAX_COMPONENTS];
- for(i=0; i<j->dinfo.num_components; i++)
+ for(i=0; i<dinfo->num_components; i++)
{
- jpeg_component_info *compptr=&j->dinfo.comp_info[i];
- yuvptr[i]=&outbuf[i][row*compptr->v_samp_factor/j->dinfo.max_v_samp_factor];
+ jpeg_component_info *compptr=&dinfo->comp_info[i];
+ if(usetmpbuf) yuvptr[i]=tmpbuf[i];
+ else yuvptr[i]=&outbuf[i][row*compptr->v_samp_factor
+ /dinfo->max_v_samp_factor];
+ }
+ jpeg_read_raw_data(dinfo, yuvptr, dinfo->max_v_samp_factor*DCTSIZE);
+ if(usetmpbuf)
+ {
+ int j;
+ for(i=0; i<dinfo->num_components; i++)
+ {
+ jpeg_component_info *compptr=&dinfo->comp_info[i];
+ for(j=0; j<min(th[i], dinfo->output_height-row); j++)
+ {
+ memcpy(outbuf[i][row*compptr->v_samp_factor
+ /dinfo->max_v_samp_factor+j], tmpbuf[i][j], cw[i]);
+ }
+ }
}
- jpeg_read_raw_data(&j->dinfo, yuvptr, j->dinfo.max_v_samp_factor*DCTSIZE);
}
}
else
jpeg_finish_decompress(&j->dinfo);
for(i=0; i<MAX_COMPONENTS; i++)
+ {
+ if(tmpbuf[i]) free(tmpbuf[i]);
if(outbuf[i]) free(outbuf[i]);
+ }
+ if(_tmpbuf) free(_tmpbuf);
if(row_pointer) free(row_pointer);
return 0;
}