commit
413ca0dfef
@ -0,0 +1,849 @@
|
||||
/*
|
||||
* Copyright (C)2009-2012 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* TurboJPEG/OSS: this implements the TurboJPEG API using libjpeg-turbo */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef JCS_EXTENSIONS
|
||||
#define JPEG_INTERNAL_OPTIONS
|
||||
#endif
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.h>
|
||||
#include "./turbojpeg.h"
|
||||
|
||||
#define PAD(v, p) ((v+(p)-1)&(~((p)-1)))
|
||||
|
||||
#define CSTATE_START 100
|
||||
#define DSTATE_START 200
|
||||
#define MEMZERO(ptr, size) memset(ptr, 0, size)
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
|
||||
/* Error handling (based on example in example.c) */
|
||||
|
||||
static char errStr[JMSG_LENGTH_MAX]="No error";
|
||||
|
||||
struct my_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
typedef struct my_error_mgr *my_error_ptr;
|
||||
|
||||
static void my_error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
my_error_ptr myerr=(my_error_ptr)cinfo->err;
|
||||
(*cinfo->err->output_message)(cinfo);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
/* Based on output_message() in jerror.c */
|
||||
|
||||
static void my_output_message(j_common_ptr cinfo)
|
||||
{
|
||||
(*cinfo->err->format_message)(cinfo, errStr);
|
||||
}
|
||||
|
||||
|
||||
/* Global structures, macros, etc. */
|
||||
|
||||
enum {COMPRESS=1, DECOMPRESS=2};
|
||||
|
||||
typedef struct _tjinstance
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_decompress_struct dinfo;
|
||||
struct jpeg_destination_mgr jdst;
|
||||
struct jpeg_source_mgr jsrc;
|
||||
struct my_error_mgr jerr;
|
||||
int init;
|
||||
} tjinstance;
|
||||
|
||||
static const int pixelsize[TJ_NUMSAMP]={3, 3, 3, 1, 3};
|
||||
|
||||
#define NUMSF 4
|
||||
static const tjscalingfactor sf[NUMSF]={
|
||||
{1, 1},
|
||||
{1, 2},
|
||||
{1, 4},
|
||||
{1, 8}
|
||||
};
|
||||
|
||||
#define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
|
||||
retval=-1; goto bailout;}
|
||||
#define getinstance(handle) tjinstance *this=(tjinstance *)handle; \
|
||||
j_compress_ptr cinfo=NULL; j_decompress_ptr dinfo=NULL; \
|
||||
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
|
||||
return -1;} \
|
||||
cinfo=&this->cinfo; dinfo=&this->dinfo;
|
||||
|
||||
static int getPixelFormat(int pixelSize, int flags)
|
||||
{
|
||||
if(pixelSize==1) return TJPF_GRAY;
|
||||
if(pixelSize==3)
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_BGR;
|
||||
else return TJPF_RGB;
|
||||
}
|
||||
if(pixelSize==4)
|
||||
{
|
||||
if(flags&TJ_ALPHAFIRST)
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_XBGR;
|
||||
else return TJPF_XRGB;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_BGRX;
|
||||
else return TJPF_RGBX;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setCompDefaults(struct jpeg_compress_struct *cinfo,
|
||||
int pixelFormat, int subsamp, int jpegQual)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_GRAY:
|
||||
cinfo->in_color_space=JCS_GRAYSCALE; break;
|
||||
#if JCS_EXTENSIONS==1
|
||||
case TJPF_RGB:
|
||||
cinfo->in_color_space=JCS_EXT_RGB; break;
|
||||
case TJPF_BGR:
|
||||
cinfo->in_color_space=JCS_EXT_BGR; break;
|
||||
case TJPF_RGBX:
|
||||
case TJPF_RGBA:
|
||||
cinfo->in_color_space=JCS_EXT_RGBX; break;
|
||||
case TJPF_BGRX:
|
||||
case TJPF_BGRA:
|
||||
cinfo->in_color_space=JCS_EXT_BGRX; break;
|
||||
case TJPF_XRGB:
|
||||
case TJPF_ARGB:
|
||||
cinfo->in_color_space=JCS_EXT_XRGB; break;
|
||||
case TJPF_XBGR:
|
||||
case TJPF_ABGR:
|
||||
cinfo->in_color_space=JCS_EXT_XBGR; break;
|
||||
#else
|
||||
case TJPF_RGB:
|
||||
case TJPF_BGR:
|
||||
case TJPF_RGBX:
|
||||
case TJPF_BGRX:
|
||||
case TJPF_XRGB:
|
||||
case TJPF_XBGR:
|
||||
case TJPF_RGBA:
|
||||
case TJPF_BGRA:
|
||||
case TJPF_ARGB:
|
||||
case TJPF_ABGR:
|
||||
cinfo->in_color_space=JCS_RGB; pixelFormat=TJPF_RGB;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
cinfo->input_components=tjPixelSize[pixelFormat];
|
||||
jpeg_set_defaults(cinfo);
|
||||
if(jpegQual>=0)
|
||||
{
|
||||
jpeg_set_quality(cinfo, jpegQual, TRUE);
|
||||
if(jpegQual>=96) cinfo->dct_method=JDCT_ISLOW;
|
||||
else cinfo->dct_method=JDCT_FASTEST;
|
||||
}
|
||||
if(subsamp==TJSAMP_GRAY)
|
||||
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
|
||||
else
|
||||
jpeg_set_colorspace(cinfo, JCS_YCbCr);
|
||||
|
||||
cinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8;
|
||||
cinfo->comp_info[1].h_samp_factor=1;
|
||||
cinfo->comp_info[2].h_samp_factor=1;
|
||||
cinfo->comp_info[0].v_samp_factor=tjMCUHeight[subsamp]/8;
|
||||
cinfo->comp_info[1].v_samp_factor=1;
|
||||
cinfo->comp_info[2].v_samp_factor=1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int setDecompDefaults(struct jpeg_decompress_struct *dinfo,
|
||||
int pixelFormat)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_GRAY:
|
||||
dinfo->out_color_space=JCS_GRAYSCALE; break;
|
||||
#if JCS_EXTENSIONS==1
|
||||
case TJPF_RGB:
|
||||
dinfo->out_color_space=JCS_EXT_RGB; break;
|
||||
case TJPF_BGR:
|
||||
dinfo->out_color_space=JCS_EXT_BGR; break;
|
||||
case TJPF_RGBX:
|
||||
dinfo->out_color_space=JCS_EXT_RGBX; break;
|
||||
case TJPF_BGRX:
|
||||
dinfo->out_color_space=JCS_EXT_BGRX; break;
|
||||
case TJPF_XRGB:
|
||||
dinfo->out_color_space=JCS_EXT_XRGB; break;
|
||||
case TJPF_XBGR:
|
||||
dinfo->out_color_space=JCS_EXT_XBGR; break;
|
||||
#if JCS_ALPHA_EXTENSIONS==1
|
||||
case TJPF_RGBA:
|
||||
dinfo->out_color_space=JCS_EXT_RGBA; break;
|
||||
case TJPF_BGRA:
|
||||
dinfo->out_color_space=JCS_EXT_BGRA; break;
|
||||
case TJPF_ARGB:
|
||||
dinfo->out_color_space=JCS_EXT_ARGB; break;
|
||||
case TJPF_ABGR:
|
||||
dinfo->out_color_space=JCS_EXT_ABGR; break;
|
||||
#endif
|
||||
#else
|
||||
case TJPF_RGB:
|
||||
case TJPF_BGR:
|
||||
case TJPF_RGBX:
|
||||
case TJPF_BGRX:
|
||||
case TJPF_XRGB:
|
||||
case TJPF_XBGR:
|
||||
case TJPF_RGBA:
|
||||
case TJPF_BGRA:
|
||||
case TJPF_ARGB:
|
||||
case TJPF_ABGR:
|
||||
dinfo->out_color_space=JCS_RGB; break;
|
||||
#endif
|
||||
default:
|
||||
_throw("Unsupported pixel format");
|
||||
}
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int getSubsamp(j_decompress_ptr dinfo)
|
||||
{
|
||||
int retval=-1, i, k;
|
||||
for(i=0; i<NUMSUBOPT; i++)
|
||||
{
|
||||
if(dinfo->num_components==pixelsize[i])
|
||||
{
|
||||
if(dinfo->comp_info[0].h_samp_factor==tjMCUWidth[i]/8
|
||||
&& dinfo->comp_info[0].v_samp_factor==tjMCUHeight[i]/8)
|
||||
{
|
||||
int match=0;
|
||||
for(k=1; k<dinfo->num_components; k++)
|
||||
{
|
||||
if(dinfo->comp_info[k].h_samp_factor==1
|
||||
&& dinfo->comp_info[k].v_samp_factor==1)
|
||||
match++;
|
||||
}
|
||||
if(match==dinfo->num_components-1)
|
||||
{
|
||||
retval=i; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
|
||||
/* Conversion functions to emulate the colorspace extensions. This allows the
|
||||
TurboJPEG wrapper to be used with libjpeg */
|
||||
|
||||
#define TORGB(PS, ROFFSET, GOFFSET, BOFFSET) { \
|
||||
int rowPad=pitch-width*PS; \
|
||||
while(height--) \
|
||||
{ \
|
||||
unsigned char *endOfRow=src+width*PS; \
|
||||
while(src<endOfRow) \
|
||||
{ \
|
||||
dst[RGB_RED]=src[ROFFSET]; \
|
||||
dst[RGB_GREEN]=src[GOFFSET]; \
|
||||
dst[RGB_BLUE]=src[BOFFSET]; \
|
||||
dst+=RGB_PIXELSIZE; src+=PS; \
|
||||
} \
|
||||
src+=rowPad; \
|
||||
} \
|
||||
}
|
||||
|
||||
static unsigned char *toRGB(unsigned char *src, int width, int pitch,
|
||||
int height, int pixelFormat, unsigned char *dst)
|
||||
{
|
||||
unsigned char *retval=src;
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_RGB:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||
retval=dst; TORGB(3, 0, 1, 2);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGR:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||
retval=dst; TORGB(3, 2, 1, 0);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBX:
|
||||
case TJPF_RGBA:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 0, 1, 2);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRX:
|
||||
case TJPF_BGRA:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 2, 1, 0);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XRGB:
|
||||
case TJPF_ARGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 1, 2, 3);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XBGR:
|
||||
case TJPF_ABGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 3, 2, 1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#define FROMRGB(PS, ROFFSET, GOFFSET, BOFFSET, SETALPHA) { \
|
||||
int rowPad=pitch-width*PS; \
|
||||
while(height--) \
|
||||
{ \
|
||||
unsigned char *endOfRow=dst+width*PS; \
|
||||
while(dst<endOfRow) \
|
||||
{ \
|
||||
dst[ROFFSET]=src[RGB_RED]; \
|
||||
dst[GOFFSET]=src[RGB_GREEN]; \
|
||||
dst[BOFFSET]=src[RGB_BLUE]; \
|
||||
SETALPHA \
|
||||
dst+=PS; src+=RGB_PIXELSIZE; \
|
||||
} \
|
||||
dst+=rowPad; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void fromRGB(unsigned char *src, unsigned char *dst, int width,
|
||||
int pitch, int height, int pixelFormat)
|
||||
{
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_RGB:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||
FROMRGB(3, 0, 1, 2,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGR:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||
FROMRGB(3, 2, 1, 0,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBX:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 0, 1, 2,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBA:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 0, 1, 2, dst[3]=0xFF;);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRX:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 2, 1, 0,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRA:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 2, 1, 0, dst[3]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XRGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 1, 2, 3,); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_ARGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 1, 2, 3, dst[0]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XBGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 3, 2, 1,); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_ABGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 3, 2, 1, dst[0]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* General API functions */
|
||||
|
||||
DLLEXPORT char* DLLCALL tjGetErrorStr(void)
|
||||
{
|
||||
return errStr;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle)
|
||||
{
|
||||
getinstance(handle);
|
||||
if(setjmp(this->jerr.setjmp_buffer)) return -1;
|
||||
if(this->init&COMPRESS) jpeg_destroy_compress(cinfo);
|
||||
if(this->init&DECOMPRESS) jpeg_destroy_decompress(dinfo);
|
||||
free(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Compressor */
|
||||
|
||||
static boolean empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
ERREXIT(cinfo, JERR_BUFFER_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void dst_noop(j_compress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static tjhandle _tjInitCompress(tjinstance *this)
|
||||
{
|
||||
/* This is also straight out of example.c */
|
||||
this->cinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||
this->jerr.pub.error_exit=my_error_exit;
|
||||
this->jerr.pub.output_message=my_output_message;
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
if(this) free(this); return NULL;
|
||||
}
|
||||
|
||||
jpeg_create_compress(&this->cinfo);
|
||||
this->cinfo.dest=&this->jdst;
|
||||
this->jdst.init_destination=dst_noop;
|
||||
this->jdst.empty_output_buffer=empty_output_buffer;
|
||||
this->jdst.term_destination=dst_noop;
|
||||
|
||||
this->init|=COMPRESS;
|
||||
return (tjhandle)this;
|
||||
}
|
||||
|
||||
DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
|
||||
{
|
||||
tjinstance *this=NULL;
|
||||
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjInitCompress(): Memory allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
MEMZERO(this, sizeof(tjinstance));
|
||||
return _tjInitCompress(this);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||
int jpegSubsamp)
|
||||
{
|
||||
unsigned long retval=0; int mcuw, mcuh, chromasf;
|
||||
if(width<1 || height<1 || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT)
|
||||
_throw("tjBufSize(): Invalid argument");
|
||||
|
||||
// This allows for rare corner cases in which a JPEG image can actually be
|
||||
// larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||
// happened before.)
|
||||
mcuw=tjMCUWidth[jpegSubsamp];
|
||||
mcuh=tjMCUHeight[jpegSubsamp];
|
||||
chromasf=jpegSubsamp==TJSAMP_GRAY? 0: 4*64/(mcuw*mcuh);
|
||||
retval=PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
|
||||
{
|
||||
unsigned long retval=0;
|
||||
if(width<1 || height<1)
|
||||
_throw("TJBUFSIZE(): Invalid argument");
|
||||
|
||||
// This allows for rare corner cases in which a JPEG image can actually be
|
||||
// larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||
// happened before.)
|
||||
retval=PAD(width, 16) * PAD(height, 16) * 6 + 2048;
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||
#ifndef JCS_EXTENSIONS
|
||||
unsigned char *rgbBuf=NULL;
|
||||
#endif
|
||||
|
||||
getinstance(handle)
|
||||
if((this->init&COMPRESS)==0)
|
||||
_throw("tjCompress2(): Instance has not been initialized for compression");
|
||||
|
||||
if(srcBuf==NULL || width<=0 || pitch<0 || height<=0 || pixelFormat<0
|
||||
|| pixelFormat>=TJ_NUMPF || jpegBuf==NULL || jpegSize==NULL
|
||||
|| jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT || jpegQual<0 || jpegQual>100)
|
||||
_throw("tjCompress2(): Invalid argument");
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
retval=-1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
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("tjCompress2(): Memory allocation failure");
|
||||
srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
|
||||
pitch=width*RGB_PIXELSIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
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(setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual)==-1)
|
||||
return -1;
|
||||
|
||||
this->jdst.next_output_byte=*jpegBuf;
|
||||
this->jdst.free_in_buffer=tjBufSize(width, height, jpegSubsamp);
|
||||
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
|
||||
_throw("tjCompress2(): Memory allocation failure");
|
||||
for(i=0; i<height; i++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&srcBuf[(height-i-1)*pitch];
|
||||
else row_pointer[i]=&srcBuf[i*pitch];
|
||||
}
|
||||
while(cinfo->next_scanline<cinfo->image_height)
|
||||
{
|
||||
jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
|
||||
cinfo->image_height-cinfo->next_scanline);
|
||||
}
|
||||
jpeg_finish_compress(cinfo);
|
||||
*jpegSize=tjBufSize(width, height, jpegSubsamp)
|
||||
-(unsigned long)(this->jdst.free_in_buffer);
|
||||
|
||||
bailout:
|
||||
if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(rgbBuf) free(rgbBuf);
|
||||
#endif
|
||||
if(row_pointer) free(row_pointer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf,
|
||||
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
|
||||
{
|
||||
int retval=0; unsigned long size;
|
||||
retval=tjCompress2(handle, srcBuf, width, pitch, height,
|
||||
getPixelFormat(pixelSize, flags), &jpegBuf, &size, jpegSubsamp, jpegQual,
|
||||
flags);
|
||||
*jpegSize=size;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* Decompressor */
|
||||
|
||||
static boolean fill_input_buffer(j_decompress_ptr dinfo)
|
||||
{
|
||||
ERREXIT(dinfo, JERR_BUFFER_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void skip_input_data(j_decompress_ptr dinfo, long num_bytes)
|
||||
{
|
||||
dinfo->src->next_input_byte += (size_t) num_bytes;
|
||||
dinfo->src->bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
|
||||
static void src_noop(j_decompress_ptr dinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static tjhandle _tjInitDecompress(tjinstance *this)
|
||||
{
|
||||
/* This is also straight out of example.c */
|
||||
this->dinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||
this->jerr.pub.error_exit=my_error_exit;
|
||||
this->jerr.pub.output_message=my_output_message;
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
if(this) free(this); return NULL;
|
||||
}
|
||||
|
||||
jpeg_create_decompress(&this->dinfo);
|
||||
this->dinfo.src=&this->jsrc;
|
||||
this->jsrc.init_source=src_noop;
|
||||
this->jsrc.fill_input_buffer=fill_input_buffer;
|
||||
this->jsrc.skip_input_data=skip_input_data;
|
||||
this->jsrc.resync_to_restart=jpeg_resync_to_restart;
|
||||
this->jsrc.term_source=src_noop;
|
||||
|
||||
this->init|=DECOMPRESS;
|
||||
return (tjhandle)this;
|
||||
}
|
||||
|
||||
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
|
||||
{
|
||||
tjinstance *this;
|
||||
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjInitDecompress(): Memory allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
MEMZERO(this, sizeof(tjinstance));
|
||||
return _tjInitDecompress(this);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||
int *jpegSubsamp)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
getinstance(handle);
|
||||
if((this->init&DECOMPRESS)==0)
|
||||
_throw("tjDecompressHeader2(): Instance has not been initialized for decompression");
|
||||
|
||||
if(jpegBuf==NULL || jpegSize<=0 || width==NULL || height==NULL
|
||||
|| jpegSubsamp==NULL)
|
||||
_throw("tjDecompressHeader2(): Invalid argument");
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->jsrc.bytes_in_buffer=jpegSize;
|
||||
this->jsrc.next_input_byte=jpegBuf;
|
||||
jpeg_read_header(dinfo, TRUE);
|
||||
|
||||
*width=dinfo->image_width;
|
||||
*height=dinfo->image_height;
|
||||
*jpegSubsamp=getSubsamp(dinfo);
|
||||
|
||||
jpeg_abort_decompress(dinfo);
|
||||
|
||||
if(*jpegSubsamp<0)
|
||||
_throw("tjDecompressHeader2(): Could not determine subsampling type for JPEG image");
|
||||
if(*width<1 || *height<1)
|
||||
_throw("tjDecompressHeader2(): Invalid data returned in header");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height)
|
||||
{
|
||||
int jpegSubsamp;
|
||||
return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
|
||||
&jpegSubsamp);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors)
|
||||
{
|
||||
if(numscalingfactors==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjGetScalingFactors(): Invalid argument");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*numscalingfactors=NUMSF;
|
||||
return (tjscalingfactor *)sf;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle, unsigned char *jpegBuf,
|
||||
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||
int height, int pixelFormat, int flags)
|
||||
{
|
||||
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||
int jpegwidth, jpegheight, scaledw, scaledh;
|
||||
#ifndef JCS_EXTENSIONS
|
||||
unsigned char *rgbBuf=NULL;
|
||||
unsigned char *_dstBuf=NULL; int _pitch=0;
|
||||
#endif
|
||||
|
||||
getinstance(handle);
|
||||
if((this->init&DECOMPRESS)==0)
|
||||
_throw("tjDecompress2(): Instance has not been initialized for decompression");
|
||||
|
||||
if(jpegBuf==NULL || jpegSize<=0 || dstBuf==NULL || width<0 || pitch<0
|
||||
|| height<0 || pixelFormat<0 || pixelFormat>=TJ_NUMPF)
|
||||
_throw("tjDecompress2(): Invalid argument");
|
||||
|
||||
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(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
retval=-1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
this->jsrc.bytes_in_buffer=jpegSize;
|
||||
this->jsrc.next_input_byte=jpegBuf;
|
||||
jpeg_read_header(dinfo, TRUE);
|
||||
if(setDecompDefaults(dinfo, pixelFormat)==-1)
|
||||
{
|
||||
retval=-1; goto bailout;
|
||||
}
|
||||
|
||||
if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE;
|
||||
|
||||
jpegwidth=dinfo->image_width; jpegheight=dinfo->image_height;
|
||||
if(width==0) width=jpegwidth;
|
||||
if(height==0) height=jpegheight;
|
||||
for(i=0; i<NUMSF; i++)
|
||||
{
|
||||
scaledw=TJSCALED(jpegwidth, sf[i]);
|
||||
scaledh=TJSCALED(jpegheight, sf[i]);
|
||||
if(scaledw<=width && scaledh<=height)
|
||||
break;
|
||||
}
|
||||
if(scaledw>width || scaledh>height)
|
||||
_throw("tjDecompress2(): Could not scale down to desired image dimensions");
|
||||
width=scaledw; height=scaledh;
|
||||
dinfo->scale_num=sf[i].num;
|
||||
dinfo->scale_denom=sf[i].denom;
|
||||
|
||||
jpeg_start_decompress(dinfo);
|
||||
if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(pixelFormat!=TJPF_GRAY &&
|
||||
(RGB_RED!=tjRedOffset[pixelFormat] ||
|
||||
RGB_GREEN!=tjGreenOffset[pixelFormat] ||
|
||||
RGB_BLUE!=tjBlueOffset[pixelFormat] ||
|
||||
RGB_PIXELSIZE!=tjPixelSize[pixelFormat]))
|
||||
{
|
||||
rgbBuf=(unsigned char *)malloc(width*height*3);
|
||||
if(!rgbBuf) _throw("tjDecompress2(): Memory allocation failure");
|
||||
_pitch=pitch; pitch=width*3;
|
||||
_dstBuf=dstBuf; dstBuf=rgbBuf;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)
|
||||
*dinfo->output_height))==NULL)
|
||||
_throw("tjDecompress2(): Memory allocation failure");
|
||||
for(i=0; i<(int)dinfo->output_height; i++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP)
|
||||
row_pointer[i]=&dstBuf[(dinfo->output_height-i-1)*pitch];
|
||||
else row_pointer[i]=&dstBuf[i*pitch];
|
||||
}
|
||||
while(dinfo->output_scanline<dinfo->output_height)
|
||||
{
|
||||
jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
|
||||
dinfo->output_height-dinfo->output_scanline);
|
||||
}
|
||||
jpeg_finish_decompress(dinfo);
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
|
||||
#endif
|
||||
|
||||
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);
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, unsigned char *jpegBuf,
|
||||
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||
int height, int pixelSize, int flags)
|
||||
{
|
||||
return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
|
||||
height, getPixelFormat(pixelSize, flags), flags);
|
||||
}
|
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright (C)2009-2012 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TURBOJPEG_H__
|
||||
#define __TURBOJPEG_H__
|
||||
|
||||
#if defined(_WIN32) && defined(DLLDEFINE)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
#define DLLCALL
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup TurboJPEG Lite
|
||||
* TurboJPEG API. This API provides an interface for generating and decoding
|
||||
* JPEG images in memory.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The number of chrominance subsampling options
|
||||
*/
|
||||
#define TJ_NUMSAMP 5
|
||||
|
||||
/**
|
||||
* Chrominance subsampling options.
|
||||
* When an image is converted from the RGB to the YCbCr colorspace as part of
|
||||
* the JPEG compression process, some of the Cb and Cr (chrominance) components
|
||||
* can be discarded or averaged together to produce a smaller image with little
|
||||
* perceptible loss of image clarity (the human eye is more sensitive to small
|
||||
* changes in brightness than small changes in color.) This is called
|
||||
* "chrominance subsampling".
|
||||
*/
|
||||
enum TJSAMP
|
||||
{
|
||||
/**
|
||||
* 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG or
|
||||
* YUV image will contain one chrominance component for every pixel in the
|
||||
* source image.
|
||||
*/
|
||||
TJSAMP_444=0,
|
||||
/**
|
||||
* 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 2x1 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_422,
|
||||
/**
|
||||
* 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 2x2 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_420,
|
||||
/**
|
||||
* Grayscale. The JPEG or YUV image will contain no chrominance components.
|
||||
*/
|
||||
TJSAMP_GRAY,
|
||||
/**
|
||||
* 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 1x2 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_440
|
||||
};
|
||||
|
||||
/**
|
||||
* MCU block width (in pixels) for a given level of chrominance subsampling.
|
||||
* MCU block sizes:
|
||||
* - 8x8 for no subsampling or grayscale
|
||||
* - 16x8 for 4:2:2
|
||||
* - 8x16 for 4:4:0
|
||||
* - 16x16 for 4:2:0
|
||||
*/
|
||||
static const int tjMCUWidth[TJ_NUMSAMP] = {8, 16, 16, 8, 8};
|
||||
|
||||
/**
|
||||
* MCU block height (in pixels) for a given level of chrominance subsampling.
|
||||
* MCU block sizes:
|
||||
* - 8x8 for no subsampling or grayscale
|
||||
* - 16x8 for 4:2:2
|
||||
* - 8x16 for 4:4:0
|
||||
* - 16x16 for 4:2:0
|
||||
*/
|
||||
static const int tjMCUHeight[TJ_NUMSAMP] = {8, 8, 16, 8, 16};
|
||||
|
||||
|
||||
/**
|
||||
* The number of pixel formats
|
||||
*/
|
||||
#define TJ_NUMPF 11
|
||||
|
||||
/**
|
||||
* Pixel formats
|
||||
*/
|
||||
enum TJPF
|
||||
{
|
||||
/**
|
||||
* RGB pixel format. The red, green, and blue components in the image are
|
||||
* stored in 3-byte pixels in the order R, G, B from lowest to highest byte
|
||||
* address within each pixel.
|
||||
*/
|
||||
TJPF_RGB=0,
|
||||
/**
|
||||
* BGR pixel format. The red, green, and blue components in the image are
|
||||
* stored in 3-byte pixels in the order B, G, R from lowest to highest byte
|
||||
* address within each pixel.
|
||||
*/
|
||||
TJPF_BGR,
|
||||
/**
|
||||
* RGBX pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order R, G, B from lowest to highest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_RGBX,
|
||||
/**
|
||||
* BGRX pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order B, G, R from lowest to highest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_BGRX,
|
||||
/**
|
||||
* XBGR pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order R, G, B from highest to lowest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_XBGR,
|
||||
/**
|
||||
* XRGB pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order B, G, R from highest to lowest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_XRGB,
|
||||
/**
|
||||
* Grayscale pixel format. Each 1-byte pixel represents a luminance
|
||||
* (brightness) level from 0 to 255.
|
||||
*/
|
||||
TJPF_GRAY,
|
||||
/**
|
||||
* RGBA pixel format. This is the same as @ref TJPF_RGBX, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_RGBA,
|
||||
/**
|
||||
* BGRA pixel format. This is the same as @ref TJPF_BGRX, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_BGRA,
|
||||
/**
|
||||
* ABGR pixel format. This is the same as @ref TJPF_XBGR, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_ABGR,
|
||||
/**
|
||||
* ARGB pixel format. This is the same as @ref TJPF_XRGB, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_ARGB
|
||||
};
|
||||
|
||||
/**
|
||||
* Red offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the red component is offset from the start of the pixel. For
|
||||
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||
* then the red component will be <tt>pixel[tjRedOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjRedOffset[TJ_NUMPF] = {0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1};
|
||||
/**
|
||||
* Green offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the green component is offset from the start of the pixel.
|
||||
* For instance, if a pixel of format TJ_BGRX is stored in
|
||||
* <tt>char pixel[]</tt>, then the green component will be
|
||||
* <tt>pixel[tjGreenOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjGreenOffset[TJ_NUMPF] = {1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2};
|
||||
/**
|
||||
* Blue offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the Blue component is offset from the start of the pixel. For
|
||||
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||
* then the blue component will be <tt>pixel[tjBlueOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjBlueOffset[TJ_NUMPF] = {2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3};
|
||||
|
||||
/**
|
||||
* Pixel size (in bytes) for a given pixel format.
|
||||
*/
|
||||
static const int tjPixelSize[TJ_NUMPF] = {3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4};
|
||||
|
||||
|
||||
/**
|
||||
* The uncompressed source/destination image is stored in bottom-up (Windows,
|
||||
* OpenGL) order, not top-down (X11) order.
|
||||
*/
|
||||
#define TJFLAG_BOTTOMUP 2
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use MMX code (IPP and
|
||||
* 32-bit libjpeg-turbo versions only.)
|
||||
*/
|
||||
#define TJFLAG_FORCEMMX 8
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE code (32-bit IPP
|
||||
* and 32-bit libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE 16
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE2 code (32-bit IPP
|
||||
* and 32-bit libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE2 32
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE3 code (64-bit IPP
|
||||
* version only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE3 128
|
||||
/**
|
||||
* Use fast, inaccurate chrominance upsampling routines in the JPEG
|
||||
* decompressor (libjpeg and libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FASTUPSAMPLE 256
|
||||
|
||||
|
||||
/**
|
||||
* Scaling factor
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* Numerator
|
||||
*/
|
||||
int num;
|
||||
/**
|
||||
* Denominator
|
||||
*/
|
||||
int denom;
|
||||
} tjscalingfactor;
|
||||
|
||||
|
||||
/**
|
||||
* TurboJPEG instance handle
|
||||
*/
|
||||
typedef void* tjhandle;
|
||||
|
||||
|
||||
/**
|
||||
* Pad the given width to the nearest 32-bit boundary
|
||||
*/
|
||||
#define TJPAD(width) (((width)+3)&(~3))
|
||||
|
||||
/**
|
||||
* Compute the scaled value of <tt>dimension</tt> using the given scaling
|
||||
* factor. This macro performs the integer equivalent of <tt>ceil(dimension *
|
||||
* scalingFactor)</tt>.
|
||||
*/
|
||||
#define TJSCALED(dimension, scalingFactor) ((dimension * scalingFactor.num \
|
||||
+ scalingFactor.denom - 1) / scalingFactor.denom)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Create a TurboJPEG compressor instance.
|
||||
*
|
||||
* @return a handle to the newly-created instance, or NULL if an error
|
||||
* occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjhandle DLLCALL tjInitCompress(void);
|
||||
|
||||
|
||||
/**
|
||||
* Compress an RGB or grayscale image into a JPEG image.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG compressor or transformer instance
|
||||
* @param srcBuf pointer to an image buffer containing RGB or grayscale pixels
|
||||
* to be compressed
|
||||
* @param width width (in pixels) of the source image
|
||||
* @param pitch bytes per line of the source image. Normally, this should be
|
||||
* <tt>width * #tjPixelSize[pixelFormat]</tt> if the image is unpadded,
|
||||
* or <tt>#TJPAD(width * #tjPixelSize[pixelFormat])</tt> if each line of
|
||||
* the image is padded to the nearest 32-bit boundary, as is the case
|
||||
* for Windows bitmaps. You can also be clever and use this 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 image
|
||||
* @param pixelFormat pixel format of the source image (see @ref TJPF
|
||||
* "Pixel formats".)
|
||||
* @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 <tt>*jpegBuf</tt> 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, <tt>*jpegSize</tt> should be set to the
|
||||
* size of your pre-allocated buffer. In any case, unless you have
|
||||
* set #TJFLAG_NOREALLOC, you should always check <tt>*jpegBuf</tt> 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 <tt>*jpegBuf</tt> points to a
|
||||
* pre-allocated buffer, then <tt>*jpegSize</tt> should be set to the
|
||||
* size of the buffer. Upon return, <tt>*jpegSize</tt> will contain the
|
||||
* size of the JPEG image (in bytes.)
|
||||
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||
* generating the JPEG image (see @ref TJSAMP
|
||||
* "Chrominance subsampling options".)
|
||||
* @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 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);
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* larger than the size of the uncompressed source image. The reason for this
|
||||
* is that the JPEG format uses 16-bit coefficients, and it is thus possible
|
||||
* for a very high-quality JPEG image with very high frequency content to
|
||||
* expand rather than compress when converted to the JPEG format. Such images
|
||||
* represent a very rare corner case, but since there is no way to predict the
|
||||
* size of a JPEG image prior to compression, the corner case has to be
|
||||
* handled.
|
||||
*
|
||||
* @param width width of the image (in pixels)
|
||||
* @param height height of the image (in pixels)
|
||||
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||
* generating the JPEG image (see @ref TJSAMP
|
||||
* "Chrominance subsampling options".)
|
||||
*
|
||||
* @return the maximum size of the buffer (in bytes) required to hold the
|
||||
* image, or -1 if the arguments are out of bounds.
|
||||
*/
|
||||
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||
int jpegSubsamp);
|
||||
|
||||
|
||||
/**
|
||||
* Create a TurboJPEG decompressor instance.
|
||||
*
|
||||
* @return a handle to the newly-created instance, or NULL if an error
|
||||
* occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve information about a JPEG image without decompressing it.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||
* @param jpegBuf pointer to a buffer containing a JPEG image
|
||||
* @param jpegSize size of the JPEG image (in bytes)
|
||||
* @param width pointer to an integer variable that will receive the width (in
|
||||
* pixels) of the JPEG image
|
||||
* @param height pointer to an integer variable that will receive the height
|
||||
* (in pixels) of the JPEG image
|
||||
* @param jpegSubsamp pointer to an integer variable that will receive the
|
||||
* level of chrominance subsampling used when compressing the JPEG image
|
||||
* (see @ref TJSAMP "Chrominance subsampling options".)
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||
int *jpegSubsamp);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of fractional scaling factors that the JPEG decompressor in
|
||||
* this implementation of TurboJPEG supports.
|
||||
*
|
||||
* @param numscalingfactors pointer to an integer variable that will receive
|
||||
* the number of elements in the list
|
||||
*
|
||||
* @return a pointer to a list of fractional scaling factors, or NULL if an
|
||||
* error is encountered (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors);
|
||||
|
||||
|
||||
/**
|
||||
* Decompress a JPEG image to an RGB or grayscale image.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||
* @param jpegBuf pointer to a buffer containing the JPEG image to decompress
|
||||
* @param jpegSize size of the JPEG image (in bytes)
|
||||
* @param dstBuf pointer to an image buffer that will receive the decompressed
|
||||
* image. This buffer should normally be <tt>pitch * scaledHeight</tt>
|
||||
* bytes in size, where <tt>scaledHeight</tt> can be determined by
|
||||
* calling #TJSCALED() with the JPEG image height and one of the scaling
|
||||
* factors returned by #tjGetScalingFactors(). The dstBuf pointer may
|
||||
* also be used to decompress into a specific region of a larger buffer.
|
||||
* @param width desired width (in pixels) of the destination image. If this is
|
||||
* smaller than the width of the JPEG image being decompressed, then
|
||||
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||
* largest possible image that will fit within the desired width. If
|
||||
* width is set to 0, then only the height will be considered when
|
||||
* determining the scaled image size.
|
||||
* @param pitch bytes per line of the destination image. Normally, this is
|
||||
* <tt>scaledWidth * #tjPixelSize[pixelFormat]</tt> if the decompressed
|
||||
* image is unpadded, else <tt>#TJPAD(scaledWidth *
|
||||
* #tjPixelSize[pixelFormat])</tt> if each line of the decompressed
|
||||
* image is padded to the nearest 32-bit boundary, as is the case for
|
||||
* Windows bitmaps. (NOTE: <tt>scaledWidth</tt> can be determined by
|
||||
* calling #TJSCALED() with the JPEG image width and one of the scaling
|
||||
* factors returned by #tjGetScalingFactors().) 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>scaledWidth
|
||||
* * #tjPixelSize[pixelFormat]</tt>.
|
||||
* @param height desired height (in pixels) of the destination image. If this
|
||||
* is smaller than the height of the JPEG image being decompressed, then
|
||||
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||
* largest possible image that will fit within the desired height. If
|
||||
* height is set to 0, then only the width will be considered when
|
||||
* determining the scaled image size.
|
||||
* @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 tjDecompress2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||
int width, int pitch, int height, int pixelFormat, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* Destroy a TurboJPEG compressor, decompressor, or transformer instance.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG compressor, decompressor or
|
||||
* transformer instance
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a descriptive error message explaining why the last command failed.
|
||||
*
|
||||
* @return a descriptive error message explaining why the last command failed.
|
||||
*/
|
||||
DLLEXPORT char* DLLCALL tjGetErrorStr(void);
|
||||
|
||||
|
||||
/* Backward compatibility functions and macros (nothing to see here) */
|
||||
#define NUMSUBOPT TJ_NUMSAMP
|
||||
#define TJ_444 TJSAMP_444
|
||||
#define TJ_422 TJSAMP_422
|
||||
#define TJ_420 TJSAMP_420
|
||||
#define TJ_411 TJSAMP_420
|
||||
#define TJ_GRAYSCALE TJSAMP_GRAY
|
||||
|
||||
#define TJ_BGR 1
|
||||
#define TJ_BOTTOMUP TJFLAG_BOTTOMUP
|
||||
#define TJ_FORCEMMX TJFLAG_FORCEMMX
|
||||
#define TJ_FORCESSE TJFLAG_FORCESSE
|
||||
#define TJ_FORCESSE2 TJFLAG_FORCESSE2
|
||||
#define TJ_ALPHAFIRST 64
|
||||
#define TJ_FORCESSE3 TJFLAG_FORCESSE3
|
||||
#define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
|
||||
|
||||
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelSize, unsigned char *dstBuf,
|
||||
unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags);
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height);
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||
int width, int pitch, int height, int pixelSize, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,389 @@
|
||||
/* Copyright (C)2004 Landmark Graphics Corporation
|
||||
* Copyright (C)2005 Sun Microsystems, Inc.
|
||||
* Copyright (C)2010, 2012 D. R. Commander
|
||||
*
|
||||
* This library is free software and may be redistributed and/or modified under
|
||||
* the terms of the wxWindows Library License, Version 3.1 or (at your option)
|
||||
* any later version. The full license is in the LICENSE.txt file included
|
||||
* with this distribution.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* wxWindows Library License for more details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "./tjutil.h"
|
||||
#include "./bmp.h"
|
||||
|
||||
#define byteswap(i) ( \
|
||||
(((i) & 0xff000000) >> 24) | \
|
||||
(((i) & 0x00ff0000) >> 8) | \
|
||||
(((i) & 0x0000ff00) << 8) | \
|
||||
(((i) & 0x000000ff) << 24) )
|
||||
|
||||
#define byteswap16(i) ( \
|
||||
(((i) & 0xff00) >> 8) | \
|
||||
(((i) & 0x00ff) << 8) )
|
||||
|
||||
static __inline int littleendian(void)
|
||||
{
|
||||
unsigned int value=1;
|
||||
unsigned char *ptr=(unsigned char *)(&value);
|
||||
if(ptr[0]==1 && ptr[3]==0) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
#ifndef BI_BITFIELDS
|
||||
#define BI_BITFIELDS 3L
|
||||
#endif
|
||||
#ifndef BI_RGB
|
||||
#define BI_RGB 0L
|
||||
#endif
|
||||
|
||||
#define BMPHDRSIZE 54
|
||||
typedef struct _bmphdr
|
||||
{
|
||||
unsigned short bfType;
|
||||
unsigned int bfSize;
|
||||
unsigned short bfReserved1, bfReserved2;
|
||||
unsigned int bfOffBits;
|
||||
|
||||
unsigned int biSize;
|
||||
int biWidth, biHeight;
|
||||
unsigned short biPlanes, biBitCount;
|
||||
unsigned int biCompression, biSizeImage;
|
||||
int biXPelsPerMeter, biYPelsPerMeter;
|
||||
unsigned int biClrUsed, biClrImportant;
|
||||
} bmphdr;
|
||||
|
||||
static const char *__bmperr="No error";
|
||||
|
||||
static const int ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4};
|
||||
static const int roffset[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1};
|
||||
static const int goffset[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2};
|
||||
static const int boffset[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3};
|
||||
|
||||
#define _throw(m) {__bmperr=m; retcode=-1; goto finally;}
|
||||
#define _unix(f) {if((f)==-1) _throw(strerror(errno));}
|
||||
#define _catch(f) {if((f)==-1) {retcode=-1; goto finally;}}
|
||||
|
||||
#define readme(fd, addr, size) \
|
||||
if((bytesread=read(fd, addr, (size)))==-1) _throw(strerror(errno)); \
|
||||
if(bytesread!=(size)) _throw("Read error");
|
||||
|
||||
void pixelconvert(unsigned char *srcbuf, enum BMPPIXELFORMAT srcformat,
|
||||
int srcpitch, unsigned char *dstbuf, enum BMPPIXELFORMAT dstformat, int dstpitch,
|
||||
int w, int h, int flip)
|
||||
{
|
||||
unsigned char *srcptr, *srcptr0, *dstptr, *dstptr0;
|
||||
int i, j;
|
||||
|
||||
srcptr=flip? &srcbuf[srcpitch*(h-1)]:srcbuf;
|
||||
for(j=0, dstptr=dstbuf; j<h; j++,
|
||||
srcptr+=flip? -srcpitch:srcpitch, dstptr+=dstpitch)
|
||||
{
|
||||
for(i=0, srcptr0=srcptr, dstptr0=dstptr; i<w; i++,
|
||||
srcptr0+=ps[srcformat], dstptr0+=ps[dstformat])
|
||||
{
|
||||
dstptr0[roffset[dstformat]]=srcptr0[roffset[srcformat]];
|
||||
dstptr0[goffset[dstformat]]=srcptr0[goffset[srcformat]];
|
||||
dstptr0[boffset[dstformat]]=srcptr0[boffset[srcformat]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int loadppm(int *fd, unsigned char **buf, int *w, int *h,
|
||||
enum BMPPIXELFORMAT f, int align, int dstbottomup, int ascii)
|
||||
{
|
||||
FILE *fs=NULL; int retcode=0, scalefactor, dstpitch;
|
||||
unsigned char *tempbuf=NULL; char temps[255], temps2[255];
|
||||
int numread=0, totalread=0, pixel[3], i, j;
|
||||
|
||||
if((fs=fdopen(*fd, "r"))==NULL) _throw(strerror(errno));
|
||||
|
||||
do
|
||||
{
|
||||
if(!fgets(temps, 255, fs)) _throw("Read error");
|
||||
if(strlen(temps)==0 || temps[0]=='\n') continue;
|
||||
if(sscanf(temps, "%s", temps2)==1 && temps2[1]=='#') continue;
|
||||
switch(totalread)
|
||||
{
|
||||
case 0:
|
||||
if((numread=sscanf(temps, "%d %d %d", w, h, &scalefactor))==EOF)
|
||||
_throw("Read error");
|
||||
break;
|
||||
case 1:
|
||||
if((numread=sscanf(temps, "%d %d", h, &scalefactor))==EOF)
|
||||
_throw("Read error");
|
||||
break;
|
||||
case 2:
|
||||
if((numread=sscanf(temps, "%d", &scalefactor))==EOF)
|
||||
_throw("Read error");
|
||||
break;
|
||||
}
|
||||
totalread+=numread;
|
||||
} while(totalread<3);
|
||||
if((*w)<1 || (*h)<1 || scalefactor<1) _throw("Corrupt PPM header");
|
||||
|
||||
dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1));
|
||||
if((*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL)
|
||||
_throw("Memory allocation error");
|
||||
if(ascii)
|
||||
{
|
||||
for(j=0; j<*h; j++)
|
||||
{
|
||||
for(i=0; i<*w; i++)
|
||||
{
|
||||
if(fscanf(fs, "%d%d%d", &pixel[0], &pixel[1], &pixel[2])!=3)
|
||||
_throw("Read error");
|
||||
(*buf)[j*dstpitch+i*ps[f]+roffset[f]]=(unsigned char)(pixel[0]*255/scalefactor);
|
||||
(*buf)[j*dstpitch+i*ps[f]+goffset[f]]=(unsigned char)(pixel[1]*255/scalefactor);
|
||||
(*buf)[j*dstpitch+i*ps[f]+boffset[f]]=(unsigned char)(pixel[2]*255/scalefactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(scalefactor!=255)
|
||||
_throw("Binary PPMs must have 8-bit components");
|
||||
if((tempbuf=(unsigned char *)malloc((*w)*(*h)*3))==NULL)
|
||||
_throw("Memory allocation error");
|
||||
if(fread(tempbuf, (*w)*(*h)*3, 1, fs)!=1) _throw("Read error");
|
||||
pixelconvert(tempbuf, BMP_RGB, (*w)*3, *buf, f, dstpitch, *w, *h, dstbottomup);
|
||||
}
|
||||
|
||||
finally:
|
||||
if(fs) {fclose(fs); *fd=-1;}
|
||||
if(tempbuf) free(tempbuf);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
|
||||
enum BMPPIXELFORMAT f, int align, int dstbottomup)
|
||||
{
|
||||
int fd=-1, bytesread, srcpitch, srcbottomup=1, srcps, dstpitch,
|
||||
retcode=0;
|
||||
unsigned char *tempbuf=NULL;
|
||||
bmphdr bh; int flags=O_RDONLY;
|
||||
|
||||
dstbottomup=dstbottomup? 1:0;
|
||||
#ifdef _WIN32
|
||||
flags|=O_BINARY;
|
||||
#endif
|
||||
if(!filename || !buf || !w || !h || f<0 || f>BMPPIXELFORMATS-1 || align<1)
|
||||
_throw("invalid argument to loadbmp()");
|
||||
if((align&(align-1))!=0)
|
||||
_throw("Alignment must be a power of 2");
|
||||
_unix(fd=open(filename, flags));
|
||||
|
||||
readme(fd, &bh.bfType, sizeof(unsigned short));
|
||||
if(!littleendian()) bh.bfType=byteswap16(bh.bfType);
|
||||
|
||||
if(bh.bfType==0x3650)
|
||||
{
|
||||
_catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 0));
|
||||
goto finally;
|
||||
}
|
||||
if(bh.bfType==0x3350)
|
||||
{
|
||||
_catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 1));
|
||||
goto finally;
|
||||
}
|
||||
|
||||
readme(fd, &bh.bfSize, sizeof(unsigned int));
|
||||
readme(fd, &bh.bfReserved1, sizeof(unsigned short));
|
||||
readme(fd, &bh.bfReserved2, sizeof(unsigned short));
|
||||
readme(fd, &bh.bfOffBits, sizeof(unsigned int));
|
||||
readme(fd, &bh.biSize, sizeof(unsigned int));
|
||||
readme(fd, &bh.biWidth, sizeof(int));
|
||||
readme(fd, &bh.biHeight, sizeof(int));
|
||||
readme(fd, &bh.biPlanes, sizeof(unsigned short));
|
||||
readme(fd, &bh.biBitCount, sizeof(unsigned short));
|
||||
readme(fd, &bh.biCompression, sizeof(unsigned int));
|
||||
readme(fd, &bh.biSizeImage, sizeof(unsigned int));
|
||||
readme(fd, &bh.biXPelsPerMeter, sizeof(int));
|
||||
readme(fd, &bh.biYPelsPerMeter, sizeof(int));
|
||||
readme(fd, &bh.biClrUsed, sizeof(unsigned int));
|
||||
readme(fd, &bh.biClrImportant, sizeof(unsigned int));
|
||||
|
||||
if(!littleendian())
|
||||
{
|
||||
bh.bfSize=byteswap(bh.bfSize);
|
||||
bh.bfOffBits=byteswap(bh.bfOffBits);
|
||||
bh.biSize=byteswap(bh.biSize);
|
||||
bh.biWidth=byteswap(bh.biWidth);
|
||||
bh.biHeight=byteswap(bh.biHeight);
|
||||
bh.biPlanes=byteswap16(bh.biPlanes);
|
||||
bh.biBitCount=byteswap16(bh.biBitCount);
|
||||
bh.biCompression=byteswap(bh.biCompression);
|
||||
bh.biSizeImage=byteswap(bh.biSizeImage);
|
||||
bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter);
|
||||
bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter);
|
||||
bh.biClrUsed=byteswap(bh.biClrUsed);
|
||||
bh.biClrImportant=byteswap(bh.biClrImportant);
|
||||
}
|
||||
|
||||
if(bh.bfType!=0x4d42 || bh.bfOffBits<BMPHDRSIZE
|
||||
|| bh.biWidth<1 || bh.biHeight==0)
|
||||
_throw("Corrupt bitmap header");
|
||||
if((bh.biBitCount!=24 && bh.biBitCount!=32) || bh.biCompression!=BI_RGB)
|
||||
_throw("Only uncompessed RGB bitmaps are supported");
|
||||
|
||||
*w=bh.biWidth; *h=bh.biHeight; srcps=bh.biBitCount/8;
|
||||
if(*h<0) {*h=-(*h); srcbottomup=0;}
|
||||
srcpitch=(((*w)*srcps)+3)&(~3);
|
||||
dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1));
|
||||
|
||||
if(srcpitch*(*h)+bh.bfOffBits!=bh.bfSize) _throw("Corrupt bitmap header");
|
||||
if((tempbuf=(unsigned char *)malloc(srcpitch*(*h)))==NULL
|
||||
|| (*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL)
|
||||
_throw("Memory allocation error");
|
||||
if(lseek(fd, (long)bh.bfOffBits, SEEK_SET)!=(long)bh.bfOffBits)
|
||||
_throw(strerror(errno));
|
||||
_unix(bytesread=read(fd, tempbuf, srcpitch*(*h)));
|
||||
if(bytesread!=srcpitch*(*h)) _throw("Read error");
|
||||
|
||||
pixelconvert(tempbuf, BMP_BGR, srcpitch, *buf, f, dstpitch, *w, *h,
|
||||
srcbottomup!=dstbottomup);
|
||||
|
||||
finally:
|
||||
if(tempbuf) free(tempbuf);
|
||||
if(fd!=-1) close(fd);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
#define writeme(fd, addr, size) \
|
||||
if((byteswritten=write(fd, addr, (size)))==-1) _throw(strerror(errno)); \
|
||||
if(byteswritten!=(size)) _throw("Write error");
|
||||
|
||||
int saveppm(char *filename, unsigned char *buf, int w, int h,
|
||||
enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup)
|
||||
{
|
||||
FILE *fs=NULL; int retcode=0;
|
||||
unsigned char *tempbuf=NULL;
|
||||
|
||||
if((fs=fopen(filename, "wb"))==NULL) _throw(strerror(errno));
|
||||
if(fprintf(fs, "P6\n")<1) _throw("Write error");
|
||||
if(fprintf(fs, "%d %d\n", w, h)<1) _throw("Write error");
|
||||
if(fprintf(fs, "255\n")<1) _throw("Write error");
|
||||
|
||||
if((tempbuf=(unsigned char *)malloc(w*h*3))==NULL)
|
||||
_throw("Memory allocation error");
|
||||
|
||||
pixelconvert(buf, f, srcpitch, tempbuf, BMP_RGB, w*3, w, h,
|
||||
srcbottomup);
|
||||
|
||||
if((fwrite(tempbuf, w*h*3, 1, fs))!=1) _throw("Write error");
|
||||
|
||||
finally:
|
||||
if(tempbuf) free(tempbuf);
|
||||
if(fs) fclose(fs);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
int savebmp(char *filename, unsigned char *buf, int w, int h,
|
||||
enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup)
|
||||
{
|
||||
int fd=-1, byteswritten, dstpitch, retcode=0;
|
||||
int flags=O_RDWR|O_CREAT|O_TRUNC;
|
||||
unsigned char *tempbuf=NULL; char *temp;
|
||||
bmphdr bh; int mode;
|
||||
|
||||
#ifdef _WIN32
|
||||
flags|=O_BINARY; mode=_S_IREAD|_S_IWRITE;
|
||||
#else
|
||||
mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
|
||||
#endif
|
||||
if(!filename || !buf || w<1 || h<1 || f<0 || f>BMPPIXELFORMATS-1 || srcpitch<0)
|
||||
_throw("bad argument to savebmp()");
|
||||
|
||||
if(srcpitch==0) srcpitch=w*ps[f];
|
||||
|
||||
if((temp=strrchr(filename, '.'))!=NULL)
|
||||
{
|
||||
if(!strcasecmp(temp, ".ppm"))
|
||||
return saveppm(filename, buf, w, h, f, srcpitch, srcbottomup);
|
||||
}
|
||||
|
||||
_unix(fd=open(filename, flags, mode));
|
||||
dstpitch=((w*3)+3)&(~3);
|
||||
|
||||
bh.bfType=0x4d42;
|
||||
bh.bfSize=BMPHDRSIZE+dstpitch*h;
|
||||
bh.bfReserved1=0; bh.bfReserved2=0;
|
||||
bh.bfOffBits=BMPHDRSIZE;
|
||||
bh.biSize=40;
|
||||
bh.biWidth=w; bh.biHeight=h;
|
||||
bh.biPlanes=0; bh.biBitCount=24;
|
||||
bh.biCompression=BI_RGB; bh.biSizeImage=0;
|
||||
bh.biXPelsPerMeter=0; bh.biYPelsPerMeter=0;
|
||||
bh.biClrUsed=0; bh.biClrImportant=0;
|
||||
|
||||
if(!littleendian())
|
||||
{
|
||||
bh.bfType=byteswap16(bh.bfType);
|
||||
bh.bfSize=byteswap(bh.bfSize);
|
||||
bh.bfOffBits=byteswap(bh.bfOffBits);
|
||||
bh.biSize=byteswap(bh.biSize);
|
||||
bh.biWidth=byteswap(bh.biWidth);
|
||||
bh.biHeight=byteswap(bh.biHeight);
|
||||
bh.biPlanes=byteswap16(bh.biPlanes);
|
||||
bh.biBitCount=byteswap16(bh.biBitCount);
|
||||
bh.biCompression=byteswap(bh.biCompression);
|
||||
bh.biSizeImage=byteswap(bh.biSizeImage);
|
||||
bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter);
|
||||
bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter);
|
||||
bh.biClrUsed=byteswap(bh.biClrUsed);
|
||||
bh.biClrImportant=byteswap(bh.biClrImportant);
|
||||
}
|
||||
|
||||
writeme(fd, &bh.bfType, sizeof(unsigned short));
|
||||
writeme(fd, &bh.bfSize, sizeof(unsigned int));
|
||||
writeme(fd, &bh.bfReserved1, sizeof(unsigned short));
|
||||
writeme(fd, &bh.bfReserved2, sizeof(unsigned short));
|
||||
writeme(fd, &bh.bfOffBits, sizeof(unsigned int));
|
||||
writeme(fd, &bh.biSize, sizeof(unsigned int));
|
||||
writeme(fd, &bh.biWidth, sizeof(int));
|
||||
writeme(fd, &bh.biHeight, sizeof(int));
|
||||
writeme(fd, &bh.biPlanes, sizeof(unsigned short));
|
||||
writeme(fd, &bh.biBitCount, sizeof(unsigned short));
|
||||
writeme(fd, &bh.biCompression, sizeof(unsigned int));
|
||||
writeme(fd, &bh.biSizeImage, sizeof(unsigned int));
|
||||
writeme(fd, &bh.biXPelsPerMeter, sizeof(int));
|
||||
writeme(fd, &bh.biYPelsPerMeter, sizeof(int));
|
||||
writeme(fd, &bh.biClrUsed, sizeof(unsigned int));
|
||||
writeme(fd, &bh.biClrImportant, sizeof(unsigned int));
|
||||
|
||||
if((tempbuf=(unsigned char *)malloc(dstpitch*h))==NULL)
|
||||
_throw("Memory allocation error");
|
||||
|
||||
pixelconvert(buf, f, srcpitch, tempbuf, BMP_BGR, dstpitch, w, h,
|
||||
!srcbottomup);
|
||||
|
||||
if((byteswritten=write(fd, tempbuf, dstpitch*h))!=dstpitch*h)
|
||||
_throw(strerror(errno));
|
||||
|
||||
finally:
|
||||
if(tempbuf) free(tempbuf);
|
||||
if(fd!=-1) close(fd);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
const char *bmpgeterr(void)
|
||||
{
|
||||
return __bmperr;
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/* Copyright (C)2004 Landmark Graphics Corporation
|
||||
* Copyright (C)2005 Sun Microsystems, Inc.
|
||||
* Copyright (C)2011 D. R. Commander
|
||||
*
|
||||
* This library is free software and may be redistributed and/or modified under
|
||||
* the terms of the wxWindows Library License, Version 3.1 or (at your option)
|
||||
* any later version. The full license is in the LICENSE.txt file included
|
||||
* with this distribution.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* wxWindows Library License for more details.
|
||||
*/
|
||||
|
||||
// This provides rudimentary facilities for loading and saving true color
|
||||
// BMP and PPM files
|
||||
|
||||
#ifndef __BMP_H__
|
||||
#define __BMP_H__
|
||||
|
||||
#define BMPPIXELFORMATS 6
|
||||
enum BMPPIXELFORMAT {BMP_RGB=0, BMP_RGBX, BMP_BGR, BMP_BGRX, BMP_XBGR, BMP_XRGB};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This will load a Windows bitmap from a file and return a buffer with the
|
||||
// specified pixel format, scanline alignment, and orientation. The width and
|
||||
// height are returned in w and h.
|
||||
|
||||
int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
|
||||
enum BMPPIXELFORMAT f, int align, int dstbottomup);
|
||||
|
||||
// This will save a buffer with the specified pixel format, pitch, orientation,
|
||||
// width, and height as a 24-bit Windows bitmap or PPM (the filename determines
|
||||
// which format to use)
|
||||
|
||||
int savebmp(char *filename, unsigned char *buf, int w, int h,
|
||||
enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup);
|
||||
|
||||
const char *bmpgeterr(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,658 @@
|
||||
/*
|
||||
* Copyright (C)2009-2012 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include "./bmp.h"
|
||||
#include "./tjutil.h"
|
||||
#include "./turbojpeg.h"
|
||||
|
||||
|
||||
#define _throw(op, err) { \
|
||||
printf("ERROR in line %d while %s:\n%s\n", __LINE__, op, err); \
|
||||
retval=-1; goto bailout;}
|
||||
#define _throwunix(m) _throw(m, strerror(errno))
|
||||
#define _throwtj(m) _throw(m, tjGetErrorStr())
|
||||
#define _throwbmp(m) _throw(m, bmpgeterr())
|
||||
|
||||
int flags=0, decomponly=0, quiet=0, dotile=0, pf=TJPF_BGR;
|
||||
char *ext="ppm";
|
||||
const char *pixFormatStr[TJ_NUMPF]=
|
||||
{
|
||||
"RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY"
|
||||
};
|
||||
const int bmpPF[TJ_NUMPF]=
|
||||
{
|
||||
BMP_RGB, BMP_BGR, BMP_RGBX, BMP_BGRX, BMP_XBGR, BMP_XRGB, -1
|
||||
};
|
||||
const char *subNameLong[TJ_NUMSAMP]=
|
||||
{
|
||||
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0"
|
||||
};
|
||||
const char *subName[NUMSUBOPT]={"444", "422", "420", "GRAY", "440"};
|
||||
tjscalingfactor *scalingfactors=NULL, sf={1, 1}; int nsf=0;
|
||||
double benchtime=5.0;
|
||||
|
||||
|
||||
char *sigfig(double val, int figs, char *buf, int len)
|
||||
{
|
||||
char format[80];
|
||||
int digitsafterdecimal=figs-(int)ceil(log10(fabs(val)));
|
||||
if(digitsafterdecimal<1) snprintf(format, 80, "%%.0f");
|
||||
else snprintf(format, 80, "%%.%df", digitsafterdecimal);
|
||||
snprintf(buf, len, format, val);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/* Decompression test */
|
||||
int decomptest(unsigned char *srcbuf, unsigned char **jpegbuf,
|
||||
unsigned long *jpegsize, unsigned char *dstbuf, int w, int h,
|
||||
int subsamp, int jpegqual, char *filename, int tilew, int tileh)
|
||||
{
|
||||
char tempstr[1024], sizestr[20]="\0", qualstr[6]="\0", *ptr;
|
||||
FILE *file=NULL; tjhandle handle=NULL;
|
||||
int row, col, i, dstbufalloc=0, retval=0;
|
||||
double start, elapsed;
|
||||
int ps=tjPixelSize[pf];
|
||||
int bufsize;
|
||||
int scaledw=TJSCALED(w, sf);
|
||||
int scaledh=TJSCALED(h, sf);
|
||||
int pitch=scaledw*ps;
|
||||
int ntilesw=(w+tilew-1)/tilew, ntilesh=(h+tileh-1)/tileh;
|
||||
unsigned char *dstptr, *dstptr2;
|
||||
|
||||
if(jpegqual>0)
|
||||
{
|
||||
snprintf(qualstr, 6, "_Q%d", jpegqual);
|
||||
qualstr[5]=0;
|
||||
}
|
||||
|
||||
if((handle=tjInitDecompress())==NULL)
|
||||
_throwtj("executing tjInitDecompress()");
|
||||
|
||||
bufsize=pitch*scaledh;
|
||||
if(dstbuf==NULL)
|
||||
{
|
||||
if((dstbuf=(unsigned char *)malloc(bufsize)) == NULL)
|
||||
_throwunix("allocating image buffer");
|
||||
dstbufalloc=1;
|
||||
}
|
||||
/* Set the destination buffer to gray so we know whether the decompressor
|
||||
attempted to write to it */
|
||||
memset(dstbuf, 127, bufsize);
|
||||
|
||||
/* Execute once to preload cache */
|
||||
if(tjDecompress2(handle, jpegbuf[0], jpegsize[0], dstbuf, scaledw,
|
||||
pitch, scaledh, pf, flags)==-1)
|
||||
_throwtj("executing tjDecompress2()");
|
||||
|
||||
/* Benchmark */
|
||||
for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++)
|
||||
{
|
||||
int tile=0;
|
||||
for(row=0, dstptr=dstbuf; row<ntilesh; row++, dstptr+=pitch*tileh)
|
||||
{
|
||||
for(col=0, dstptr2=dstptr; col<ntilesw; col++, tile++, dstptr2+=ps*tilew)
|
||||
{
|
||||
int width=dotile? min(tilew, w-col*tilew):scaledw;
|
||||
int height=dotile? min(tileh, h-row*tileh):scaledh;
|
||||
if(tjDecompress2(handle, jpegbuf[tile], jpegsize[tile], dstptr2, width,
|
||||
pitch, height, pf, flags)==-1)
|
||||
_throwtj("executing tjDecompress2()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()");
|
||||
handle=NULL;
|
||||
|
||||
if(quiet)
|
||||
{
|
||||
printf("%s\n",
|
||||
sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("D--> Frame rate: %f fps\n", (double)i/elapsed);
|
||||
printf(" Dest. throughput: %f Megapixels/sec\n",
|
||||
(double)(w*h)/1000000.*(double)i/elapsed);
|
||||
}
|
||||
if(sf.num!=1 || sf.denom!=1)
|
||||
snprintf(sizestr, 20, "%d_%d", sf.num, sf.denom);
|
||||
else if(tilew!=w || tileh!=h)
|
||||
snprintf(sizestr, 20, "%dx%d", tilew, tileh);
|
||||
else snprintf(sizestr, 20, "full");
|
||||
if(decomponly)
|
||||
snprintf(tempstr, 1024, "%s_%s.%s", filename, sizestr, ext);
|
||||
else
|
||||
snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp],
|
||||
qualstr, sizestr, ext);
|
||||
if(savebmp(tempstr, dstbuf, scaledw, scaledh, bmpPF[pf], pitch,
|
||||
(flags&TJFLAG_BOTTOMUP)!=0)==-1)
|
||||
_throwbmp("saving bitmap");
|
||||
ptr=strrchr(tempstr, '.');
|
||||
snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext);
|
||||
if(srcbuf && sf.num==1 && sf.denom==1)
|
||||
{
|
||||
if(!quiet) printf("Compression error written to %s.\n", tempstr);
|
||||
if(subsamp==TJ_GRAYSCALE)
|
||||
{
|
||||
int index, index2;
|
||||
for(row=0, index=0; row<h; row++, index+=pitch)
|
||||
{
|
||||
for(col=0, index2=index; col<w; col++, index2+=ps)
|
||||
{
|
||||
int rindex=index2+tjRedOffset[pf];
|
||||
int gindex=index2+tjGreenOffset[pf];
|
||||
int bindex=index2+tjBlueOffset[pf];
|
||||
int y=(int)((double)srcbuf[rindex]*0.299
|
||||
+ (double)srcbuf[gindex]*0.587
|
||||
+ (double)srcbuf[bindex]*0.114 + 0.5);
|
||||
if(y>255) y=255; if(y<0) y=0;
|
||||
dstbuf[rindex]=abs(dstbuf[rindex]-y);
|
||||
dstbuf[gindex]=abs(dstbuf[gindex]-y);
|
||||
dstbuf[bindex]=abs(dstbuf[bindex]-y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(row=0; row<h; row++)
|
||||
for(col=0; col<w*ps; col++)
|
||||
dstbuf[pitch*row+col]
|
||||
=abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]);
|
||||
}
|
||||
if(savebmp(tempstr, dstbuf, w, h, bmpPF[pf], pitch,
|
||||
(flags&TJFLAG_BOTTOMUP)!=0)==-1)
|
||||
_throwbmp("saving bitmap");
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(file) {fclose(file); file=NULL;}
|
||||
if(handle) {tjDestroy(handle); handle=NULL;}
|
||||
if(dstbuf && dstbufalloc) {free(dstbuf); dstbuf=NULL;}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void dotest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual,
|
||||
char *filename)
|
||||
{
|
||||
char tempstr[1024], tempstr2[80];
|
||||
FILE *file=NULL; tjhandle handle=NULL;
|
||||
unsigned char **jpegbuf=NULL, *tmpbuf=NULL, *srcptr, *srcptr2;
|
||||
double start, elapsed;
|
||||
int totaljpegsize=0, row, col, i, tilew=w, tileh=h, retval=0;
|
||||
unsigned long *jpegsize=NULL;
|
||||
int ps=tjPixelSize[pf], ntilesw=1, ntilesh=1, pitch=w*ps;
|
||||
|
||||
if((tmpbuf=(unsigned char *)malloc(pitch*h)) == NULL)
|
||||
_throwunix("allocating temporary image buffer");
|
||||
|
||||
if(!quiet)
|
||||
printf(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pixFormatStr[pf],
|
||||
(flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down", subNameLong[subsamp],
|
||||
jpegqual);
|
||||
|
||||
for(tilew=dotile? 8:w, tileh=dotile? 8:h; ; tilew*=2, tileh*=2)
|
||||
{
|
||||
if(tilew>w) tilew=w; if(tileh>h) tileh=h;
|
||||
ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh;
|
||||
|
||||
if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)
|
||||
*ntilesw*ntilesh))==NULL)
|
||||
_throwunix("allocating JPEG tile array");
|
||||
memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh);
|
||||
if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long)
|
||||
*ntilesw*ntilesh))==NULL)
|
||||
_throwunix("allocating JPEG size array");
|
||||
memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh);
|
||||
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh,
|
||||
subsamp)))==NULL)
|
||||
_throwunix("allocating JPEG tiles");
|
||||
}
|
||||
|
||||
/* Compression test */
|
||||
if(quiet==1)
|
||||
printf("%s\t%s\t%s\t%d\t", pixFormatStr[pf],
|
||||
(flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp], jpegqual);
|
||||
for(i=0; i<h; i++)
|
||||
memcpy(&tmpbuf[pitch*i], &srcbuf[w*ps*i], w*ps);
|
||||
if((handle=tjInitCompress())==NULL)
|
||||
_throwtj("executing tjInitCompress()");
|
||||
|
||||
/* Execute once to preload cache */
|
||||
if(tjCompress2(handle, srcbuf, tilew, pitch, tileh, pf, &jpegbuf[0],
|
||||
&jpegsize[0], subsamp, jpegqual, flags)==-1)
|
||||
_throwtj("executing tjCompress2()");
|
||||
|
||||
/* Benchmark */
|
||||
for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++)
|
||||
{
|
||||
int tile=0;
|
||||
totaljpegsize=0;
|
||||
for(row=0, srcptr=srcbuf; row<ntilesh; row++, srcptr+=pitch*tileh)
|
||||
{
|
||||
for(col=0, srcptr2=srcptr; col<ntilesw; col++, tile++,
|
||||
srcptr2+=ps*tilew)
|
||||
{
|
||||
int width=min(tilew, w-col*tilew);
|
||||
int height=min(tileh, h-row*tileh);
|
||||
if(tjCompress2(handle, srcptr2, width, pitch, height, pf,
|
||||
&jpegbuf[tile], &jpegsize[tile], subsamp, jpegqual, flags)==-1)
|
||||
_throwtj("executing tjCompress()2");
|
||||
totaljpegsize+=jpegsize[tile];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()");
|
||||
handle=NULL;
|
||||
|
||||
if(quiet==1) printf("%-4d %-4d\t", tilew, tileh);
|
||||
if(quiet)
|
||||
{
|
||||
printf("%s%c%s%c",
|
||||
sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024),
|
||||
quiet==2? '\n':'\t',
|
||||
sigfig((double)(w*h*ps)/(double)totaljpegsize, 4, tempstr2, 80),
|
||||
quiet==2? '\n':'\t');
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\n%s size: %d x %d\n", dotile? "Tile":"Image", tilew,
|
||||
tileh);
|
||||
printf("C--> Frame rate: %f fps\n", (double)i/elapsed);
|
||||
printf(" Output image size: %d bytes\n", totaljpegsize);
|
||||
printf(" Compression ratio: %f:1\n",
|
||||
(double)(w*h*ps)/(double)totaljpegsize);
|
||||
printf(" Source throughput: %f Megapixels/sec\n",
|
||||
(double)(w*h)/1000000.*(double)i/elapsed);
|
||||
printf(" Output bit stream: %f Megabits/sec\n",
|
||||
(double)totaljpegsize*8./1000000.*(double)i/elapsed);
|
||||
}
|
||||
if(tilew==w && tileh==h)
|
||||
{
|
||||
snprintf(tempstr, 1024, "%s_%s_Q%d.jpg", filename, subName[subsamp],
|
||||
jpegqual);
|
||||
if((file=fopen(tempstr, "wb"))==NULL)
|
||||
_throwunix("opening reference image");
|
||||
if(fwrite(jpegbuf[0], jpegsize[0], 1, file)!=1)
|
||||
_throwunix("writing reference image");
|
||||
fclose(file); file=NULL;
|
||||
if(!quiet) printf("Reference image written to %s\n", tempstr);
|
||||
}
|
||||
|
||||
/* Decompression test */
|
||||
if(decomptest(srcbuf, jpegbuf, jpegsize, tmpbuf, w, h, subsamp, jpegqual,
|
||||
filename, tilew, tileh)==-1)
|
||||
goto bailout;
|
||||
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL;
|
||||
}
|
||||
free(jpegbuf); jpegbuf=NULL;
|
||||
free(jpegsize); jpegsize=NULL;
|
||||
|
||||
if(tilew==w && tileh==h) break;
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(file) {fclose(file); file=NULL;}
|
||||
if(jpegbuf)
|
||||
{
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL;
|
||||
}
|
||||
free(jpegbuf); jpegbuf=NULL;
|
||||
}
|
||||
if(jpegsize) {free(jpegsize); jpegsize=NULL;}
|
||||
if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;}
|
||||
if(handle) {tjDestroy(handle); handle=NULL;}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void dodecomptest(char *filename)
|
||||
{
|
||||
FILE *file=NULL; tjhandle handle=NULL;
|
||||
unsigned char **jpegbuf=NULL, *srcbuf=NULL;
|
||||
unsigned long *jpegsize=NULL, srcsize;
|
||||
int w=0, h=0, subsamp=-1, _w, _h, _tilew, _tileh, _subsamp;
|
||||
char *temp=NULL;
|
||||
int i, tilew, tileh, ntilesw=1, ntilesh=1, retval=0;
|
||||
|
||||
if((file=fopen(filename, "rb"))==NULL)
|
||||
_throwunix("opening file");
|
||||
if(fseek(file, 0, SEEK_END)<0 || (srcsize=ftell(file))<0)
|
||||
_throwunix("determining file size");
|
||||
if((srcbuf=(unsigned char *)malloc(srcsize))==NULL)
|
||||
_throwunix("allocating memory");
|
||||
if(fseek(file, 0, SEEK_SET)<0)
|
||||
_throwunix("setting file position");
|
||||
if(fread(srcbuf, srcsize, 1, file)<1)
|
||||
_throwunix("reading JPEG data");
|
||||
fclose(file); file=NULL;
|
||||
|
||||
temp=strrchr(filename, '.');
|
||||
if(temp!=NULL) *temp='\0';
|
||||
|
||||
if((handle=tjInitDecompress())==NULL)
|
||||
_throwtj("executing tjInitDecompress()");
|
||||
if(tjDecompressHeader2(handle, srcbuf, srcsize, &w, &h, &subsamp)==-1)
|
||||
_throwtj("executing tjDecompressHeader2()");
|
||||
|
||||
if(quiet==1)
|
||||
{
|
||||
printf("All performance values in Mpixels/sec\n\n");
|
||||
printf("Bitmap\tBitmap\tJPEG\t%s %s \tXform\tComp\tDecomp\n",
|
||||
dotile? "Tile ":"Image", dotile? "Tile ":"Image");
|
||||
printf("Format\tOrder\tSubsamp\tWidth Height\tPerf \tRatio\tPerf\n\n");
|
||||
}
|
||||
else if(!quiet)
|
||||
{
|
||||
printf(">>>>> JPEG %s --> %s (%s) <<<<<\n", subNameLong[subsamp],
|
||||
pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down");
|
||||
}
|
||||
|
||||
for(tilew=dotile? 16:w, tileh=dotile? 16:h; ; tilew*=2, tileh*=2)
|
||||
{
|
||||
if(tilew>w) tilew=w; if(tileh>h) tileh=h;
|
||||
ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh;
|
||||
|
||||
if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)
|
||||
*ntilesw*ntilesh))==NULL)
|
||||
_throwunix("allocating JPEG tile array");
|
||||
memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh);
|
||||
if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long)
|
||||
*ntilesw*ntilesh))==NULL)
|
||||
_throwunix("allocating JPEG size array");
|
||||
memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh);
|
||||
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh,
|
||||
subsamp)))==NULL)
|
||||
_throwunix("allocating JPEG tiles");
|
||||
}
|
||||
|
||||
_w=w; _h=h; _tilew=tilew; _tileh=tileh;
|
||||
if(!quiet)
|
||||
{
|
||||
printf("\n%s size: %d x %d", dotile? "Tile":"Image", _tilew,
|
||||
_tileh);
|
||||
if(sf.num!=1 || sf.denom!=1)
|
||||
printf(" --> %d x %d", TJSCALED(_w, sf), TJSCALED(_h, sf));
|
||||
printf("\n");
|
||||
}
|
||||
else if(quiet==1)
|
||||
{
|
||||
printf("%s\t%s\t%s\t", pixFormatStr[pf],
|
||||
(flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp]);
|
||||
printf("%-4d %-4d\t", tilew, tileh);
|
||||
}
|
||||
|
||||
_subsamp=subsamp;
|
||||
if(quiet==1) printf("N/A\tN/A\t");
|
||||
jpegsize[0]=srcsize;
|
||||
memcpy(jpegbuf[0], srcbuf, srcsize);
|
||||
|
||||
if(w==tilew) _tilew=_w;
|
||||
if(h==tileh) _tileh=_h;
|
||||
if(decomptest(NULL, jpegbuf, jpegsize, NULL, _w, _h, _subsamp, 0,
|
||||
filename, _tilew, _tileh)==-1)
|
||||
goto bailout;
|
||||
else if(quiet==1) printf("N/A\n");
|
||||
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
free(jpegbuf[i]); jpegbuf[i]=NULL;
|
||||
}
|
||||
free(jpegbuf); jpegbuf=NULL;
|
||||
if(jpegsize) {free(jpegsize); jpegsize=NULL;}
|
||||
|
||||
if(tilew==w && tileh==h) break;
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(file) {fclose(file); file=NULL;}
|
||||
if(jpegbuf)
|
||||
{
|
||||
for(i=0; i<ntilesw*ntilesh; i++)
|
||||
{
|
||||
if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL;
|
||||
}
|
||||
free(jpegbuf); jpegbuf=NULL;
|
||||
}
|
||||
if(jpegsize) {free(jpegsize); jpegsize=NULL;}
|
||||
if(srcbuf) {free(srcbuf); srcbuf=NULL;}
|
||||
if(handle) {tjDestroy(handle); handle=NULL;}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void usage(char *progname)
|
||||
{
|
||||
int i;
|
||||
printf("USAGE: %s\n", progname);
|
||||
printf(" <Inputfile (BMP|PPM)> <%% Quality> [options]\n\n");
|
||||
printf(" %s\n", progname);
|
||||
printf(" <Inputfile (JPG)> [options]\n\n");
|
||||
printf("Options:\n\n");
|
||||
printf("-bmp = Generate output images in Windows Bitmap format (default=PPM)\n");
|
||||
printf("-bottomup = Test bottom-up compression/decompression\n");
|
||||
printf("-tile = Test performance of the codec when the image is encoded as separate\n");
|
||||
printf(" tiles of varying sizes.\n");
|
||||
printf("-forcemmx, -forcesse, -forcesse2, -forcesse3 =\n");
|
||||
printf(" Force MMX, SSE, SSE2, or SSE3 code paths in the underlying codec\n");
|
||||
printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n");
|
||||
printf(" Test the specified color conversion path in the codec (default: BGR)\n");
|
||||
printf("-fastupsample = Use fast, inaccurate upsampling code to perform 4:2:2 and 4:2:0\n");
|
||||
printf(" YUV decoding\n");
|
||||
printf("-quiet = Output results in tabular rather than verbose format\n");
|
||||
printf("-scale M/N = scale down the width/height of the decompressed JPEG image by a\n");
|
||||
printf(" factor of M/N (M/N = ");
|
||||
for(i=0; i<nsf; i++)
|
||||
{
|
||||
printf("%d/%d", scalingfactors[i].num, scalingfactors[i].denom);
|
||||
if(nsf==2 && i!=nsf-1) printf(" or ");
|
||||
else if(nsf>2)
|
||||
{
|
||||
if(i!=nsf-1) printf(", ");
|
||||
if(i==nsf-2) printf("or ");
|
||||
}
|
||||
}
|
||||
printf(")\n");
|
||||
printf("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)\n\n");
|
||||
printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n");
|
||||
printf("test will be performed for all quality values in the range.\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned char *srcbuf=NULL; int w, h, i, j;
|
||||
int minqual=-1, maxqual=-1; char *temp;
|
||||
int minarg=2; int retval=0;
|
||||
|
||||
if((scalingfactors=tjGetScalingFactors(&nsf))==NULL || nsf==0)
|
||||
_throwtj("executing tjGetScalingFactors()");
|
||||
|
||||
if(argc<minarg) usage(argv[0]);
|
||||
|
||||
temp=strrchr(argv[1], '.');
|
||||
if(temp!=NULL)
|
||||
{
|
||||
if(!strcasecmp(temp, ".bmp")) ext="bmp";
|
||||
if(!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg")) decomponly=1;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
if(!decomponly)
|
||||
{
|
||||
minarg=3;
|
||||
if(argc<minarg) usage(argv[0]);
|
||||
if((minqual=atoi(argv[2]))<1 || minqual>100)
|
||||
{
|
||||
puts("ERROR: Quality must be between 1 and 100.");
|
||||
exit(1);
|
||||
}
|
||||
if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1
|
||||
&& sscanf(&temp[1], "%d", &maxqual)==1 && maxqual>minqual && maxqual>=1
|
||||
&& maxqual<=100) {}
|
||||
else maxqual=minqual;
|
||||
}
|
||||
|
||||
if(argc>minarg)
|
||||
{
|
||||
for(i=minarg; i<argc; i++)
|
||||
{
|
||||
if(!strcasecmp(argv[i], "-tile"))
|
||||
{
|
||||
dotile=1;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-forcesse3"))
|
||||
{
|
||||
printf("Forcing SSE3 code\n\n");
|
||||
flags|=TJFLAG_FORCESSE3;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-forcesse2"))
|
||||
{
|
||||
printf("Forcing SSE2 code\n\n");
|
||||
flags|=TJFLAG_FORCESSE2;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-forcesse"))
|
||||
{
|
||||
printf("Forcing SSE code\n\n");
|
||||
flags|=TJFLAG_FORCESSE;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-forcemmx"))
|
||||
{
|
||||
printf("Forcing MMX code\n\n");
|
||||
flags|=TJFLAG_FORCEMMX;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-fastupsample"))
|
||||
{
|
||||
printf("Using fast upsampling code\n\n");
|
||||
flags|=TJFLAG_FASTUPSAMPLE;
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-rgb")) pf=TJPF_RGB;
|
||||
if(!strcasecmp(argv[i], "-rgbx")) pf=TJPF_RGBX;
|
||||
if(!strcasecmp(argv[i], "-bgr")) pf=TJPF_BGR;
|
||||
if(!strcasecmp(argv[i], "-bgrx")) pf=TJPF_BGRX;
|
||||
if(!strcasecmp(argv[i], "-xbgr")) pf=TJPF_XBGR;
|
||||
if(!strcasecmp(argv[i], "-xrgb")) pf=TJPF_XRGB;
|
||||
if(!strcasecmp(argv[i], "-bottomup")) flags|=TJFLAG_BOTTOMUP;
|
||||
if(!strcasecmp(argv[i], "-quiet")) quiet=1;
|
||||
if(!strcasecmp(argv[i], "-qq")) quiet=2;
|
||||
if(!strcasecmp(argv[i], "-scale") && i<argc-1)
|
||||
{
|
||||
int temp1=0, temp2=0, match=0;
|
||||
if(sscanf(argv[++i], "%d/%d", &temp1, &temp2)==2)
|
||||
{
|
||||
for(j=0; j<nsf; j++)
|
||||
{
|
||||
if(temp1==scalingfactors[j].num && temp2==scalingfactors[j].denom)
|
||||
{
|
||||
sf=scalingfactors[j];
|
||||
match=1; break;
|
||||
}
|
||||
}
|
||||
if(!match) usage(argv[0]);
|
||||
}
|
||||
else usage(argv[0]);
|
||||
}
|
||||
if(!strcasecmp(argv[i], "-benchtime") && i<argc-1)
|
||||
{
|
||||
double temp=atof(argv[++i]);
|
||||
if(temp>0.0) benchtime=temp;
|
||||
else usage(argv[0]);
|
||||
}
|
||||
if(!strcmp(argv[i], "-?")) usage(argv[0]);
|
||||
if(!strcasecmp(argv[i], "-bmp")) ext="bmp";
|
||||
}
|
||||
}
|
||||
|
||||
if((sf.num!=1 || sf.denom!=1) && dotile)
|
||||
{
|
||||
printf("Disabling tiled compression/decompression tests, because those tests do not\n");
|
||||
printf("work when scaled decompression is enabled.\n");
|
||||
dotile=0;
|
||||
}
|
||||
|
||||
if(!decomponly)
|
||||
{
|
||||
if(loadbmp(argv[1], &srcbuf, &w, &h, bmpPF[pf], 1,
|
||||
(flags&TJFLAG_BOTTOMUP)!=0)==-1)
|
||||
_throwbmp("loading bitmap");
|
||||
temp=strrchr(argv[1], '.');
|
||||
if(temp!=NULL) *temp='\0';
|
||||
}
|
||||
|
||||
if(quiet==1 && !decomponly)
|
||||
{
|
||||
printf("All performance values in Mpixels/sec\n\n");
|
||||
printf("Bitmap\tBitmap\tJPEG\tJPEG\t%s %s \tComp\tComp\tDecomp\n",
|
||||
dotile? "Tile ":"Image", dotile? "Tile ":"Image");
|
||||
printf("Format\tOrder\tSubsamp\tQual\tWidth Height\tPerf \tRatio\tPerf\n\n");
|
||||
}
|
||||
|
||||
if(decomponly)
|
||||
{
|
||||
dodecomptest(argv[1]);
|
||||
printf("\n");
|
||||
goto bailout;
|
||||
}
|
||||
for(i=maxqual; i>=minqual; i--)
|
||||
dotest(srcbuf, w, h, TJ_GRAYSCALE, i, argv[1]);
|
||||
printf("\n");
|
||||
for(i=maxqual; i>=minqual; i--)
|
||||
dotest(srcbuf, w, h, TJ_420, i, argv[1]);
|
||||
printf("\n");
|
||||
for(i=maxqual; i>=minqual; i--)
|
||||
dotest(srcbuf, w, h, TJ_422, i, argv[1]);
|
||||
printf("\n");
|
||||
for(i=maxqual; i>=minqual; i--)
|
||||
dotest(srcbuf, w, h, TJ_444, i, argv[1]);
|
||||
printf("\n");
|
||||
|
||||
bailout:
|
||||
if(srcbuf) free(srcbuf);
|
||||
return retval;
|
||||
}
|
@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C)2009-2012 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program tests the various code paths in the TurboJPEG C Wrapper
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "./tjutil.h"
|
||||
#include "./turbojpeg.h"
|
||||
#ifdef _WIN32
|
||||
#include <time.h>
|
||||
#define random() rand()
|
||||
#endif
|
||||
|
||||
|
||||
#define _throwtj() {printf("TurboJPEG ERROR:\n%s\n", tjGetErrorStr()); \
|
||||
bailout();}
|
||||
#define _tj(f) {if((f)==-1) _throwtj();}
|
||||
#define _throw(m) {printf("ERROR: %s\n", m); bailout();}
|
||||
|
||||
const char *subNameLong[TJ_NUMSAMP]=
|
||||
{
|
||||
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0"
|
||||
};
|
||||
const char *subName[TJ_NUMSAMP]={"444", "422", "420", "GRAY", "440"};
|
||||
|
||||
const char *pixFormatStr[TJ_NUMPF]=
|
||||
{
|
||||
"RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale",
|
||||
"RGBA", "BGRA", "ABGR", "ARGB"
|
||||
};
|
||||
|
||||
const int alphaOffset[TJ_NUMPF] = {-1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0};
|
||||
|
||||
const int _3byteFormats[]={TJPF_RGB, TJPF_BGR};
|
||||
const int _4byteFormats[]={TJPF_RGBX, TJPF_BGRX, TJPF_XBGR, TJPF_XRGB};
|
||||
const int _onlyGray[]={TJPF_GRAY};
|
||||
const int _onlyRGB[]={TJPF_RGB};
|
||||
|
||||
int exitStatus=0;
|
||||
#define bailout() {exitStatus=-1; goto bailout;}
|
||||
|
||||
|
||||
void initBuf(unsigned char *buf, int w, int h, int pf, int flags)
|
||||
{
|
||||
int roffset=tjRedOffset[pf];
|
||||
int goffset=tjGreenOffset[pf];
|
||||
int boffset=tjBlueOffset[pf];
|
||||
int ps=tjPixelSize[pf];
|
||||
int index, row, col, halfway=16;
|
||||
|
||||
memset(buf, 0, w*h*ps);
|
||||
if(pf==TJPF_GRAY)
|
||||
{
|
||||
for(row=0; row<h; row++)
|
||||
{
|
||||
for(col=0; col<w; col++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col;
|
||||
else index=row*w+col;
|
||||
if(((row/8)+(col/8))%2==0) buf[index]=(row<halfway)? 255:0;
|
||||
else buf[index]=(row<halfway)? 76:226;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(row=0; row<h; row++)
|
||||
{
|
||||
for(col=0; col<w; col++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col;
|
||||
else index=row*w+col;
|
||||
if(((row/8)+(col/8))%2==0)
|
||||
{
|
||||
if(row<halfway)
|
||||
{
|
||||
buf[index*ps+roffset]=255;
|
||||
buf[index*ps+goffset]=255;
|
||||
buf[index*ps+boffset]=255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[index*ps+roffset]=255;
|
||||
if(row>=halfway) buf[index*ps+goffset]=255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define checkval(v, cv) { \
|
||||
if(v<cv-1 || v>cv+1) { \
|
||||
printf("\nComp. %s at %d,%d should be %d, not %d\n", \
|
||||
#v, row, col, cv, v); \
|
||||
retval=0; exitStatus=-1; goto bailout; \
|
||||
}}
|
||||
|
||||
#define checkval0(v) { \
|
||||
if(v>1) { \
|
||||
printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \
|
||||
retval=0; exitStatus=-1; goto bailout; \
|
||||
}}
|
||||
|
||||
#define checkval255(v) { \
|
||||
if(v<254) { \
|
||||
printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \
|
||||
retval=0; exitStatus=-1; goto bailout; \
|
||||
}}
|
||||
|
||||
|
||||
int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp,
|
||||
tjscalingfactor sf, int flags)
|
||||
{
|
||||
int roffset=tjRedOffset[pf];
|
||||
int goffset=tjGreenOffset[pf];
|
||||
int boffset=tjBlueOffset[pf];
|
||||
int aoffset=alphaOffset[pf];
|
||||
int ps=tjPixelSize[pf];
|
||||
int index, row, col, retval=1;
|
||||
int halfway=16*sf.num/sf.denom;
|
||||
int blocksize=8*sf.num/sf.denom;
|
||||
|
||||
for(row=0; row<h; row++)
|
||||
{
|
||||
for(col=0; col<w; col++)
|
||||
{
|
||||
unsigned char r, g, b, a;
|
||||
if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col;
|
||||
else index=row*w+col;
|
||||
r=buf[index*ps+roffset];
|
||||
g=buf[index*ps+goffset];
|
||||
b=buf[index*ps+boffset];
|
||||
a=aoffset>=0? buf[index*ps+aoffset]:0xFF;
|
||||
if(((row/blocksize)+(col/blocksize))%2==0)
|
||||
{
|
||||
if(row<halfway)
|
||||
{
|
||||
checkval255(r); checkval255(g); checkval255(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkval0(r); checkval0(g); checkval0(b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(subsamp==TJSAMP_GRAY)
|
||||
{
|
||||
if(row<halfway)
|
||||
{
|
||||
checkval(r, 76); checkval(g, 76); checkval(b, 76);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkval(r, 226); checkval(g, 226); checkval(b, 226);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(row<halfway)
|
||||
{
|
||||
checkval255(r); checkval0(g); checkval0(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkval255(r); checkval255(g); checkval0(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkval255(a);
|
||||
}
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(retval==0)
|
||||
{
|
||||
printf("\n");
|
||||
for(row=0; row<h; row++)
|
||||
{
|
||||
for(col=0; col<w; col++)
|
||||
{
|
||||
printf("%.3d/%.3d/%.3d ", buf[(row*w+col)*ps+roffset],
|
||||
buf[(row*w+col)*ps+goffset], buf[(row*w+col)*ps+boffset]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, char *filename)
|
||||
{
|
||||
FILE *file=fopen(filename, "wb");
|
||||
if(!file || fwrite(jpegBuf, jpegSize, 1, file)!=1)
|
||||
{
|
||||
printf("ERROR: Could not write to %s.\n%s\n", filename, strerror(errno));
|
||||
bailout();
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(file) fclose(file);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
double t;
|
||||
|
||||
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(*dstBuf && *dstSize>0) memset(*dstBuf, 0, *dstSize);
|
||||
|
||||
t=gettime();
|
||||
*dstSize=tjBufSize(w, h, subsamp);
|
||||
_tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp,
|
||||
jpegQual, flags));
|
||||
t=gettime()-t;
|
||||
|
||||
snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename,
|
||||
pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subName[subsamp],
|
||||
jpegQual);
|
||||
writeJPEG(*dstBuf, *dstSize, tempStr);
|
||||
printf("Done.");
|
||||
printf(" %f ms\n Result in %s\n", t*1000., tempStr);
|
||||
|
||||
bailout:
|
||||
if(srcBuf) free(srcBuf);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
int _hdrw=0, _hdrh=0, _hdrsubsamp=-1; double t;
|
||||
int scaledWidth=TJSCALED(w, sf);
|
||||
int scaledHeight=TJSCALED(h, sf);
|
||||
unsigned long dstSize=0;
|
||||
|
||||
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");
|
||||
|
||||
dstSize=scaledWidth*scaledHeight*tjPixelSize[pf];
|
||||
if((dstBuf=(unsigned char *)malloc(dstSize))==NULL)
|
||||
_throw("Memory allocation failure");
|
||||
memset(dstBuf, 0, dstSize);
|
||||
|
||||
t=gettime();
|
||||
_tj(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0,
|
||||
scaledHeight, pf, flags));
|
||||
t=gettime()-t;
|
||||
|
||||
if(checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags))
|
||||
printf("Passed.");
|
||||
else printf("FAILED!");
|
||||
printf(" %f ms\n", t*1000.);
|
||||
|
||||
bailout:
|
||||
if(dstBuf) free(dstBuf);
|
||||
}
|
||||
|
||||
|
||||
void decompTest(tjhandle handle, unsigned char *jpegBuf,
|
||||
unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp,
|
||||
int flags)
|
||||
{
|
||||
int i, n=0;
|
||||
tjscalingfactor *sf=tjGetScalingFactors(&n), sf1={1, 1};
|
||||
if(!sf || !n) _throwtj();
|
||||
|
||||
if((subsamp==TJSAMP_444 || subsamp==TJSAMP_GRAY))
|
||||
{
|
||||
for(i=0; i<n; i++)
|
||||
_decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp,
|
||||
flags, sf[i]);
|
||||
}
|
||||
else
|
||||
_decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, flags,
|
||||
sf1);
|
||||
|
||||
bailout:
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void doTest(int w, int h, const int *formats, int nformats, int subsamp,
|
||||
char *basename)
|
||||
{
|
||||
tjhandle chandle=NULL, dhandle=NULL;
|
||||
unsigned char *dstBuf=NULL;
|
||||
unsigned long size=0; int pfi, pf, i;
|
||||
|
||||
size=tjBufSize(w, h, subsamp);
|
||||
if((dstBuf=(unsigned char *)malloc(size))==NULL)
|
||||
_throw("Memory allocation failure.");
|
||||
|
||||
if((chandle=tjInitCompress())==NULL || (dhandle=tjInitDecompress())==NULL)
|
||||
_throwtj();
|
||||
|
||||
for(pfi=0; pfi<nformats; pfi++)
|
||||
{
|
||||
for(i=0; i<2; i++)
|
||||
{
|
||||
int flags=0;
|
||||
if(subsamp==TJSAMP_422 || subsamp==TJSAMP_420 || subsamp==TJSAMP_440)
|
||||
flags|=TJFLAG_FASTUPSAMPLE;
|
||||
if(i==1) flags|=TJFLAG_BOTTOMUP;
|
||||
pf=formats[pfi];
|
||||
compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, 100,
|
||||
flags);
|
||||
decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp,
|
||||
flags);
|
||||
if(pf>=TJPF_RGBX && pf<=TJPF_XRGB)
|
||||
decompTest(dhandle, dstBuf, size, w, h, pf+(TJPF_RGBA-TJPF_RGBX),
|
||||
basename, subsamp, flags);
|
||||
}
|
||||
}
|
||||
|
||||
bailout:
|
||||
if(chandle) tjDestroy(chandle);
|
||||
if(dhandle) tjDestroy(dhandle);
|
||||
|
||||
if(dstBuf) free(dstBuf);
|
||||
}
|
||||
|
||||
|
||||
void bufSizeTest(void)
|
||||
{
|
||||
int w, h, i, subsamp;
|
||||
unsigned char *srcBuf=NULL, *jpegBuf=NULL;
|
||||
tjhandle handle=NULL;
|
||||
unsigned long jpegSize=0;
|
||||
|
||||
if((handle=tjInitCompress())==NULL) _throwtj();
|
||||
|
||||
printf("Buffer size regression test\n");
|
||||
for(subsamp=0; subsamp<TJ_NUMSAMP; subsamp++)
|
||||
{
|
||||
for(w=1; w<48; w++)
|
||||
{
|
||||
int maxh=(w==1)? 2048:48;
|
||||
for(h=1; h<maxh; h++)
|
||||
{
|
||||
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((jpegBuf=(unsigned char *)malloc(tjBufSize(w, h, subsamp)))
|
||||
==NULL)
|
||||
_throw("Memory allocation failure");
|
||||
jpegSize=tjBufSize(w, h, subsamp);
|
||||
|
||||
for(i=0; i<w*h*4; i++)
|
||||
{
|
||||
if(random()<RAND_MAX/2) srcBuf[i]=0;
|
||||
else srcBuf[i]=255;
|
||||
}
|
||||
|
||||
_tj(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &jpegBuf,
|
||||
&jpegSize, subsamp, 100, 0));
|
||||
free(srcBuf); srcBuf=NULL;
|
||||
free(jpegBuf); jpegBuf=NULL;
|
||||
|
||||
if((srcBuf=(unsigned char *)malloc(h*w*4))==NULL)
|
||||
_throw("Memory allocation failure");
|
||||
if((jpegBuf=(unsigned char *)malloc(tjBufSize(h, w, subsamp)))
|
||||
==NULL)
|
||||
_throw("Memory allocation failure");
|
||||
jpegSize=tjBufSize(h, w, subsamp);
|
||||
|
||||
for(i=0; i<h*w*4; i++)
|
||||
{
|
||||
if(random()<RAND_MAX/2) srcBuf[i]=0;
|
||||
else srcBuf[i]=255;
|
||||
}
|
||||
|
||||
_tj(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &jpegBuf,
|
||||
&jpegSize, subsamp, 100, 0));
|
||||
free(srcBuf); srcBuf=NULL;
|
||||
free(jpegBuf); jpegBuf=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Done. \n");
|
||||
|
||||
bailout:
|
||||
if(srcBuf) free(srcBuf);
|
||||
if(jpegBuf) free(jpegBuf);
|
||||
if(handle) tjDestroy(handle);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef _WIN32
|
||||
srand((unsigned int)time(NULL));
|
||||
#endif
|
||||
doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test");
|
||||
doTest(39, 41, _4byteFormats, 4, TJSAMP_444, "test");
|
||||
doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test");
|
||||
doTest(35, 39, _4byteFormats, 4, TJSAMP_422, "test");
|
||||
doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test");
|
||||
doTest(41, 35, _4byteFormats, 4, TJSAMP_420, "test");
|
||||
doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test");
|
||||
doTest(39, 41, _4byteFormats, 4, TJSAMP_440, "test");
|
||||
doTest(35, 39, _onlyGray, 1, TJSAMP_GRAY, "test");
|
||||
doTest(39, 41, _3byteFormats, 2, TJSAMP_GRAY, "test");
|
||||
doTest(41, 35, _4byteFormats, 4, TJSAMP_GRAY, "test");
|
||||
bufSizeTest();
|
||||
|
||||
return exitStatus;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C)2011 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static double getfreq(void)
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
if(!QueryPerformanceFrequency(&freq)) return 0.0;
|
||||
return (double)freq.QuadPart;
|
||||
}
|
||||
|
||||
static double f=-1.0;
|
||||
|
||||
double gettime(void)
|
||||
{
|
||||
LARGE_INTEGER t;
|
||||
if(f<0.0) f=getfreq();
|
||||
if(f==0.0) return (double)GetTickCount()/1000.;
|
||||
else
|
||||
{
|
||||
QueryPerformanceCounter(&t);
|
||||
return (double)t.QuadPart/f;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
double gettime(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
if(gettimeofday(&tv, NULL)<0) return 0.0;
|
||||
else return (double)tv.tv_sec+((double)tv.tv_usec/1000000.);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C)2011 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef __MINGW32__
|
||||
#include <stdio.h>
|
||||
#define snprintf(str, n, format, ...) \
|
||||
_snprintf_s(str, n, _TRUNCATE, format, __VA_ARGS__)
|
||||
#endif
|
||||
#define strcasecmp stricmp
|
||||
#define strncasecmp strnicmp
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
extern double gettime(void);
|
Loading…
Reference in New Issue