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