2010-03-03 17:02:51 +00:00
/* stbi-1.18 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
2010-01-05 18:02:29 +00:00
when you control the images you ' re loading
QUICK NOTES :
Primarily of interest to game developers and other people who can
avoid problematic images and only need the trivial interface
JPEG baseline ( no JPEG progressive , no oddball channel decimations )
2010-03-03 17:02:51 +00:00
PNG 8 - bit only
2010-01-05 18:02:29 +00:00
BMP non - 1 bpp , non - RLE
TGA ( not sure what subset , if a subset )
PSD ( composited view only , no extra channels )
HDR ( radiance rgbE format )
writes BMP , TGA ( define STBI_NO_WRITE to remove code )
decoded from memory or through stdio FILE ( define STBI_NO_STDIO to remove code )
supports installable dequantizing - IDCT , YCbCr - to - RGB conversion ( define STBI_SIMD )
TODO :
stbi_info_ *
history :
2010-03-03 17:02:51 +00:00
1.18 fix a threading bug ( local mutable static )
1.17 support interlaced PNG
1.16 major bugfix - convert_format converted one too many pixels
1.15 initialize some fields for thread safety
1.14 fix threadsafe conversion bug ; header - file - only version ( # define STBI_HEADER_FILE_ONLY before including )
1.13 threadsafe
2010-01-05 18:02:29 +00:00
1.12 const qualifiers in the API
1.11 Support installable IDCT , colorspace conversion routines
1.10 Fixes for 64 - bit ( don ' t use " unsigned long " )
optimized upsampling by Fabian " ryg " Giesen
1.09 Fix format - conversion for PSD code ( bad global variables ! )
1.08 Thatcher Ulrich ' s PSD code integrated by Nicolas Schulz
1.07 attempt to fix C + + warning / errors again
1.06 attempt to fix C + + warning / errors again
1.05 fix TGA loading to return correct * comp and use good luminance calc
1.04 default float alpha is 1 , not 255 ; use ' void * ' for stbi_image_free
1.03 bugfixes to STBI_NO_STDIO , STBI_NO_HDR
1.02 support for ( subset of ) HDR files , float interface for preferred access to them
1.01 fix bug : possible bug in handling right - side up bmps . . . not sure
fix bug : the stbi_bmp_load ( ) and stbi_tga_load ( ) functions didn ' t work at all
1.00 interface to zlib that skips zlib header
0.99 correct handling of alpha in palette
0.98 TGA loader by lonesock ; dynamically add loaders ( untested )
0.97 jpeg errors on too large a file ; also catch another malloc failure
0.96 fix detection of invalid v value - particleman @ mollyrocket forum
0.95 during header scan , seek to markers in case of padding
0.94 STBI_NO_STDIO to disable stdio usage ; rename all # defines the same
0.93 handle jpegtran output ; verbose errors
0.92 read 4 , 8 , 16 , 24 , 32 - bit BMP files of several formats
0.91 output 24 - bit Windows 3.0 BMP files
0.90 fix a few more warnings ; bump version number to approach 1.0
0.61 bugfixes due to Marc LeBlanc , Christopher Lloyd
0.60 fix compiling as c + +
0.59 fix warnings : merge Dave Moore ' s - Wall fixes
0.58 fix bug : zlib uncompressed mode len / nlen was wrong endian
0.57 fix bug : jpg last huffman symbol before marker was > 9 bits but less
than 16 available
0.56 fix bug : zlib uncompressed mode len vs . nlen
0.55 fix bug : restart_interval not initialized to 0
0.54 allow NULL for ' int * comp '
0.53 fix bug in png 3 - > 4 ; speedup png decoding
0.52 png handles req_comp = 3 , 4 directly ; minor cleanup ; jpeg comments
0.51 obey req_comp requests , 1 - component jpegs return as 1 - component ,
on ' test ' only check type , not whether we support this variant
*/
2011-08-04 14:09:17 +00:00
# include <glib.h>
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
# ifndef STBI_INCLUDE_STB_IMAGE_H
# define STBI_INCLUDE_STB_IMAGE_H
2010-01-05 18:02:29 +00:00
//// begin header file ////////////////////////////////////////////////////
//
// Limitations:
// - no progressive/interlaced support (jpeg, png)
// - 8-bit samples only (jpeg, png)
// - not threadsafe
// - channel subsampling of at most 2 in each dimension (jpeg)
// - no delayed line count (jpeg) -- IJG doesn't support either
//
// Basic usage (see HDR discussion below):
// int x,y,n;
// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
// // ... process data if not NULL ...
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// stbi_image_free(data)
//
// Standard parameters:
// int *x -- outputs image width in pixels
// int *y -- outputs image height in pixels
// int *comp -- outputs # of image components in image file
// int req_comp -- if non-zero, # of image components requested in result
//
// The return value from an image loader is an 'unsigned char *' which points
// to the pixel data. The pixel data consists of *y scanlines of *x pixels,
// with each pixel consisting of N interleaved 8-bit components; the first
// pixel pointed to is top-left-most in the image. There is no padding between
// image scanlines or between pixels, regardless of format. The number of
// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise.
// If req_comp is non-zero, *comp has the number of components that _would_
// have been output otherwise. E.g. if you set req_comp to 4, you will always
// get RGBA output, but you can check *comp to easily see if it's opaque.
//
// An output image with N components has the following components interleaved
// in this order in each pixel:
//
// N=#comp components
// 1 grey
// 2 grey, alpha
// 3 red, green, blue
// 4 red, green, blue, alpha
//
// If image loading fails for any reason, the return value will be NULL,
// and *x, *y, *comp will be unchanged. The function stbi_failure_reason()
// can be queried for an extremely brief, end-user unfriendly explanation
// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid
// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
// more user-friendly ones.
//
// Paletted PNG and BMP images are automatically depalettized.
//
//
// ===========================================================================
//
// HDR image support (disable by defining STBI_NO_HDR)
//
// stb_image now supports loading HDR images in general, and currently
// the Radiance .HDR file format, although the support is provided
// generically. You can still load any file through the existing interface;
// if you attempt to load an HDR file, it will be automatically remapped to
// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
// both of these constants can be reconfigured through this interface:
//
// stbi_hdr_to_ldr_gamma(2.2f);
// stbi_hdr_to_ldr_scale(1.0f);
//
// (note, do not use _inverse_ constants; stbi_image will invert them
// appropriately).
//
// Additionally, there is a new, parallel interface for loading files as
// (linear) floats to preserve the full dynamic range:
//
// float *data = stbi_loadf(filename, &x, &y, &n, 0);
//
// If you load LDR images through this interface, those images will
// be promoted to floating point values, run through the inverse of
// constants corresponding to the above:
//
// stbi_ldr_to_hdr_scale(1.0f);
// stbi_ldr_to_hdr_gamma(2.2f);
//
// Finally, given a filename (or an open file or memory block--see header
// file for details) containing image data, you can query for the "most
// appropriate" interface to use (that is, whether the image is HDR or
// not), using:
//
// stbi_is_hdr(char *filename);
# ifndef STBI_NO_STDIO
# include <stdio.h>
# endif
2010-03-03 17:02:51 +00:00
# define STBI_VERSION 1
2010-01-05 18:02:29 +00:00
enum
{
STBI_default = 0 , // only used for req_comp
STBI_grey = 1 ,
STBI_grey_alpha = 2 ,
STBI_rgb = 3 ,
STBI_rgb_alpha = 4 ,
} ;
typedef unsigned char stbi_uc ;
# ifdef __cplusplus
extern " C " {
# endif
// WRITING API
# if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO)
// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding)
// (you must include the appropriate extension in the filename).
// returns TRUE on success, FALSE if couldn't open file, error writing file
extern int stbi_write_bmp ( char const * filename , int x , int y , int comp , void * data ) ;
extern int stbi_write_tga ( char const * filename , int x , int y , int comp , void * data ) ;
# endif
// PRIMARY API - works on images of any type
// load image by filename, open file, or memory buffer
# ifndef STBI_NO_STDIO
extern stbi_uc * stbi_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern stbi_uc * stbi_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
extern stbi_uc * stbi_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
// for stbi_load_from_file, file pointer is left pointing immediately after image
# ifndef STBI_NO_HDR
# ifndef STBI_NO_STDIO
extern float * stbi_loadf ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern float * stbi_loadf_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
extern float * stbi_loadf_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
extern void stbi_hdr_to_ldr_gamma ( float gamma ) ;
extern void stbi_hdr_to_ldr_scale ( float scale ) ;
extern void stbi_ldr_to_hdr_gamma ( float gamma ) ;
extern void stbi_ldr_to_hdr_scale ( float scale ) ;
# endif // STBI_NO_HDR
// get a VERY brief reason for failure
2010-03-03 17:02:51 +00:00
// NOT THREADSAFE
extern char * stbi_failure_reason ( void ) ;
2010-01-05 18:02:29 +00:00
// free the loaded image -- this is just free()
extern void stbi_image_free ( void * retval_from_stbi_load ) ;
// get image dimensions & components without fully decoding
extern int stbi_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
extern int stbi_is_hdr_from_memory ( stbi_uc const * buffer , int len ) ;
# ifndef STBI_NO_STDIO
extern int stbi_info ( char const * filename , int * x , int * y , int * comp ) ;
extern int stbi_is_hdr ( char const * filename ) ;
extern int stbi_is_hdr_from_file ( FILE * f ) ;
# endif
// ZLIB client - used by PNG, available for other purposes
2010-03-03 17:02:51 +00:00
extern char * stbi_zlib_decode_malloc_guesssize ( const char * buffer , int len , int initial_size , int * outlen ) ;
2010-01-05 18:02:29 +00:00
extern char * stbi_zlib_decode_malloc ( const char * buffer , int len , int * outlen ) ;
extern int stbi_zlib_decode_buffer ( char * obuffer , int olen , const char * ibuffer , int ilen ) ;
extern char * stbi_zlib_decode_noheader_malloc ( const char * buffer , int len , int * outlen ) ;
extern int stbi_zlib_decode_noheader_buffer ( char * obuffer , int olen , const char * ibuffer , int ilen ) ;
// TYPE-SPECIFIC ACCESS
// is it a jpeg?
extern int stbi_jpeg_test_memory ( stbi_uc const * buffer , int len ) ;
extern stbi_uc * stbi_jpeg_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_jpeg_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
# ifndef STBI_NO_STDIO
extern stbi_uc * stbi_jpeg_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_jpeg_test_file ( FILE * f ) ;
extern stbi_uc * stbi_jpeg_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_jpeg_info ( char const * filename , int * x , int * y , int * comp ) ;
extern int stbi_jpeg_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
// is it a png?
extern int stbi_png_test_memory ( stbi_uc const * buffer , int len ) ;
extern stbi_uc * stbi_png_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_png_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
# ifndef STBI_NO_STDIO
extern stbi_uc * stbi_png_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_png_info ( char const * filename , int * x , int * y , int * comp ) ;
extern int stbi_png_test_file ( FILE * f ) ;
extern stbi_uc * stbi_png_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
extern int stbi_png_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
// is it a bmp?
extern int stbi_bmp_test_memory ( stbi_uc const * buffer , int len ) ;
extern stbi_uc * stbi_bmp_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern stbi_uc * stbi_bmp_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
# ifndef STBI_NO_STDIO
extern int stbi_bmp_test_file ( FILE * f ) ;
extern stbi_uc * stbi_bmp_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
// is it a tga?
extern int stbi_tga_test_memory ( stbi_uc const * buffer , int len ) ;
extern stbi_uc * stbi_tga_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern stbi_uc * stbi_tga_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
# ifndef STBI_NO_STDIO
extern int stbi_tga_test_file ( FILE * f ) ;
extern stbi_uc * stbi_tga_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
// is it a psd?
extern int stbi_psd_test_memory ( stbi_uc const * buffer , int len ) ;
extern stbi_uc * stbi_psd_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern stbi_uc * stbi_psd_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
# ifndef STBI_NO_STDIO
extern int stbi_psd_test_file ( FILE * f ) ;
extern stbi_uc * stbi_psd_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
// is it an hdr?
extern int stbi_hdr_test_memory ( stbi_uc const * buffer , int len ) ;
extern float * stbi_hdr_load ( char const * filename , int * x , int * y , int * comp , int req_comp ) ;
extern float * stbi_hdr_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
# ifndef STBI_NO_STDIO
extern int stbi_hdr_test_file ( FILE * f ) ;
extern float * stbi_hdr_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
// define new loaders
typedef struct
{
int ( * test_memory ) ( stbi_uc const * buffer , int len ) ;
stbi_uc * ( * load_from_memory ) ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp ) ;
# ifndef STBI_NO_STDIO
int ( * test_file ) ( FILE * f ) ;
stbi_uc * ( * load_from_file ) ( FILE * f , int * x , int * y , int * comp , int req_comp ) ;
# endif
} stbi_loader ;
// register a loader by filling out the above structure (you must defined ALL functions)
// returns 1 if added or already added, 0 if not added (too many loaders)
2010-03-03 17:02:51 +00:00
// NOT THREADSAFE
2010-01-05 18:02:29 +00:00
extern int stbi_register_loader ( stbi_loader * loader ) ;
// define faster low-level operations (typically SIMD support)
# if STBI_SIMD
2011-08-04 14:09:17 +00:00
typedef void ( * stbi_idct_8x8 ) ( guint8 * out , int out_stride , short data [ 64 ] , unsigned short * dequantize ) ;
2010-01-05 18:02:29 +00:00
// compute an integer IDCT on "input"
// input[x] = data[x] * dequantize[x]
// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride'
// CLAMP results to 0..255
2011-08-04 14:09:17 +00:00
typedef void ( * stbi_YCbCr_to_RGB_run ) ( guint8 * output , guint8 const * y , guint8 const * cb , guint8 const * cr , int count , int step ) ;
2010-01-05 18:02:29 +00:00
// compute a conversion from YCbCr to RGB
// 'count' pixels
// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B
// y: Y input channel
// cb: Cb input channel; scale/biased to be 0..255
// cr: Cr input channel; scale/biased to be 0..255
extern void stbi_install_idct ( stbi_idct_8x8 func ) ;
extern void stbi_install_YCbCr_to_RGB ( stbi_YCbCr_to_RGB_run func ) ;
# endif // STBI_SIMD
# ifdef __cplusplus
}
# endif
//
//
//// end header file /////////////////////////////////////////////////////
2010-03-03 17:02:51 +00:00
# endif // STBI_INCLUDE_STB_IMAGE_H
# ifndef STBI_HEADER_FILE_ONLY
# ifndef STBI_NO_HDR
# include <math.h> // ldexp
# include <string.h> // strcmp
# endif
2010-01-05 18:02:29 +00:00
# ifndef STBI_NO_STDIO
# include <stdio.h>
# endif
# include <stdlib.h>
# include <memory.h>
# include <assert.h>
# include <stdarg.h>
# ifndef _MSC_VER
2010-03-03 17:02:51 +00:00
# ifdef __cplusplus
# define __forceinline inline
# else
# define __forceinline
# endif
2010-01-05 18:02:29 +00:00
# endif
// should produce compiler error if size is wrong
2011-08-04 14:09:17 +00:00
typedef unsigned char validate_guint32 [ sizeof ( guint32 ) = = 4 ] ;
2010-01-05 18:02:29 +00:00
# if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE)
# define STBI_NO_WRITE
# endif
//////////////////////////////////////////////////////////////////////////////
//
// Generic API that works on all image types
//
2010-03-03 17:02:51 +00:00
// this is not threadsafe
2010-01-05 18:02:29 +00:00
static char * failure_reason ;
char * stbi_failure_reason ( void )
{
return failure_reason ;
}
static int e ( char * str )
{
failure_reason = str ;
return 0 ;
}
# ifdef STBI_NO_FAILURE_STRINGS
# define e(x,y) 0
# elif defined(STBI_FAILURE_USERMSG)
# define e(x,y) e(y)
# else
# define e(x,y) e(x)
# endif
# define epf(x,y) ((float *) (e(x,y)?NULL:NULL))
# define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL))
void stbi_image_free ( void * retval_from_stbi_load )
{
free ( retval_from_stbi_load ) ;
}
# define MAX_LOADERS 32
stbi_loader * loaders [ MAX_LOADERS ] ;
static int max_loaders = 0 ;
int stbi_register_loader ( stbi_loader * loader )
{
int i ;
for ( i = 0 ; i < MAX_LOADERS ; + + i ) {
// already present?
if ( loaders [ i ] = = loader )
return 1 ;
// end of the list?
if ( loaders [ i ] = = NULL ) {
loaders [ i ] = loader ;
max_loaders = i + 1 ;
return 1 ;
}
}
// no room for it
return 0 ;
}
# ifndef STBI_NO_HDR
static float * ldr_to_hdr ( stbi_uc * data , int x , int y , int comp ) ;
static stbi_uc * hdr_to_ldr ( float * data , int x , int y , int comp ) ;
# endif
# ifndef STBI_NO_STDIO
unsigned char * stbi_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
FILE * f = fopen ( filename , " rb " ) ;
unsigned char * result ;
if ( ! f ) return epuc ( " can't fopen " , " Unable to open file " ) ;
result = stbi_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return result ;
}
unsigned char * stbi_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
int i ;
if ( stbi_jpeg_test_file ( f ) )
return stbi_jpeg_load_from_file ( f , x , y , comp , req_comp ) ;
if ( stbi_png_test_file ( f ) )
return stbi_png_load_from_file ( f , x , y , comp , req_comp ) ;
if ( stbi_bmp_test_file ( f ) )
return stbi_bmp_load_from_file ( f , x , y , comp , req_comp ) ;
if ( stbi_psd_test_file ( f ) )
return stbi_psd_load_from_file ( f , x , y , comp , req_comp ) ;
# ifndef STBI_NO_HDR
if ( stbi_hdr_test_file ( f ) ) {
float * hdr = stbi_hdr_load_from_file ( f , x , y , comp , req_comp ) ;
return hdr_to_ldr ( hdr , * x , * y , req_comp ? req_comp : * comp ) ;
}
# endif
for ( i = 0 ; i < max_loaders ; + + i )
if ( loaders [ i ] - > test_file ( f ) )
return loaders [ i ] - > load_from_file ( f , x , y , comp , req_comp ) ;
// test tga last because it's a crappy test!
if ( stbi_tga_test_file ( f ) )
return stbi_tga_load_from_file ( f , x , y , comp , req_comp ) ;
return epuc ( " unknown image type " , " Image not of any known type, or corrupt " ) ;
}
# endif
unsigned char * stbi_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
int i ;
if ( stbi_jpeg_test_memory ( buffer , len ) )
return stbi_jpeg_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
if ( stbi_png_test_memory ( buffer , len ) )
return stbi_png_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
if ( stbi_bmp_test_memory ( buffer , len ) )
return stbi_bmp_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
if ( stbi_psd_test_memory ( buffer , len ) )
return stbi_psd_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
# ifndef STBI_NO_HDR
if ( stbi_hdr_test_memory ( buffer , len ) ) {
float * hdr = stbi_hdr_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
return hdr_to_ldr ( hdr , * x , * y , req_comp ? req_comp : * comp ) ;
}
# endif
for ( i = 0 ; i < max_loaders ; + + i )
if ( loaders [ i ] - > test_memory ( buffer , len ) )
return loaders [ i ] - > load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
// test tga last because it's a crappy test!
if ( stbi_tga_test_memory ( buffer , len ) )
return stbi_tga_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
return epuc ( " unknown image type " , " Image not of any known type, or corrupt " ) ;
}
# ifndef STBI_NO_HDR
# ifndef STBI_NO_STDIO
float * stbi_loadf ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
FILE * f = fopen ( filename , " rb " ) ;
float * result ;
if ( ! f ) return epf ( " can't fopen " , " Unable to open file " ) ;
result = stbi_loadf_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return result ;
}
float * stbi_loadf_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
unsigned char * data ;
# ifndef STBI_NO_HDR
if ( stbi_hdr_test_file ( f ) )
return stbi_hdr_load_from_file ( f , x , y , comp , req_comp ) ;
# endif
data = stbi_load_from_file ( f , x , y , comp , req_comp ) ;
if ( data )
return ldr_to_hdr ( data , * x , * y , req_comp ? req_comp : * comp ) ;
return epf ( " unknown image type " , " Image not of any known type, or corrupt " ) ;
}
# endif
float * stbi_loadf_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
stbi_uc * data ;
# ifndef STBI_NO_HDR
if ( stbi_hdr_test_memory ( buffer , len ) )
return stbi_hdr_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
# endif
data = stbi_load_from_memory ( buffer , len , x , y , comp , req_comp ) ;
if ( data )
return ldr_to_hdr ( data , * x , * y , req_comp ? req_comp : * comp ) ;
return epf ( " unknown image type " , " Image not of any known type, or corrupt " ) ;
}
# endif
// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is
// defined, for API simplicity; if STBI_NO_HDR is defined, it always
// reports false!
2010-03-03 17:02:51 +00:00
int stbi_is_hdr_from_memory ( stbi_uc const * buffer , int len )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_HDR
return stbi_hdr_test_memory ( buffer , len ) ;
# else
return 0 ;
# endif
}
# ifndef STBI_NO_STDIO
extern int stbi_is_hdr ( char const * filename )
{
FILE * f = fopen ( filename , " rb " ) ;
int result = 0 ;
if ( f ) {
result = stbi_is_hdr_from_file ( f ) ;
fclose ( f ) ;
}
return result ;
}
extern int stbi_is_hdr_from_file ( FILE * f )
{
# ifndef STBI_NO_HDR
return stbi_hdr_test_file ( f ) ;
# else
return 0 ;
# endif
}
# endif
// @TODO: get image dimensions & components without fully decoding
# ifndef STBI_NO_STDIO
extern int stbi_info ( char const * filename , int * x , int * y , int * comp ) ;
extern int stbi_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
extern int stbi_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
# ifndef STBI_NO_HDR
static float h2l_gamma_i = 1.0f / 2.2f , h2l_scale_i = 1.0f ;
static float l2h_gamma = 2.2f , l2h_scale = 1.0f ;
void stbi_hdr_to_ldr_gamma ( float gamma ) { h2l_gamma_i = 1 / gamma ; }
void stbi_hdr_to_ldr_scale ( float scale ) { h2l_scale_i = 1 / scale ; }
void stbi_ldr_to_hdr_gamma ( float gamma ) { l2h_gamma = gamma ; }
void stbi_ldr_to_hdr_scale ( float scale ) { l2h_scale = scale ; }
# endif
//////////////////////////////////////////////////////////////////////////////
//
// Common code used by all image loaders
//
enum
{
SCAN_load = 0 ,
SCAN_type ,
SCAN_header ,
} ;
2010-03-03 17:02:51 +00:00
typedef struct
{
2011-08-04 14:09:17 +00:00
guint32 img_x , img_y ;
2010-03-03 17:02:51 +00:00
int img_n , img_out_n ;
# ifndef STBI_NO_STDIO
FILE * img_file ;
# endif
2011-08-04 14:09:17 +00:00
guint8 * img_buffer , * img_buffer_end ;
2010-03-03 17:02:51 +00:00
} stbi ;
2010-01-05 18:02:29 +00:00
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
static void start_file ( stbi * s , FILE * f )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
s - > img_file = f ;
2010-01-05 18:02:29 +00:00
}
# endif
2011-08-04 14:09:17 +00:00
static void start_mem ( stbi * s , guint8 const * buffer , int len )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
s - > img_file = NULL ;
2010-01-05 18:02:29 +00:00
# endif
2011-08-04 14:09:17 +00:00
s - > img_buffer = ( guint8 * ) buffer ;
s - > img_buffer_end = ( guint8 * ) buffer + len ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
__forceinline static int get8 ( stbi * s )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
if ( s - > img_file ) {
int c = fgetc ( s - > img_file ) ;
2010-01-05 18:02:29 +00:00
return c = = EOF ? 0 : c ;
}
# endif
2010-03-03 17:02:51 +00:00
if ( s - > img_buffer < s - > img_buffer_end )
return * s - > img_buffer + + ;
2010-01-05 18:02:29 +00:00
return 0 ;
}
2010-03-03 17:02:51 +00:00
__forceinline static int at_eof ( stbi * s )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
if ( s - > img_file )
return feof ( s - > img_file ) ;
2010-01-05 18:02:29 +00:00
# endif
2010-03-03 17:02:51 +00:00
return s - > img_buffer > = s - > img_buffer_end ;
2010-01-05 18:02:29 +00:00
}
2011-08-04 14:09:17 +00:00
__forceinline static guint8 get8u ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
return ( guint8 ) get8 ( s ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static void skip ( stbi * s , int n )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
if ( s - > img_file )
fseek ( s - > img_file , n , SEEK_CUR ) ;
2010-01-05 18:02:29 +00:00
else
# endif
2010-03-03 17:02:51 +00:00
s - > img_buffer + = n ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static int get16 ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
int z = get8 ( s ) ;
return ( z < < 8 ) + get8 ( s ) ;
2010-01-05 18:02:29 +00:00
}
2011-08-04 14:09:17 +00:00
static guint32 get32 ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint32 z = get16 ( s ) ;
2010-03-03 17:02:51 +00:00
return ( z < < 16 ) + get16 ( s ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static int get16le ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
int z = get8 ( s ) ;
return z + ( get8 ( s ) < < 8 ) ;
2010-01-05 18:02:29 +00:00
}
2011-08-04 14:09:17 +00:00
static guint32 get32le ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint32 z = get16le ( s ) ;
2010-03-03 17:02:51 +00:00
return z + ( get16le ( s ) < < 16 ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static void getn ( stbi * s , stbi_uc * buffer , int n )
2010-01-05 18:02:29 +00:00
{
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
if ( s - > img_file ) {
fread ( buffer , 1 , n , s - > img_file ) ;
2010-01-05 18:02:29 +00:00
return ;
}
# endif
2010-03-03 17:02:51 +00:00
memcpy ( buffer , s - > img_buffer , n ) ;
s - > img_buffer + = n ;
2010-01-05 18:02:29 +00:00
}
//////////////////////////////////////////////////////////////////////////////
//
// generic converter from built-in img_n to req_comp
// individual types do this automatically as much as possible (e.g. jpeg
// does all cases internally since it needs to colorspace convert anyway,
// and it never has alpha, so very few cases ). png can automatically
// interleave an alpha=255 channel, but falls back to this for other cases
//
// assume data buffer is malloced, so malloc a new one and free that one
// only failure mode is malloc failing
2011-08-04 14:09:17 +00:00
static guint8 compute_y ( int r , int g , int b )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
return ( guint8 ) ( ( ( r * 77 ) + ( g * 150 ) + ( 29 * b ) ) > > 8 ) ;
2010-01-05 18:02:29 +00:00
}
2011-08-04 14:09:17 +00:00
static unsigned char * convert_format ( unsigned char * data , int img_n , int req_comp , guint x , guint y )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
int i , j ;
2010-01-05 18:02:29 +00:00
unsigned char * good ;
if ( req_comp = = img_n ) return data ;
assert ( req_comp > = 1 & & req_comp < = 4 ) ;
2010-03-03 17:02:51 +00:00
good = ( unsigned char * ) malloc ( req_comp * x * y ) ;
2010-01-05 18:02:29 +00:00
if ( good = = NULL ) {
free ( data ) ;
return epuc ( " outofmem " , " Out of memory " ) ;
}
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < ( int ) y ; + + j ) {
unsigned char * src = data + j * x * img_n ;
unsigned char * dest = good + j * x * req_comp ;
2010-01-05 18:02:29 +00:00
# define COMBO(a,b) ((a)*8+(b))
2010-03-03 17:02:51 +00:00
# define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
2010-01-05 18:02:29 +00:00
// convert source image with img_n components to one with req_comp components;
// avoid switch per pixel, so use switch per scanline and massive macros
switch ( COMBO ( img_n , req_comp ) ) {
CASE ( 1 , 2 ) dest [ 0 ] = src [ 0 ] , dest [ 1 ] = 255 ; break ;
CASE ( 1 , 3 ) dest [ 0 ] = dest [ 1 ] = dest [ 2 ] = src [ 0 ] ; break ;
CASE ( 1 , 4 ) dest [ 0 ] = dest [ 1 ] = dest [ 2 ] = src [ 0 ] , dest [ 3 ] = 255 ; break ;
CASE ( 2 , 1 ) dest [ 0 ] = src [ 0 ] ; break ;
CASE ( 2 , 3 ) dest [ 0 ] = dest [ 1 ] = dest [ 2 ] = src [ 0 ] ; break ;
CASE ( 2 , 4 ) dest [ 0 ] = dest [ 1 ] = dest [ 2 ] = src [ 0 ] , dest [ 3 ] = src [ 1 ] ; break ;
CASE ( 3 , 4 ) dest [ 0 ] = src [ 0 ] , dest [ 1 ] = src [ 1 ] , dest [ 2 ] = src [ 2 ] , dest [ 3 ] = 255 ; break ;
CASE ( 3 , 1 ) dest [ 0 ] = compute_y ( src [ 0 ] , src [ 1 ] , src [ 2 ] ) ; break ;
CASE ( 3 , 2 ) dest [ 0 ] = compute_y ( src [ 0 ] , src [ 1 ] , src [ 2 ] ) , dest [ 1 ] = 255 ; break ;
CASE ( 4 , 1 ) dest [ 0 ] = compute_y ( src [ 0 ] , src [ 1 ] , src [ 2 ] ) ; break ;
CASE ( 4 , 2 ) dest [ 0 ] = compute_y ( src [ 0 ] , src [ 1 ] , src [ 2 ] ) , dest [ 1 ] = src [ 3 ] ; break ;
CASE ( 4 , 3 ) dest [ 0 ] = src [ 0 ] , dest [ 1 ] = src [ 1 ] , dest [ 2 ] = src [ 2 ] ; break ;
default : assert ( 0 ) ;
}
# undef CASE
}
free ( data ) ;
return good ;
}
# ifndef STBI_NO_HDR
static float * ldr_to_hdr ( stbi_uc * data , int x , int y , int comp )
{
int i , k , n ;
float * output = ( float * ) malloc ( x * y * comp * sizeof ( float ) ) ;
if ( output = = NULL ) { free ( data ) ; return epf ( " outofmem " , " Out of memory " ) ; }
// compute number of non-alpha components
if ( comp & 1 ) n = comp ; else n = comp - 1 ;
for ( i = 0 ; i < x * y ; + + i ) {
for ( k = 0 ; k < n ; + + k ) {
output [ i * comp + k ] = ( float ) pow ( data [ i * comp + k ] / 255.0f , l2h_gamma ) * l2h_scale ;
}
if ( k < comp ) output [ i * comp + k ] = data [ i * comp + k ] / 255.0f ;
}
free ( data ) ;
return output ;
}
# define float2int(x) ((int) (x))
static stbi_uc * hdr_to_ldr ( float * data , int x , int y , int comp )
{
int i , k , n ;
stbi_uc * output = ( stbi_uc * ) malloc ( x * y * comp ) ;
if ( output = = NULL ) { free ( data ) ; return epuc ( " outofmem " , " Out of memory " ) ; }
// compute number of non-alpha components
if ( comp & 1 ) n = comp ; else n = comp - 1 ;
for ( i = 0 ; i < x * y ; + + i ) {
for ( k = 0 ; k < n ; + + k ) {
float z = ( float ) pow ( data [ i * comp + k ] * h2l_scale_i , h2l_gamma_i ) * 255 + 0.5f ;
if ( z < 0 ) z = 0 ;
if ( z > 255 ) z = 255 ;
output [ i * comp + k ] = float2int ( z ) ;
}
if ( k < comp ) {
float z = data [ i * comp + k ] * 255 + 0.5f ;
if ( z < 0 ) z = 0 ;
if ( z > 255 ) z = 255 ;
output [ i * comp + k ] = float2int ( z ) ;
}
}
free ( data ) ;
return output ;
}
# endif
//////////////////////////////////////////////////////////////////////////////
//
// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation)
//
// simple implementation
// - channel subsampling of at most 2 in each dimension
// - doesn't support delayed output of y-dimension
// - simple interface (only one output format: 8-bit interleaved RGB)
// - doesn't try to recover corrupt jpegs
// - doesn't allow partial loading, loading multiple at once
// - still fast on x86 (copying globals into locals doesn't help x86)
// - allocates lots of intermediate memory (full size of all components)
// - non-interleaved case requires this anyway
// - allows good upsampling (see next)
// high-quality
// - upsampled channels are bilinearly interpolated, even across blocks
// - quality integer IDCT derived from IJG's 'slow'
// performance
// - fast huffman; reasonable integer IDCT
// - uses a lot of intermediate memory, could cache poorly
// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4
// stb_jpeg: 1.34 seconds (MSVC6, default release build)
// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro)
// IJL11.dll: 1.08 seconds (compiled by intel)
// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG)
// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro)
// huffman decoding acceleration
# define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
typedef struct
{
2011-08-04 14:09:17 +00:00
guint8 fast [ 1 < < FAST_BITS ] ;
2010-01-05 18:02:29 +00:00
// weirdly, repacking this into AoS is a 10% speed loss, instead of a win
2011-08-04 14:09:17 +00:00
guint16 code [ 256 ] ;
guint8 values [ 256 ] ;
guint8 size [ 257 ] ;
2010-01-05 18:02:29 +00:00
unsigned int maxcode [ 18 ] ;
int delta [ 17 ] ; // old 'firstsymbol' - old 'firstcode'
} huffman ;
2010-03-03 17:02:51 +00:00
typedef struct
{
# if STBI_SIMD
unsigned short dequant2 [ 4 ] [ 64 ] ;
# endif
stbi s ;
huffman huff_dc [ 4 ] ;
huffman huff_ac [ 4 ] ;
2011-08-04 14:09:17 +00:00
guint8 dequant [ 4 ] [ 64 ] ;
2010-03-03 17:02:51 +00:00
// sizes for components, interleaved MCUs
int img_h_max , img_v_max ;
int img_mcu_x , img_mcu_y ;
int img_mcu_w , img_mcu_h ;
// definition of jpeg image component
struct
{
int id ;
int h , v ;
int tq ;
int hd , ha ;
int dc_pred ;
int x , y , w2 , h2 ;
2011-08-04 14:09:17 +00:00
guint8 * data ;
2010-03-03 17:02:51 +00:00
void * raw_data ;
2011-08-04 14:09:17 +00:00
guint8 * linebuf ;
2010-03-03 17:02:51 +00:00
} img_comp [ 4 ] ;
2011-08-04 14:09:17 +00:00
guint32 code_buffer ; // jpeg entropy-coded buffer
2010-03-03 17:02:51 +00:00
int code_bits ; // number of valid bits
unsigned char marker ; // marker seen while filling entropy buffer
int nomore ; // flag if we saw a marker so must stop
int scan_n , order [ 4 ] ;
int restart_interval , todo ;
} jpeg ;
2010-01-05 18:02:29 +00:00
static int build_huffman ( huffman * h , int * count )
{
int i , j , k = 0 , code ;
// build size list for each symbol (from JPEG spec)
for ( i = 0 ; i < 16 ; + + i )
for ( j = 0 ; j < count [ i ] ; + + j )
2011-08-04 14:09:17 +00:00
h - > size [ k + + ] = ( guint8 ) ( i + 1 ) ;
2010-01-05 18:02:29 +00:00
h - > size [ k ] = 0 ;
// compute actual symbols (from jpeg spec)
code = 0 ;
k = 0 ;
for ( j = 1 ; j < = 16 ; + + j ) {
// compute delta to add to code to compute symbol id
h - > delta [ j ] = k - code ;
if ( h - > size [ k ] = = j ) {
while ( h - > size [ k ] = = j )
2011-08-04 14:09:17 +00:00
h - > code [ k + + ] = ( guint16 ) ( code + + ) ;
2010-01-05 18:02:29 +00:00
if ( code - 1 > = ( 1 < < j ) ) return e ( " bad code lengths " , " Corrupt JPEG " ) ;
}
// compute largest code + 1 for this size, preshifted as needed later
h - > maxcode [ j ] = code < < ( 16 - j ) ;
code < < = 1 ;
}
h - > maxcode [ j ] = 0xffffffff ;
// build non-spec acceleration table; 255 is flag for not-accelerated
memset ( h - > fast , 255 , 1 < < FAST_BITS ) ;
for ( i = 0 ; i < k ; + + i ) {
int s = h - > size [ i ] ;
if ( s < = FAST_BITS ) {
int c = h - > code [ i ] < < ( FAST_BITS - s ) ;
int m = 1 < < ( FAST_BITS - s ) ;
for ( j = 0 ; j < m ; + + j ) {
2011-08-04 14:09:17 +00:00
h - > fast [ c + j ] = ( guint8 ) i ;
2010-01-05 18:02:29 +00:00
}
}
}
return 1 ;
}
2010-03-03 17:02:51 +00:00
static void grow_buffer_unsafe ( jpeg * j )
2010-01-05 18:02:29 +00:00
{
do {
2010-03-03 17:02:51 +00:00
int b = j - > nomore ? 0 : get8 ( & j - > s ) ;
2010-01-05 18:02:29 +00:00
if ( b = = 0xff ) {
2010-03-03 17:02:51 +00:00
int c = get8 ( & j - > s ) ;
2010-01-05 18:02:29 +00:00
if ( c ! = 0 ) {
2010-03-03 17:02:51 +00:00
j - > marker = ( unsigned char ) c ;
j - > nomore = 1 ;
2010-01-05 18:02:29 +00:00
return ;
}
}
2010-03-03 17:02:51 +00:00
j - > code_buffer = ( j - > code_buffer < < 8 ) | b ;
j - > code_bits + = 8 ;
} while ( j - > code_bits < = 24 ) ;
2010-01-05 18:02:29 +00:00
}
// (1 << n) - 1
2011-08-04 14:09:17 +00:00
static guint32 bmask [ 17 ] = { 0 , 1 , 3 , 7 , 15 , 31 , 63 , 127 , 255 , 511 , 1023 , 2047 , 4095 , 8191 , 16383 , 32767 , 65535 } ;
2010-01-05 18:02:29 +00:00
// decode a jpeg huffman value from the bitstream
2010-03-03 17:02:51 +00:00
__forceinline static int decode ( jpeg * j , huffman * h )
2010-01-05 18:02:29 +00:00
{
unsigned int temp ;
int c , k ;
2010-03-03 17:02:51 +00:00
if ( j - > code_bits < 16 ) grow_buffer_unsafe ( j ) ;
2010-01-05 18:02:29 +00:00
// look at the top FAST_BITS and determine what symbol ID it is,
// if the code is <= FAST_BITS
2010-03-03 17:02:51 +00:00
c = ( j - > code_buffer > > ( j - > code_bits - FAST_BITS ) ) & ( ( 1 < < FAST_BITS ) - 1 ) ;
2010-01-05 18:02:29 +00:00
k = h - > fast [ c ] ;
if ( k < 255 ) {
2010-03-03 17:02:51 +00:00
if ( h - > size [ k ] > j - > code_bits )
2010-01-05 18:02:29 +00:00
return - 1 ;
2010-03-03 17:02:51 +00:00
j - > code_bits - = h - > size [ k ] ;
2010-01-05 18:02:29 +00:00
return h - > values [ k ] ;
}
// naive test is to shift the code_buffer down so k bits are
// valid, then test against maxcode. To speed this up, we've
// preshifted maxcode left so that it has (16-k) 0s at the
// end; in other words, regardless of the number of bits, it
// wants to be compared against something shifted to have 16;
// that way we don't need to shift inside the loop.
2010-03-03 17:02:51 +00:00
if ( j - > code_bits < 16 )
temp = ( j - > code_buffer < < ( 16 - j - > code_bits ) ) & 0xffff ;
2010-01-05 18:02:29 +00:00
else
2010-03-03 17:02:51 +00:00
temp = ( j - > code_buffer > > ( j - > code_bits - 16 ) ) & 0xffff ;
2010-01-05 18:02:29 +00:00
for ( k = FAST_BITS + 1 ; ; + + k )
if ( temp < h - > maxcode [ k ] )
break ;
if ( k = = 17 ) {
// error! code not found
2010-03-03 17:02:51 +00:00
j - > code_bits - = 16 ;
2010-01-05 18:02:29 +00:00
return - 1 ;
}
2010-03-03 17:02:51 +00:00
if ( k > j - > code_bits )
2010-01-05 18:02:29 +00:00
return - 1 ;
// convert the huffman code to the symbol id
2010-03-03 17:02:51 +00:00
c = ( ( j - > code_buffer > > ( j - > code_bits - k ) ) & bmask [ k ] ) + h - > delta [ k ] ;
assert ( ( ( ( j - > code_buffer ) > > ( j - > code_bits - h - > size [ c ] ) ) & bmask [ h - > size [ c ] ] ) = = h - > code [ c ] ) ;
2010-01-05 18:02:29 +00:00
// convert the id to a symbol
2010-03-03 17:02:51 +00:00
j - > code_bits - = k ;
2010-01-05 18:02:29 +00:00
return h - > values [ c ] ;
}
// combined JPEG 'receive' and JPEG 'extend', since baseline
// always extends everything it receives.
2010-03-03 17:02:51 +00:00
__forceinline static int extend_receive ( jpeg * j , int n )
2010-01-05 18:02:29 +00:00
{
unsigned int m = 1 < < ( n - 1 ) ;
unsigned int k ;
2010-03-03 17:02:51 +00:00
if ( j - > code_bits < n ) grow_buffer_unsafe ( j ) ;
k = ( j - > code_buffer > > ( j - > code_bits - n ) ) & bmask [ n ] ;
j - > code_bits - = n ;
2010-01-05 18:02:29 +00:00
// the following test is probably a random branch that won't
// predict well. I tried to table accelerate it but failed.
// maybe it's compiling as a conditional move?
if ( k < m )
return ( - 1 < < n ) + k + 1 ;
else
return k ;
}
// given a value that's at position X in the zigzag stream,
// where does it appear in the 8x8 matrix coded as row-major?
2011-08-04 14:09:17 +00:00
static guint8 dezigzag [ 64 + 15 ] =
2010-01-05 18:02:29 +00:00
{
0 , 1 , 8 , 16 , 9 , 2 , 3 , 10 ,
17 , 24 , 32 , 25 , 18 , 11 , 4 , 5 ,
12 , 19 , 26 , 33 , 40 , 48 , 41 , 34 ,
27 , 20 , 13 , 6 , 7 , 14 , 21 , 28 ,
35 , 42 , 49 , 56 , 57 , 50 , 43 , 36 ,
29 , 22 , 15 , 23 , 30 , 37 , 44 , 51 ,
58 , 59 , 52 , 45 , 38 , 31 , 39 , 46 ,
53 , 60 , 61 , 54 , 47 , 55 , 62 , 63 ,
// let corrupt input sample past end
63 , 63 , 63 , 63 , 63 , 63 , 63 , 63 ,
63 , 63 , 63 , 63 , 63 , 63 , 63
} ;
// decode one 64-entry block--
2010-03-03 17:02:51 +00:00
static int decode_block ( jpeg * j , short data [ 64 ] , huffman * hdc , huffman * hac , int b )
2010-01-05 18:02:29 +00:00
{
int diff , dc , k ;
2010-03-03 17:02:51 +00:00
int t = decode ( j , hdc ) ;
2010-01-05 18:02:29 +00:00
if ( t < 0 ) return e ( " bad huffman code " , " Corrupt JPEG " ) ;
// 0 all the ac values now so we can do it 32-bits at a time
memset ( data , 0 , 64 * sizeof ( data [ 0 ] ) ) ;
2010-03-03 17:02:51 +00:00
diff = t ? extend_receive ( j , t ) : 0 ;
dc = j - > img_comp [ b ] . dc_pred + diff ;
j - > img_comp [ b ] . dc_pred = dc ;
2010-01-05 18:02:29 +00:00
data [ 0 ] = ( short ) dc ;
// decode AC components, see JPEG spec
k = 1 ;
do {
int r , s ;
2010-03-03 17:02:51 +00:00
int rs = decode ( j , hac ) ;
2010-01-05 18:02:29 +00:00
if ( rs < 0 ) return e ( " bad huffman code " , " Corrupt JPEG " ) ;
s = rs & 15 ;
r = rs > > 4 ;
if ( s = = 0 ) {
if ( rs ! = 0xf0 ) break ; // end block
k + = 16 ;
} else {
k + = r ;
// decode into unzigzag'd location
2010-03-03 17:02:51 +00:00
data [ dezigzag [ k + + ] ] = ( short ) extend_receive ( j , s ) ;
2010-01-05 18:02:29 +00:00
}
} while ( k < 64 ) ;
return 1 ;
}
// take a -128..127 value and clamp it and convert to 0..255
2011-08-04 14:09:17 +00:00
__forceinline static guint8 clamp ( int x )
2010-01-05 18:02:29 +00:00
{
x + = 128 ;
// trick to use a single test to catch both cases
if ( ( unsigned int ) x > 255 ) {
if ( x < 0 ) return 0 ;
if ( x > 255 ) return 255 ;
}
2011-08-04 14:09:17 +00:00
return ( guint8 ) x ;
2010-01-05 18:02:29 +00:00
}
# define f2f(x) (int) (((x) * 4096 + 0.5))
# define fsh(x) ((x) << 12)
// derived from jidctint -- DCT_ISLOW
# define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
int t0 , t1 , t2 , t3 , p1 , p2 , p3 , p4 , p5 , x0 , x1 , x2 , x3 ; \
p2 = s2 ; \
p3 = s6 ; \
p1 = ( p2 + p3 ) * f2f ( 0.5411961f ) ; \
t2 = p1 + p3 * f2f ( - 1.847759065f ) ; \
t3 = p1 + p2 * f2f ( 0.765366865f ) ; \
p2 = s0 ; \
p3 = s4 ; \
t0 = fsh ( p2 + p3 ) ; \
t1 = fsh ( p2 - p3 ) ; \
x0 = t0 + t3 ; \
x3 = t0 - t3 ; \
x1 = t1 + t2 ; \
x2 = t1 - t2 ; \
t0 = s7 ; \
t1 = s5 ; \
t2 = s3 ; \
t3 = s1 ; \
p3 = t0 + t2 ; \
p4 = t1 + t3 ; \
p1 = t0 + t3 ; \
p2 = t1 + t2 ; \
p5 = ( p3 + p4 ) * f2f ( 1.175875602f ) ; \
t0 = t0 * f2f ( 0.298631336f ) ; \
t1 = t1 * f2f ( 2.053119869f ) ; \
t2 = t2 * f2f ( 3.072711026f ) ; \
t3 = t3 * f2f ( 1.501321110f ) ; \
p1 = p5 + p1 * f2f ( - 0.899976223f ) ; \
p2 = p5 + p2 * f2f ( - 2.562915447f ) ; \
p3 = p3 * f2f ( - 1.961570560f ) ; \
p4 = p4 * f2f ( - 0.390180644f ) ; \
t3 + = p1 + p4 ; \
t2 + = p2 + p3 ; \
t1 + = p2 + p4 ; \
t0 + = p1 + p3 ;
# if !STBI_SIMD
// .344 seconds on 3*anemones.jpg
2011-08-04 14:09:17 +00:00
static void idct_block ( guint8 * out , int out_stride , short data [ 64 ] , guint8 * dequantize )
2010-01-05 18:02:29 +00:00
{
int i , val [ 64 ] , * v = val ;
2011-08-04 14:09:17 +00:00
guint8 * o , * dq = dequantize ;
2010-01-05 18:02:29 +00:00
short * d = data ;
// columns
for ( i = 0 ; i < 8 ; + + i , + + d , + + dq , + + v ) {
// if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
if ( d [ 8 ] = = 0 & & d [ 16 ] = = 0 & & d [ 24 ] = = 0 & & d [ 32 ] = = 0
& & d [ 40 ] = = 0 & & d [ 48 ] = = 0 & & d [ 56 ] = = 0 ) {
// no shortcut 0 seconds
// (1|2|3|4|5|6|7)==0 0 seconds
// all separate -0.047 seconds
// 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
int dcterm = d [ 0 ] * dq [ 0 ] < < 2 ;
v [ 0 ] = v [ 8 ] = v [ 16 ] = v [ 24 ] = v [ 32 ] = v [ 40 ] = v [ 48 ] = v [ 56 ] = dcterm ;
} else {
IDCT_1D ( d [ 0 ] * dq [ 0 ] , d [ 8 ] * dq [ 8 ] , d [ 16 ] * dq [ 16 ] , d [ 24 ] * dq [ 24 ] ,
d [ 32 ] * dq [ 32 ] , d [ 40 ] * dq [ 40 ] , d [ 48 ] * dq [ 48 ] , d [ 56 ] * dq [ 56 ] )
// constants scaled things up by 1<<12; let's bring them back
// down, but keep 2 extra bits of precision
x0 + = 512 ; x1 + = 512 ; x2 + = 512 ; x3 + = 512 ;
v [ 0 ] = ( x0 + t3 ) > > 10 ;
v [ 56 ] = ( x0 - t3 ) > > 10 ;
v [ 8 ] = ( x1 + t2 ) > > 10 ;
v [ 48 ] = ( x1 - t2 ) > > 10 ;
v [ 16 ] = ( x2 + t1 ) > > 10 ;
v [ 40 ] = ( x2 - t1 ) > > 10 ;
v [ 24 ] = ( x3 + t0 ) > > 10 ;
v [ 32 ] = ( x3 - t0 ) > > 10 ;
}
}
for ( i = 0 , v = val , o = out ; i < 8 ; + + i , v + = 8 , o + = out_stride ) {
// no fast case since the first 1D IDCT spread components out
IDCT_1D ( v [ 0 ] , v [ 1 ] , v [ 2 ] , v [ 3 ] , v [ 4 ] , v [ 5 ] , v [ 6 ] , v [ 7 ] )
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
x0 + = 65536 ; x1 + = 65536 ; x2 + = 65536 ; x3 + = 65536 ;
o [ 0 ] = clamp ( ( x0 + t3 ) > > 17 ) ;
o [ 7 ] = clamp ( ( x0 - t3 ) > > 17 ) ;
o [ 1 ] = clamp ( ( x1 + t2 ) > > 17 ) ;
o [ 6 ] = clamp ( ( x1 - t2 ) > > 17 ) ;
o [ 2 ] = clamp ( ( x2 + t1 ) > > 17 ) ;
o [ 5 ] = clamp ( ( x2 - t1 ) > > 17 ) ;
o [ 3 ] = clamp ( ( x3 + t0 ) > > 17 ) ;
o [ 4 ] = clamp ( ( x3 - t0 ) > > 17 ) ;
}
}
# else
2011-08-04 14:09:17 +00:00
static void idct_block ( guint8 * out , int out_stride , short data [ 64 ] , unsigned short * dequantize )
2010-01-05 18:02:29 +00:00
{
int i , val [ 64 ] , * v = val ;
2011-08-04 14:09:17 +00:00
guint8 * o ;
2010-01-05 18:02:29 +00:00
unsigned short * dq = dequantize ;
short * d = data ;
// columns
for ( i = 0 ; i < 8 ; + + i , + + d , + + dq , + + v ) {
// if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
if ( d [ 8 ] = = 0 & & d [ 16 ] = = 0 & & d [ 24 ] = = 0 & & d [ 32 ] = = 0
& & d [ 40 ] = = 0 & & d [ 48 ] = = 0 & & d [ 56 ] = = 0 ) {
// no shortcut 0 seconds
// (1|2|3|4|5|6|7)==0 0 seconds
// all separate -0.047 seconds
// 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
int dcterm = d [ 0 ] * dq [ 0 ] < < 2 ;
v [ 0 ] = v [ 8 ] = v [ 16 ] = v [ 24 ] = v [ 32 ] = v [ 40 ] = v [ 48 ] = v [ 56 ] = dcterm ;
} else {
IDCT_1D ( d [ 0 ] * dq [ 0 ] , d [ 8 ] * dq [ 8 ] , d [ 16 ] * dq [ 16 ] , d [ 24 ] * dq [ 24 ] ,
d [ 32 ] * dq [ 32 ] , d [ 40 ] * dq [ 40 ] , d [ 48 ] * dq [ 48 ] , d [ 56 ] * dq [ 56 ] )
// constants scaled things up by 1<<12; let's bring them back
// down, but keep 2 extra bits of precision
x0 + = 512 ; x1 + = 512 ; x2 + = 512 ; x3 + = 512 ;
v [ 0 ] = ( x0 + t3 ) > > 10 ;
v [ 56 ] = ( x0 - t3 ) > > 10 ;
v [ 8 ] = ( x1 + t2 ) > > 10 ;
v [ 48 ] = ( x1 - t2 ) > > 10 ;
v [ 16 ] = ( x2 + t1 ) > > 10 ;
v [ 40 ] = ( x2 - t1 ) > > 10 ;
v [ 24 ] = ( x3 + t0 ) > > 10 ;
v [ 32 ] = ( x3 - t0 ) > > 10 ;
}
}
for ( i = 0 , v = val , o = out ; i < 8 ; + + i , v + = 8 , o + = out_stride ) {
// no fast case since the first 1D IDCT spread components out
IDCT_1D ( v [ 0 ] , v [ 1 ] , v [ 2 ] , v [ 3 ] , v [ 4 ] , v [ 5 ] , v [ 6 ] , v [ 7 ] )
// constants scaled things up by 1<<12, plus we had 1<<2 from first
// loop, plus horizontal and vertical each scale by sqrt(8) so together
// we've got an extra 1<<3, so 1<<17 total we need to remove.
x0 + = 65536 ; x1 + = 65536 ; x2 + = 65536 ; x3 + = 65536 ;
o [ 0 ] = clamp ( ( x0 + t3 ) > > 17 ) ;
o [ 7 ] = clamp ( ( x0 - t3 ) > > 17 ) ;
o [ 1 ] = clamp ( ( x1 + t2 ) > > 17 ) ;
o [ 6 ] = clamp ( ( x1 - t2 ) > > 17 ) ;
o [ 2 ] = clamp ( ( x2 + t1 ) > > 17 ) ;
o [ 5 ] = clamp ( ( x2 - t1 ) > > 17 ) ;
o [ 3 ] = clamp ( ( x3 + t0 ) > > 17 ) ;
o [ 4 ] = clamp ( ( x3 - t0 ) > > 17 ) ;
}
}
static stbi_idct_8x8 stbi_idct_installed = idct_block ;
extern void stbi_install_idct ( stbi_idct_8x8 func )
{
stbi_idct_installed = func ;
}
# endif
# define MARKER_none 0xff
// if there's a pending marker from the entropy stream, return that
// otherwise, fetch from the stream and get a marker. if there's no
// marker, return 0xff, which is never a valid marker value
2011-08-04 14:09:17 +00:00
static guint8 get_marker ( jpeg * j )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint8 x ;
2010-03-03 17:02:51 +00:00
if ( j - > marker ! = MARKER_none ) { x = j - > marker ; j - > marker = MARKER_none ; return x ; }
x = get8u ( & j - > s ) ;
2010-01-05 18:02:29 +00:00
if ( x ! = 0xff ) return MARKER_none ;
while ( x = = 0xff )
2010-03-03 17:02:51 +00:00
x = get8u ( & j - > s ) ;
2010-01-05 18:02:29 +00:00
return x ;
}
// in each scan, we'll have scan_n components, and the order
// of the components is specified by order[]
# define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
// after a restart interval, reset the entropy decoder and
// the dc prediction
2010-03-03 17:02:51 +00:00
static void reset ( jpeg * j )
{
j - > code_bits = 0 ;
j - > code_buffer = 0 ;
j - > nomore = 0 ;
j - > img_comp [ 0 ] . dc_pred = j - > img_comp [ 1 ] . dc_pred = j - > img_comp [ 2 ] . dc_pred = 0 ;
j - > marker = MARKER_none ;
j - > todo = j - > restart_interval ? j - > restart_interval : 0x7fffffff ;
2010-01-05 18:02:29 +00:00
// no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
// since we don't even allow 1<<30 pixels
}
2010-03-03 17:02:51 +00:00
static int parse_entropy_coded_data ( jpeg * z )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
reset ( z ) ;
if ( z - > scan_n = = 1 ) {
2010-01-05 18:02:29 +00:00
int i , j ;
# if STBI_SIMD
__declspec ( align ( 16 ) )
# endif
short data [ 64 ] ;
2010-03-03 17:02:51 +00:00
int n = z - > order [ 0 ] ;
2010-01-05 18:02:29 +00:00
// non-interleaved data, we just need to process one block at a time,
// in trivial scanline order
// number of blocks to do just depends on how many actual "pixels" this
// component has, independent of interleaved MCU blocking and such
2010-03-03 17:02:51 +00:00
int w = ( z - > img_comp [ n ] . x + 7 ) > > 3 ;
int h = ( z - > img_comp [ n ] . y + 7 ) > > 3 ;
2010-01-05 18:02:29 +00:00
for ( j = 0 ; j < h ; + + j ) {
for ( i = 0 ; i < w ; + + i ) {
2010-03-03 17:02:51 +00:00
if ( ! decode_block ( z , data , z - > huff_dc + z - > img_comp [ n ] . hd , z - > huff_ac + z - > img_comp [ n ] . ha , n ) ) return 0 ;
2010-01-05 18:02:29 +00:00
# if STBI_SIMD
2010-03-03 17:02:51 +00:00
stbi_idct_installed ( z - > img_comp [ n ] . data + z - > img_comp [ n ] . w2 * j * 8 + i * 8 , z - > img_comp [ n ] . w2 , data , z - > dequant2 [ z - > img_comp [ n ] . tq ] ) ;
2010-01-05 18:02:29 +00:00
# else
2010-03-03 17:02:51 +00:00
idct_block ( z - > img_comp [ n ] . data + z - > img_comp [ n ] . w2 * j * 8 + i * 8 , z - > img_comp [ n ] . w2 , data , z - > dequant [ z - > img_comp [ n ] . tq ] ) ;
2010-01-05 18:02:29 +00:00
# endif
// every data block is an MCU, so countdown the restart interval
2010-03-03 17:02:51 +00:00
if ( - - z - > todo < = 0 ) {
if ( z - > code_bits < 24 ) grow_buffer_unsafe ( z ) ;
2010-01-05 18:02:29 +00:00
// if it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
2010-03-03 17:02:51 +00:00
if ( ! RESTART ( z - > marker ) ) return 1 ;
reset ( z ) ;
2010-01-05 18:02:29 +00:00
}
}
}
} else { // interleaved!
int i , j , k , x , y ;
short data [ 64 ] ;
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < z - > img_mcu_y ; + + j ) {
for ( i = 0 ; i < z - > img_mcu_x ; + + i ) {
2010-01-05 18:02:29 +00:00
// scan an interleaved mcu... process scan_n components in order
2010-03-03 17:02:51 +00:00
for ( k = 0 ; k < z - > scan_n ; + + k ) {
int n = z - > order [ k ] ;
2010-01-05 18:02:29 +00:00
// scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
2010-03-03 17:02:51 +00:00
for ( y = 0 ; y < z - > img_comp [ n ] . v ; + + y ) {
for ( x = 0 ; x < z - > img_comp [ n ] . h ; + + x ) {
int x2 = ( i * z - > img_comp [ n ] . h + x ) * 8 ;
int y2 = ( j * z - > img_comp [ n ] . v + y ) * 8 ;
if ( ! decode_block ( z , data , z - > huff_dc + z - > img_comp [ n ] . hd , z - > huff_ac + z - > img_comp [ n ] . ha , n ) ) return 0 ;
2010-01-05 18:02:29 +00:00
# if STBI_SIMD
2010-03-03 17:02:51 +00:00
stbi_idct_installed ( z - > img_comp [ n ] . data + z - > img_comp [ n ] . w2 * y2 + x2 , z - > img_comp [ n ] . w2 , data , z - > dequant2 [ z - > img_comp [ n ] . tq ] ) ;
2010-01-05 18:02:29 +00:00
# else
2010-03-03 17:02:51 +00:00
idct_block ( z - > img_comp [ n ] . data + z - > img_comp [ n ] . w2 * y2 + x2 , z - > img_comp [ n ] . w2 , data , z - > dequant [ z - > img_comp [ n ] . tq ] ) ;
2010-01-05 18:02:29 +00:00
# endif
}
}
}
// after all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
2010-03-03 17:02:51 +00:00
if ( - - z - > todo < = 0 ) {
if ( z - > code_bits < 24 ) grow_buffer_unsafe ( z ) ;
2010-01-05 18:02:29 +00:00
// if it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
2010-03-03 17:02:51 +00:00
if ( ! RESTART ( z - > marker ) ) return 1 ;
reset ( z ) ;
2010-01-05 18:02:29 +00:00
}
}
}
}
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int process_marker ( jpeg * z , int m )
2010-01-05 18:02:29 +00:00
{
int L ;
switch ( m ) {
case MARKER_none : // no marker found
return e ( " expected marker " , " Corrupt JPEG " ) ;
case 0xC2 : // SOF - progressive
return e ( " progressive jpeg " , " JPEG format not supported (progressive) " ) ;
case 0xDD : // DRI - specify restart interval
2010-03-03 17:02:51 +00:00
if ( get16 ( & z - > s ) ! = 4 ) return e ( " bad DRI len " , " Corrupt JPEG " ) ;
z - > restart_interval = get16 ( & z - > s ) ;
2010-01-05 18:02:29 +00:00
return 1 ;
case 0xDB : // DQT - define quantization table
2010-03-03 17:02:51 +00:00
L = get16 ( & z - > s ) - 2 ;
2010-01-05 18:02:29 +00:00
while ( L > 0 ) {
2010-03-03 17:02:51 +00:00
int q = get8 ( & z - > s ) ;
int p = q > > 4 ;
int t = q & 15 , i ;
2010-01-05 18:02:29 +00:00
if ( p ! = 0 ) return e ( " bad DQT type " , " Corrupt JPEG " ) ;
if ( t > 3 ) return e ( " bad DQT table " , " Corrupt JPEG " ) ;
for ( i = 0 ; i < 64 ; + + i )
2010-03-03 17:02:51 +00:00
z - > dequant [ t ] [ dezigzag [ i ] ] = get8u ( & z - > s ) ;
2010-01-05 18:02:29 +00:00
# if STBI_SIMD
for ( i = 0 ; i < 64 ; + + i )
2010-03-03 17:02:51 +00:00
z - > dequant2 [ t ] [ i ] = z - > dequant [ t ] [ i ] ;
2010-01-05 18:02:29 +00:00
# endif
L - = 65 ;
}
return L = = 0 ;
case 0xC4 : // DHT - define huffman table
2010-03-03 17:02:51 +00:00
L = get16 ( & z - > s ) - 2 ;
2010-01-05 18:02:29 +00:00
while ( L > 0 ) {
2011-08-04 14:09:17 +00:00
guint8 * v ;
2010-01-05 18:02:29 +00:00
int sizes [ 16 ] , i , m = 0 ;
2010-03-03 17:02:51 +00:00
int q = get8 ( & z - > s ) ;
int tc = q > > 4 ;
int th = q & 15 ;
2010-01-05 18:02:29 +00:00
if ( tc > 1 | | th > 3 ) return e ( " bad DHT header " , " Corrupt JPEG " ) ;
for ( i = 0 ; i < 16 ; + + i ) {
2010-03-03 17:02:51 +00:00
sizes [ i ] = get8 ( & z - > s ) ;
2010-01-05 18:02:29 +00:00
m + = sizes [ i ] ;
}
L - = 17 ;
if ( tc = = 0 ) {
2010-03-03 17:02:51 +00:00
if ( ! build_huffman ( z - > huff_dc + th , sizes ) ) return 0 ;
v = z - > huff_dc [ th ] . values ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
if ( ! build_huffman ( z - > huff_ac + th , sizes ) ) return 0 ;
v = z - > huff_ac [ th ] . values ;
2010-01-05 18:02:29 +00:00
}
for ( i = 0 ; i < m ; + + i )
2010-03-03 17:02:51 +00:00
v [ i ] = get8u ( & z - > s ) ;
2010-01-05 18:02:29 +00:00
L - = m ;
}
return L = = 0 ;
}
// check for comment block or APP blocks
if ( ( m > = 0xE0 & & m < = 0xEF ) | | m = = 0xFE ) {
2010-03-03 17:02:51 +00:00
skip ( & z - > s , get16 ( & z - > s ) - 2 ) ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
return 0 ;
}
// after we see SOS
2010-03-03 17:02:51 +00:00
static int process_scan_header ( jpeg * z )
2010-01-05 18:02:29 +00:00
{
int i ;
2010-03-03 17:02:51 +00:00
int Ls = get16 ( & z - > s ) ;
z - > scan_n = get8 ( & z - > s ) ;
if ( z - > scan_n < 1 | | z - > scan_n > 4 | | z - > scan_n > ( int ) z - > s . img_n ) return e ( " bad SOS component count " , " Corrupt JPEG " ) ;
if ( Ls ! = 6 + 2 * z - > scan_n ) return e ( " bad SOS len " , " Corrupt JPEG " ) ;
for ( i = 0 ; i < z - > scan_n ; + + i ) {
int id = get8 ( & z - > s ) , which ;
int q = get8 ( & z - > s ) ;
for ( which = 0 ; which < z - > s . img_n ; + + which )
if ( z - > img_comp [ which ] . id = = id )
2010-01-05 18:02:29 +00:00
break ;
2010-03-03 17:02:51 +00:00
if ( which = = z - > s . img_n ) return 0 ;
z - > img_comp [ which ] . hd = q > > 4 ; if ( z - > img_comp [ which ] . hd > 3 ) return e ( " bad DC huff " , " Corrupt JPEG " ) ;
z - > img_comp [ which ] . ha = q & 15 ; if ( z - > img_comp [ which ] . ha > 3 ) return e ( " bad AC huff " , " Corrupt JPEG " ) ;
z - > order [ i ] = which ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
if ( get8 ( & z - > s ) ! = 0 ) return e ( " bad SOS " , " Corrupt JPEG " ) ;
get8 ( & z - > s ) ; // should be 63, but might be 0
if ( get8 ( & z - > s ) ! = 0 ) return e ( " bad SOS " , " Corrupt JPEG " ) ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int process_frame_header ( jpeg * z , int scan )
{
stbi * s = & z - > s ;
int Lf , p , i , q , h_max = 1 , v_max = 1 , c ;
Lf = get16 ( s ) ; if ( Lf < 11 ) return e ( " bad SOF len " , " Corrupt JPEG " ) ; // JPEG
p = get8 ( s ) ; if ( p ! = 8 ) return e ( " only 8-bit " , " JPEG format not supported: 8-bit only " ) ; // JPEG baseline
s - > img_y = get16 ( s ) ; if ( s - > img_y = = 0 ) return e ( " no header height " , " JPEG format not supported: delayed height " ) ; // Legal, but we don't handle it--but neither does IJG
s - > img_x = get16 ( s ) ; if ( s - > img_x = = 0 ) return e ( " 0 width " , " Corrupt JPEG " ) ; // JPEG requires
c = get8 ( s ) ;
if ( c ! = 3 & & c ! = 1 ) return e ( " bad component count " , " Corrupt JPEG " ) ; // JFIF requires
s - > img_n = c ;
for ( i = 0 ; i < c ; + + i ) {
z - > img_comp [ i ] . data = NULL ;
z - > img_comp [ i ] . linebuf = NULL ;
}
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
if ( Lf ! = 8 + 3 * s - > img_n ) return e ( " bad SOF len " , " Corrupt JPEG " ) ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < s - > img_n ; + + i ) {
z - > img_comp [ i ] . id = get8 ( s ) ;
if ( z - > img_comp [ i ] . id ! = i + 1 ) // JFIF requires
if ( z - > img_comp [ i ] . id ! = i ) // some version of jpegtran outputs non-JFIF-compliant files!
2010-01-05 18:02:29 +00:00
return e ( " bad component ID " , " Corrupt JPEG " ) ;
2010-03-03 17:02:51 +00:00
q = get8 ( s ) ;
z - > img_comp [ i ] . h = ( q > > 4 ) ; if ( ! z - > img_comp [ i ] . h | | z - > img_comp [ i ] . h > 4 ) return e ( " bad H " , " Corrupt JPEG " ) ;
z - > img_comp [ i ] . v = q & 15 ; if ( ! z - > img_comp [ i ] . v | | z - > img_comp [ i ] . v > 4 ) return e ( " bad V " , " Corrupt JPEG " ) ;
z - > img_comp [ i ] . tq = get8 ( s ) ; if ( z - > img_comp [ i ] . tq > 3 ) return e ( " bad TQ " , " Corrupt JPEG " ) ;
2010-01-05 18:02:29 +00:00
}
if ( scan ! = SCAN_load ) return 1 ;
2010-03-03 17:02:51 +00:00
if ( ( 1 < < 30 ) / s - > img_x / s - > img_n < s - > img_y ) return e ( " too large " , " Image too large to decode " ) ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < s - > img_n ; + + i ) {
if ( z - > img_comp [ i ] . h > h_max ) h_max = z - > img_comp [ i ] . h ;
if ( z - > img_comp [ i ] . v > v_max ) v_max = z - > img_comp [ i ] . v ;
2010-01-05 18:02:29 +00:00
}
// compute interleaved mcu info
2010-03-03 17:02:51 +00:00
z - > img_h_max = h_max ;
z - > img_v_max = v_max ;
z - > img_mcu_w = h_max * 8 ;
z - > img_mcu_h = v_max * 8 ;
z - > img_mcu_x = ( s - > img_x + z - > img_mcu_w - 1 ) / z - > img_mcu_w ;
z - > img_mcu_y = ( s - > img_y + z - > img_mcu_h - 1 ) / z - > img_mcu_h ;
for ( i = 0 ; i < s - > img_n ; + + i ) {
2010-01-05 18:02:29 +00:00
// number of effective pixels (e.g. for non-interleaved MCU)
2010-03-03 17:02:51 +00:00
z - > img_comp [ i ] . x = ( s - > img_x * z - > img_comp [ i ] . h + h_max - 1 ) / h_max ;
z - > img_comp [ i ] . y = ( s - > img_y * z - > img_comp [ i ] . v + v_max - 1 ) / v_max ;
2010-01-05 18:02:29 +00:00
// to simplify generation, we'll allocate enough memory to decode
// the bogus oversized data from using interleaved MCUs and their
// big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
// discard the extra data until colorspace conversion
2010-03-03 17:02:51 +00:00
z - > img_comp [ i ] . w2 = z - > img_mcu_x * z - > img_comp [ i ] . h * 8 ;
z - > img_comp [ i ] . h2 = z - > img_mcu_y * z - > img_comp [ i ] . v * 8 ;
z - > img_comp [ i ] . raw_data = malloc ( z - > img_comp [ i ] . w2 * z - > img_comp [ i ] . h2 + 15 ) ;
if ( z - > img_comp [ i ] . raw_data = = NULL ) {
2010-01-05 18:02:29 +00:00
for ( - - i ; i > = 0 ; - - i ) {
2010-03-03 17:02:51 +00:00
free ( z - > img_comp [ i ] . raw_data ) ;
z - > img_comp [ i ] . data = NULL ;
2010-01-05 18:02:29 +00:00
}
return e ( " outofmem " , " Out of memory " ) ;
}
2010-03-03 17:02:51 +00:00
// align blocks for installable-idct using mmx/sse
2011-08-04 14:09:17 +00:00
z - > img_comp [ i ] . data = ( guint8 * ) ( ( ( size_t ) z - > img_comp [ i ] . raw_data + 15 ) & ~ 15 ) ;
2010-03-03 17:02:51 +00:00
z - > img_comp [ i ] . linebuf = NULL ;
2010-01-05 18:02:29 +00:00
}
return 1 ;
}
// use comparisons since in some cases we handle more than one case (e.g. SOF)
# define DNL(x) ((x) == 0xdc)
# define SOI(x) ((x) == 0xd8)
# define EOI(x) ((x) == 0xd9)
# define SOF(x) ((x) == 0xc0 || (x) == 0xc1)
# define SOS(x) ((x) == 0xda)
2010-03-03 17:02:51 +00:00
static int decode_jpeg_header ( jpeg * z , int scan )
2010-01-05 18:02:29 +00:00
{
int m ;
2010-03-03 17:02:51 +00:00
z - > marker = MARKER_none ; // initialize cached marker to empty
m = get_marker ( z ) ;
2010-01-05 18:02:29 +00:00
if ( ! SOI ( m ) ) return e ( " no SOI " , " Corrupt JPEG " ) ;
if ( scan = = SCAN_type ) return 1 ;
2010-03-03 17:02:51 +00:00
m = get_marker ( z ) ;
2010-01-05 18:02:29 +00:00
while ( ! SOF ( m ) ) {
2010-03-03 17:02:51 +00:00
if ( ! process_marker ( z , m ) ) return 0 ;
m = get_marker ( z ) ;
2010-01-05 18:02:29 +00:00
while ( m = = MARKER_none ) {
// some files have extra padding after their blocks, so ok, we'll scan
2010-03-03 17:02:51 +00:00
if ( at_eof ( & z - > s ) ) return e ( " no SOF " , " Corrupt JPEG " ) ;
m = get_marker ( z ) ;
2010-01-05 18:02:29 +00:00
}
}
2010-03-03 17:02:51 +00:00
if ( ! process_frame_header ( z , scan ) ) return 0 ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int decode_jpeg_image ( jpeg * j )
2010-01-05 18:02:29 +00:00
{
int m ;
2010-03-03 17:02:51 +00:00
j - > restart_interval = 0 ;
if ( ! decode_jpeg_header ( j , SCAN_load ) ) return 0 ;
m = get_marker ( j ) ;
2010-01-05 18:02:29 +00:00
while ( ! EOI ( m ) ) {
if ( SOS ( m ) ) {
2010-03-03 17:02:51 +00:00
if ( ! process_scan_header ( j ) ) return 0 ;
if ( ! parse_entropy_coded_data ( j ) ) return 0 ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
if ( ! process_marker ( j , m ) ) return 0 ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
m = get_marker ( j ) ;
2010-01-05 18:02:29 +00:00
}
return 1 ;
}
// static jfif-centered resampling (across block boundaries)
2011-08-04 14:09:17 +00:00
typedef guint8 * ( * resample_row_func ) ( guint8 * out , guint8 * in0 , guint8 * in1 ,
2010-01-05 18:02:29 +00:00
int w , int hs ) ;
2011-08-04 14:09:17 +00:00
# define div4(x) ((guint8) ((x) >> 2))
2010-01-05 18:02:29 +00:00
2011-08-04 14:09:17 +00:00
static guint8 * resample_row_1 ( guint8 * out , guint8 * in_near , guint8 * in_far , int w , int hs )
2010-01-05 18:02:29 +00:00
{
return in_near ;
}
2011-08-04 14:09:17 +00:00
static guint8 * resample_row_v_2 ( guint8 * out , guint8 * in_near , guint8 * in_far , int w , int hs )
2010-01-05 18:02:29 +00:00
{
// need to generate two samples vertically for every one in input
int i ;
for ( i = 0 ; i < w ; + + i )
out [ i ] = div4 ( 3 * in_near [ i ] + in_far [ i ] + 2 ) ;
return out ;
}
2011-08-04 14:09:17 +00:00
static guint8 * resample_row_h_2 ( guint8 * out , guint8 * in_near , guint8 * in_far , int w , int hs )
2010-01-05 18:02:29 +00:00
{
// need to generate two samples horizontally for every one in input
int i ;
2011-08-04 14:09:17 +00:00
guint8 * input = in_near ;
2010-01-05 18:02:29 +00:00
if ( w = = 1 ) {
// if only one sample, can't do any interpolation
out [ 0 ] = out [ 1 ] = input [ 0 ] ;
return out ;
}
out [ 0 ] = input [ 0 ] ;
out [ 1 ] = div4 ( input [ 0 ] * 3 + input [ 1 ] + 2 ) ;
for ( i = 1 ; i < w - 1 ; + + i ) {
int n = 3 * input [ i ] + 2 ;
out [ i * 2 + 0 ] = div4 ( n + input [ i - 1 ] ) ;
out [ i * 2 + 1 ] = div4 ( n + input [ i + 1 ] ) ;
}
out [ i * 2 + 0 ] = div4 ( input [ w - 2 ] * 3 + input [ w - 1 ] + 2 ) ;
out [ i * 2 + 1 ] = input [ w - 1 ] ;
return out ;
}
2011-08-04 14:09:17 +00:00
# define div16(x) ((guint8) ((x) >> 4))
2010-01-05 18:02:29 +00:00
2011-08-04 14:09:17 +00:00
static guint8 * resample_row_hv_2 ( guint8 * out , guint8 * in_near , guint8 * in_far , int w , int hs )
2010-01-05 18:02:29 +00:00
{
// need to generate 2x2 samples for every one in input
int i , t0 , t1 ;
if ( w = = 1 ) {
out [ 0 ] = out [ 1 ] = div4 ( 3 * in_near [ 0 ] + in_far [ 0 ] + 2 ) ;
return out ;
}
t1 = 3 * in_near [ 0 ] + in_far [ 0 ] ;
out [ 0 ] = div4 ( t1 + 2 ) ;
for ( i = 1 ; i < w ; + + i ) {
t0 = t1 ;
t1 = 3 * in_near [ i ] + in_far [ i ] ;
out [ i * 2 - 1 ] = div16 ( 3 * t0 + t1 + 8 ) ;
out [ i * 2 ] = div16 ( 3 * t1 + t0 + 8 ) ;
}
out [ w * 2 - 1 ] = div4 ( t1 + 2 ) ;
return out ;
}
2011-08-04 14:09:17 +00:00
static guint8 * resample_row_generic ( guint8 * out , guint8 * in_near , guint8 * in_far , int w , int hs )
2010-01-05 18:02:29 +00:00
{
// resample with nearest-neighbor
int i , j ;
for ( i = 0 ; i < w ; + + i )
for ( j = 0 ; j < hs ; + + j )
out [ i * hs + j ] = in_near [ i ] ;
return out ;
}
# define float2fixed(x) ((int) ((x) * 65536 + 0.5))
// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
// VC6 without processor=Pro is generating multiple LEAs per multiply!
2011-08-04 14:09:17 +00:00
static void YCbCr_to_RGB_row ( guint8 * out , const guint8 * y , const guint8 * pcb , const guint8 * pcr , int count , int step )
2010-01-05 18:02:29 +00:00
{
int i ;
for ( i = 0 ; i < count ; + + i ) {
int y_fixed = ( y [ i ] < < 16 ) + 32768 ; // rounding
int r , g , b ;
int cr = pcr [ i ] - 128 ;
int cb = pcb [ i ] - 128 ;
r = y_fixed + cr * float2fixed ( 1.40200f ) ;
g = y_fixed - cr * float2fixed ( 0.71414f ) - cb * float2fixed ( 0.34414f ) ;
b = y_fixed + cb * float2fixed ( 1.77200f ) ;
r > > = 16 ;
g > > = 16 ;
b > > = 16 ;
if ( ( unsigned ) r > 255 ) { if ( r < 0 ) r = 0 ; else r = 255 ; }
if ( ( unsigned ) g > 255 ) { if ( g < 0 ) g = 0 ; else g = 255 ; }
if ( ( unsigned ) b > 255 ) { if ( b < 0 ) b = 0 ; else b = 255 ; }
2011-08-04 14:09:17 +00:00
out [ 0 ] = ( guint8 ) r ;
out [ 1 ] = ( guint8 ) g ;
out [ 2 ] = ( guint8 ) b ;
2010-01-05 18:02:29 +00:00
out [ 3 ] = 255 ;
out + = step ;
}
}
# if STBI_SIMD
static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row ;
void stbi_install_YCbCr_to_RGB ( stbi_YCbCr_to_RGB_run func )
{
stbi_YCbCr_installed = func ;
}
# endif
// clean up the temporary component buffers
2010-03-03 17:02:51 +00:00
static void cleanup_jpeg ( jpeg * j )
2010-01-05 18:02:29 +00:00
{
int i ;
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < j - > s . img_n ; + + i ) {
if ( j - > img_comp [ i ] . data ) {
free ( j - > img_comp [ i ] . raw_data ) ;
j - > img_comp [ i ] . data = NULL ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
if ( j - > img_comp [ i ] . linebuf ) {
free ( j - > img_comp [ i ] . linebuf ) ;
j - > img_comp [ i ] . linebuf = NULL ;
2010-01-05 18:02:29 +00:00
}
}
}
typedef struct
{
resample_row_func resample ;
2011-08-04 14:09:17 +00:00
guint8 * line0 , * line1 ;
2010-01-05 18:02:29 +00:00
int hs , vs ; // expansion factor in each axis
int w_lores ; // horizontal pixels pre-expansion
int ystep ; // how far through vertical expansion we are
int ypos ; // which pre-expansion row we're on
} stbi_resample ;
2011-08-04 14:09:17 +00:00
static guint8 * load_jpeg_image ( jpeg * z , int * out_x , int * out_y , int * comp , int req_comp )
2010-01-05 18:02:29 +00:00
{
int n , decode_n ;
// validate req_comp
if ( req_comp < 0 | | req_comp > 4 ) return epuc ( " bad req_comp " , " Internal error " ) ;
2010-03-03 17:02:51 +00:00
z - > s . img_n = 0 ;
2010-01-05 18:02:29 +00:00
// load a jpeg image from whichever source
2010-03-03 17:02:51 +00:00
if ( ! decode_jpeg_image ( z ) ) { cleanup_jpeg ( z ) ; return NULL ; }
2010-01-05 18:02:29 +00:00
// determine actual number of components to generate
2010-03-03 17:02:51 +00:00
n = req_comp ? req_comp : z - > s . img_n ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
if ( z - > s . img_n = = 3 & & n < 3 )
2010-01-05 18:02:29 +00:00
decode_n = 1 ;
else
2010-03-03 17:02:51 +00:00
decode_n = z - > s . img_n ;
2010-01-05 18:02:29 +00:00
// resample and color-convert
{
int k ;
2011-08-04 14:09:17 +00:00
guint i , j ;
guint8 * output ;
guint8 * coutput [ 4 ] ;
2010-01-05 18:02:29 +00:00
stbi_resample res_comp [ 4 ] ;
for ( k = 0 ; k < decode_n ; + + k ) {
stbi_resample * r = & res_comp [ k ] ;
// allocate line buffer big enough for upsampling off the edges
// with upsample factor of 4
2011-08-04 14:09:17 +00:00
z - > img_comp [ k ] . linebuf = ( guint8 * ) malloc ( z - > s . img_x + 3 ) ;
2010-03-03 17:02:51 +00:00
if ( ! z - > img_comp [ k ] . linebuf ) { cleanup_jpeg ( z ) ; return epuc ( " outofmem " , " Out of memory " ) ; }
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
r - > hs = z - > img_h_max / z - > img_comp [ k ] . h ;
r - > vs = z - > img_v_max / z - > img_comp [ k ] . v ;
2010-01-05 18:02:29 +00:00
r - > ystep = r - > vs > > 1 ;
2010-03-03 17:02:51 +00:00
r - > w_lores = ( z - > s . img_x + r - > hs - 1 ) / r - > hs ;
2010-01-05 18:02:29 +00:00
r - > ypos = 0 ;
2010-03-03 17:02:51 +00:00
r - > line0 = r - > line1 = z - > img_comp [ k ] . data ;
2010-01-05 18:02:29 +00:00
if ( r - > hs = = 1 & & r - > vs = = 1 ) r - > resample = resample_row_1 ;
else if ( r - > hs = = 1 & & r - > vs = = 2 ) r - > resample = resample_row_v_2 ;
else if ( r - > hs = = 2 & & r - > vs = = 1 ) r - > resample = resample_row_h_2 ;
else if ( r - > hs = = 2 & & r - > vs = = 2 ) r - > resample = resample_row_hv_2 ;
else r - > resample = resample_row_generic ;
}
// can't error after this so, this is safe
2011-08-04 14:09:17 +00:00
output = ( guint8 * ) malloc ( n * z - > s . img_x * z - > s . img_y + 1 ) ;
2010-03-03 17:02:51 +00:00
if ( ! output ) { cleanup_jpeg ( z ) ; return epuc ( " outofmem " , " Out of memory " ) ; }
2010-01-05 18:02:29 +00:00
// now go ahead and resample
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < z - > s . img_y ; + + j ) {
2011-08-04 14:09:17 +00:00
guint8 * out = output + n * z - > s . img_x * j ;
2010-01-05 18:02:29 +00:00
for ( k = 0 ; k < decode_n ; + + k ) {
stbi_resample * r = & res_comp [ k ] ;
int y_bot = r - > ystep > = ( r - > vs > > 1 ) ;
2010-03-03 17:02:51 +00:00
coutput [ k ] = r - > resample ( z - > img_comp [ k ] . linebuf ,
2010-01-05 18:02:29 +00:00
y_bot ? r - > line1 : r - > line0 ,
y_bot ? r - > line0 : r - > line1 ,
r - > w_lores , r - > hs ) ;
if ( + + r - > ystep > = r - > vs ) {
r - > ystep = 0 ;
r - > line0 = r - > line1 ;
2010-03-03 17:02:51 +00:00
if ( + + r - > ypos < z - > img_comp [ k ] . y )
r - > line1 + = z - > img_comp [ k ] . w2 ;
2010-01-05 18:02:29 +00:00
}
}
if ( n > = 3 ) {
2011-08-04 14:09:17 +00:00
guint8 * y = coutput [ 0 ] ;
2010-03-03 17:02:51 +00:00
if ( z - > s . img_n = = 3 ) {
2010-01-05 18:02:29 +00:00
# if STBI_SIMD
2010-03-03 17:02:51 +00:00
stbi_YCbCr_installed ( out , y , coutput [ 1 ] , coutput [ 2 ] , z - > s . img_x , n ) ;
2010-01-05 18:02:29 +00:00
# else
2010-03-03 17:02:51 +00:00
YCbCr_to_RGB_row ( out , y , coutput [ 1 ] , coutput [ 2 ] , z - > s . img_x , n ) ;
2010-01-05 18:02:29 +00:00
# endif
} else
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < z - > s . img_x ; + + i ) {
2010-01-05 18:02:29 +00:00
out [ 0 ] = out [ 1 ] = out [ 2 ] = y [ i ] ;
out [ 3 ] = 255 ; // not used if n==3
out + = n ;
}
} else {
2011-08-04 14:09:17 +00:00
guint8 * y = coutput [ 0 ] ;
2010-01-05 18:02:29 +00:00
if ( n = = 1 )
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < z - > s . img_x ; + + i ) out [ i ] = y [ i ] ;
2010-01-05 18:02:29 +00:00
else
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < z - > s . img_x ; + + i ) * out + + = y [ i ] , * out + + = 255 ;
2010-01-05 18:02:29 +00:00
}
}
2010-03-03 17:02:51 +00:00
cleanup_jpeg ( z ) ;
* out_x = z - > s . img_x ;
* out_y = z - > s . img_y ;
if ( comp ) * comp = z - > s . img_n ; // report original components, not output
2010-01-05 18:02:29 +00:00
return output ;
}
}
# ifndef STBI_NO_STDIO
unsigned char * stbi_jpeg_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
jpeg j ;
start_file ( & j . s , f ) ;
return load_jpeg_image ( & j , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
unsigned char * stbi_jpeg_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
unsigned char * data ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return NULL ;
data = stbi_jpeg_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return data ;
}
# endif
unsigned char * stbi_jpeg_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
jpeg j ;
start_mem ( & j . s , buffer , len ) ;
return load_jpeg_image ( & j , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# ifndef STBI_NO_STDIO
int stbi_jpeg_test_file ( FILE * f )
{
int n , r ;
2010-03-03 17:02:51 +00:00
jpeg j ;
2010-01-05 18:02:29 +00:00
n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & j . s , f ) ;
r = decode_jpeg_header ( & j , SCAN_type ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
int stbi_jpeg_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
jpeg j ;
start_mem ( & j . s , buffer , len ) ;
return decode_jpeg_header ( & j , SCAN_type ) ;
2010-01-05 18:02:29 +00:00
}
// @TODO:
# ifndef STBI_NO_STDIO
extern int stbi_jpeg_info ( char const * filename , int * x , int * y , int * comp ) ;
extern int stbi_jpeg_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
extern int stbi_jpeg_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
// public domain zlib decode v0.2 Sean Barrett 2006-11-18
// simple implementation
// - all input must be provided in an upfront buffer
// - all output is written to a single output buffer (can malloc/realloc)
// performance
// - fast huffman
// fast-way is faster to check than jpeg huffman, but slow way is slower
# define ZFAST_BITS 9 // accelerate all cases in default tables
# define ZFAST_MASK ((1 << ZFAST_BITS) - 1)
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
typedef struct
{
2011-08-04 14:09:17 +00:00
guint16 fast [ 1 < < ZFAST_BITS ] ;
guint16 firstcode [ 16 ] ;
2010-01-05 18:02:29 +00:00
int maxcode [ 17 ] ;
2011-08-04 14:09:17 +00:00
guint16 firstsymbol [ 16 ] ;
guint8 size [ 288 ] ;
guint16 value [ 288 ] ;
2010-01-05 18:02:29 +00:00
} zhuffman ;
__forceinline static int bitreverse16 ( int n )
{
n = ( ( n & 0xAAAA ) > > 1 ) | ( ( n & 0x5555 ) < < 1 ) ;
n = ( ( n & 0xCCCC ) > > 2 ) | ( ( n & 0x3333 ) < < 2 ) ;
n = ( ( n & 0xF0F0 ) > > 4 ) | ( ( n & 0x0F0F ) < < 4 ) ;
n = ( ( n & 0xFF00 ) > > 8 ) | ( ( n & 0x00FF ) < < 8 ) ;
return n ;
}
__forceinline static int bit_reverse ( int v , int bits )
{
assert ( bits < = 16 ) ;
// to bit reverse n bits, reverse 16 and shift
// e.g. 11 bits, bit reverse and shift away 5
return bitreverse16 ( v ) > > ( 16 - bits ) ;
}
2011-08-04 14:09:17 +00:00
static int zbuild_huffman ( zhuffman * z , guint8 * sizelist , int num )
2010-01-05 18:02:29 +00:00
{
int i , k = 0 ;
int code , next_code [ 16 ] , sizes [ 17 ] ;
// DEFLATE spec for generating codes
memset ( sizes , 0 , sizeof ( sizes ) ) ;
memset ( z - > fast , 255 , sizeof ( z - > fast ) ) ;
for ( i = 0 ; i < num ; + + i )
+ + sizes [ sizelist [ i ] ] ;
sizes [ 0 ] = 0 ;
for ( i = 1 ; i < 16 ; + + i )
assert ( sizes [ i ] < = ( 1 < < i ) ) ;
code = 0 ;
for ( i = 1 ; i < 16 ; + + i ) {
next_code [ i ] = code ;
2011-08-04 14:09:17 +00:00
z - > firstcode [ i ] = ( guint16 ) code ;
z - > firstsymbol [ i ] = ( guint16 ) k ;
2010-01-05 18:02:29 +00:00
code = ( code + sizes [ i ] ) ;
if ( sizes [ i ] )
if ( code - 1 > = ( 1 < < i ) ) return e ( " bad codelengths " , " Corrupt JPEG " ) ;
z - > maxcode [ i ] = code < < ( 16 - i ) ; // preshift for inner loop
code < < = 1 ;
k + = sizes [ i ] ;
}
z - > maxcode [ 16 ] = 0x10000 ; // sentinel
for ( i = 0 ; i < num ; + + i ) {
int s = sizelist [ i ] ;
if ( s ) {
int c = next_code [ s ] - z - > firstcode [ s ] + z - > firstsymbol [ s ] ;
2011-08-04 14:09:17 +00:00
z - > size [ c ] = ( guint8 ) s ;
z - > value [ c ] = ( guint16 ) i ;
2010-01-05 18:02:29 +00:00
if ( s < = ZFAST_BITS ) {
int k = bit_reverse ( next_code [ s ] , s ) ;
while ( k < ( 1 < < ZFAST_BITS ) ) {
2011-08-04 14:09:17 +00:00
z - > fast [ k ] = ( guint16 ) c ;
2010-01-05 18:02:29 +00:00
k + = ( 1 < < s ) ;
}
}
+ + next_code [ s ] ;
}
}
return 1 ;
}
// zlib-from-memory implementation for PNG reading
// because PNG allows splitting the zlib stream arbitrarily,
// and it's annoying structurally to have PNG call ZLIB call PNG,
// we require PNG read all the IDATs and combine them into a single
// memory buffer
2010-03-03 17:02:51 +00:00
typedef struct
{
2011-08-04 14:09:17 +00:00
guint8 * zbuffer , * zbuffer_end ;
2010-03-03 17:02:51 +00:00
int num_bits ;
2011-08-04 14:09:17 +00:00
guint32 code_buffer ;
2010-03-03 17:02:51 +00:00
char * zout ;
char * zout_start ;
char * zout_end ;
int z_expandable ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
zhuffman z_length , z_distance ;
} zbuf ;
__forceinline static int zget8 ( zbuf * z )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
if ( z - > zbuffer > = z - > zbuffer_end ) return 0 ;
return * z - > zbuffer + + ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static void fill_bits ( zbuf * z )
2010-01-05 18:02:29 +00:00
{
do {
2010-03-03 17:02:51 +00:00
assert ( z - > code_buffer < ( 1U < < z - > num_bits ) ) ;
z - > code_buffer | = zget8 ( z ) < < z - > num_bits ;
z - > num_bits + = 8 ;
} while ( z - > num_bits < = 24 ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
__forceinline static unsigned int zreceive ( zbuf * z , int n )
2010-01-05 18:02:29 +00:00
{
unsigned int k ;
2010-03-03 17:02:51 +00:00
if ( z - > num_bits < n ) fill_bits ( z ) ;
k = z - > code_buffer & ( ( 1 < < n ) - 1 ) ;
z - > code_buffer > > = n ;
z - > num_bits - = n ;
2010-01-05 18:02:29 +00:00
return k ;
}
2010-03-03 17:02:51 +00:00
__forceinline static int zhuffman_decode ( zbuf * a , zhuffman * z )
2010-01-05 18:02:29 +00:00
{
int b , s , k ;
2010-03-03 17:02:51 +00:00
if ( a - > num_bits < 16 ) fill_bits ( a ) ;
b = z - > fast [ a - > code_buffer & ZFAST_MASK ] ;
2010-01-05 18:02:29 +00:00
if ( b < 0xffff ) {
s = z - > size [ b ] ;
2010-03-03 17:02:51 +00:00
a - > code_buffer > > = s ;
a - > num_bits - = s ;
2010-01-05 18:02:29 +00:00
return z - > value [ b ] ;
}
// not resolved by fast table, so compute it the slow way
// use jpeg approach, which requires MSbits at top
2010-03-03 17:02:51 +00:00
k = bit_reverse ( a - > code_buffer , 16 ) ;
2010-01-05 18:02:29 +00:00
for ( s = ZFAST_BITS + 1 ; ; + + s )
if ( k < z - > maxcode [ s ] )
break ;
if ( s = = 16 ) return - 1 ; // invalid code!
// code size is s, so:
b = ( k > > ( 16 - s ) ) - z - > firstcode [ s ] + z - > firstsymbol [ s ] ;
assert ( z - > size [ b ] = = s ) ;
2010-03-03 17:02:51 +00:00
a - > code_buffer > > = s ;
a - > num_bits - = s ;
2010-01-05 18:02:29 +00:00
return z - > value [ b ] ;
}
2010-03-03 17:02:51 +00:00
static int expand ( zbuf * z , int n ) // need to make room for n bytes
2010-01-05 18:02:29 +00:00
{
char * q ;
int cur , limit ;
2010-03-03 17:02:51 +00:00
if ( ! z - > z_expandable ) return e ( " output buffer limit " , " Corrupt PNG " ) ;
cur = ( int ) ( z - > zout - z - > zout_start ) ;
limit = ( int ) ( z - > zout_end - z - > zout_start ) ;
2010-01-05 18:02:29 +00:00
while ( cur + n > limit )
limit * = 2 ;
2010-03-03 17:02:51 +00:00
q = ( char * ) realloc ( z - > zout_start , limit ) ;
2010-01-05 18:02:29 +00:00
if ( q = = NULL ) return e ( " outofmem " , " Out of memory " ) ;
2010-03-03 17:02:51 +00:00
z - > zout_start = q ;
z - > zout = q + cur ;
z - > zout_end = q + limit ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
static int length_base [ 31 ] = {
3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 13 ,
15 , 17 , 19 , 23 , 27 , 31 , 35 , 43 , 51 , 59 ,
67 , 83 , 99 , 115 , 131 , 163 , 195 , 227 , 258 , 0 , 0 } ;
static int length_extra [ 31 ] =
{ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 3 , 3 , 3 , 3 , 4 , 4 , 4 , 4 , 5 , 5 , 5 , 5 , 0 , 0 , 0 } ;
static int dist_base [ 32 ] = { 1 , 2 , 3 , 4 , 5 , 7 , 9 , 13 , 17 , 25 , 33 , 49 , 65 , 97 , 129 , 193 ,
257 , 385 , 513 , 769 , 1025 , 1537 , 2049 , 3073 , 4097 , 6145 , 8193 , 12289 , 16385 , 24577 , 0 , 0 } ;
static int dist_extra [ 32 ] =
{ 0 , 0 , 0 , 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 , 5 , 5 , 6 , 6 , 7 , 7 , 8 , 8 , 9 , 9 , 10 , 10 , 11 , 11 , 12 , 12 , 13 , 13 } ;
2010-03-03 17:02:51 +00:00
static int parse_huffman_block ( zbuf * a )
2010-01-05 18:02:29 +00:00
{
for ( ; ; ) {
2010-03-03 17:02:51 +00:00
int z = zhuffman_decode ( a , & a - > z_length ) ;
2010-01-05 18:02:29 +00:00
if ( z < 256 ) {
if ( z < 0 ) return e ( " bad huffman code " , " Corrupt PNG " ) ; // error in huffman codes
2010-03-03 17:02:51 +00:00
if ( a - > zout > = a - > zout_end ) if ( ! expand ( a , 1 ) ) return 0 ;
* a - > zout + + = ( char ) z ;
2010-01-05 18:02:29 +00:00
} else {
2011-08-04 14:09:17 +00:00
guint8 * p ;
2010-01-05 18:02:29 +00:00
int len , dist ;
if ( z = = 256 ) return 1 ;
z - = 257 ;
len = length_base [ z ] ;
2010-03-03 17:02:51 +00:00
if ( length_extra [ z ] ) len + = zreceive ( a , length_extra [ z ] ) ;
z = zhuffman_decode ( a , & a - > z_distance ) ;
2010-01-05 18:02:29 +00:00
if ( z < 0 ) return e ( " bad huffman code " , " Corrupt PNG " ) ;
dist = dist_base [ z ] ;
2010-03-03 17:02:51 +00:00
if ( dist_extra [ z ] ) dist + = zreceive ( a , dist_extra [ z ] ) ;
if ( a - > zout - a - > zout_start < dist ) return e ( " bad dist " , " Corrupt PNG " ) ;
if ( a - > zout + len > a - > zout_end ) if ( ! expand ( a , len ) ) return 0 ;
2011-08-04 14:09:17 +00:00
p = ( guint8 * ) ( a - > zout - dist ) ;
2010-01-05 18:02:29 +00:00
while ( len - - )
2010-03-03 17:02:51 +00:00
* a - > zout + + = * p + + ;
2010-01-05 18:02:29 +00:00
}
}
}
2010-03-03 17:02:51 +00:00
static int compute_huffman_codes ( zbuf * a )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
static guint8 length_dezigzag [ 19 ] = { 16 , 17 , 18 , 0 , 8 , 7 , 9 , 6 , 10 , 5 , 11 , 4 , 12 , 3 , 13 , 2 , 14 , 1 , 15 } ;
2010-03-03 17:02:51 +00:00
zhuffman z_codelength ;
2011-08-04 14:09:17 +00:00
guint8 lencodes [ 286 + 32 + 137 ] ; //padding for maximum single op
guint8 codelength_sizes [ 19 ] ;
2010-01-05 18:02:29 +00:00
int i , n ;
2010-03-03 17:02:51 +00:00
int hlit = zreceive ( a , 5 ) + 257 ;
int hdist = zreceive ( a , 5 ) + 1 ;
int hclen = zreceive ( a , 4 ) + 4 ;
2010-01-05 18:02:29 +00:00
memset ( codelength_sizes , 0 , sizeof ( codelength_sizes ) ) ;
for ( i = 0 ; i < hclen ; + + i ) {
2010-03-03 17:02:51 +00:00
int s = zreceive ( a , 3 ) ;
2011-08-04 14:09:17 +00:00
codelength_sizes [ length_dezigzag [ i ] ] = ( guint8 ) s ;
2010-01-05 18:02:29 +00:00
}
if ( ! zbuild_huffman ( & z_codelength , codelength_sizes , 19 ) ) return 0 ;
n = 0 ;
while ( n < hlit + hdist ) {
2010-03-03 17:02:51 +00:00
int c = zhuffman_decode ( a , & z_codelength ) ;
2010-01-05 18:02:29 +00:00
assert ( c > = 0 & & c < 19 ) ;
if ( c < 16 )
2011-08-04 14:09:17 +00:00
lencodes [ n + + ] = ( guint8 ) c ;
2010-01-05 18:02:29 +00:00
else if ( c = = 16 ) {
2010-03-03 17:02:51 +00:00
c = zreceive ( a , 2 ) + 3 ;
2010-01-05 18:02:29 +00:00
memset ( lencodes + n , lencodes [ n - 1 ] , c ) ;
n + = c ;
} else if ( c = = 17 ) {
2010-03-03 17:02:51 +00:00
c = zreceive ( a , 3 ) + 3 ;
2010-01-05 18:02:29 +00:00
memset ( lencodes + n , 0 , c ) ;
n + = c ;
} else {
assert ( c = = 18 ) ;
2010-03-03 17:02:51 +00:00
c = zreceive ( a , 7 ) + 11 ;
2010-01-05 18:02:29 +00:00
memset ( lencodes + n , 0 , c ) ;
n + = c ;
}
}
if ( n ! = hlit + hdist ) return e ( " bad codelengths " , " Corrupt PNG " ) ;
2010-03-03 17:02:51 +00:00
if ( ! zbuild_huffman ( & a - > z_length , lencodes , hlit ) ) return 0 ;
if ( ! zbuild_huffman ( & a - > z_distance , lencodes + hlit , hdist ) ) return 0 ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int parse_uncompressed_block ( zbuf * a )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint8 header [ 4 ] ;
2010-01-05 18:02:29 +00:00
int len , nlen , k ;
2010-03-03 17:02:51 +00:00
if ( a - > num_bits & 7 )
zreceive ( a , a - > num_bits & 7 ) ; // discard
2010-01-05 18:02:29 +00:00
// drain the bit-packed data into header
k = 0 ;
2010-03-03 17:02:51 +00:00
while ( a - > num_bits > 0 ) {
2011-08-04 14:09:17 +00:00
header [ k + + ] = ( guint8 ) ( a - > code_buffer & 255 ) ; // wtf this warns?
2010-03-03 17:02:51 +00:00
a - > code_buffer > > = 8 ;
a - > num_bits - = 8 ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
assert ( a - > num_bits = = 0 ) ;
2010-01-05 18:02:29 +00:00
// now fill header the normal way
while ( k < 4 )
2011-08-04 14:09:17 +00:00
header [ k + + ] = ( guint8 ) zget8 ( a ) ;
2010-01-05 18:02:29 +00:00
len = header [ 1 ] * 256 + header [ 0 ] ;
nlen = header [ 3 ] * 256 + header [ 2 ] ;
if ( nlen ! = ( len ^ 0xffff ) ) return e ( " zlib corrupt " , " Corrupt PNG " ) ;
2010-03-03 17:02:51 +00:00
if ( a - > zbuffer + len > a - > zbuffer_end ) return e ( " read past buffer " , " Corrupt PNG " ) ;
if ( a - > zout + len > a - > zout_end )
if ( ! expand ( a , len ) ) return 0 ;
memcpy ( a - > zout , a - > zbuffer , len ) ;
a - > zbuffer + = len ;
a - > zout + = len ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int parse_zlib_header ( zbuf * a )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
int cmf = zget8 ( a ) ;
2010-01-05 18:02:29 +00:00
int cm = cmf & 15 ;
/* int cinfo = cmf >> 4; */
2010-03-03 17:02:51 +00:00
int flg = zget8 ( a ) ;
2010-01-05 18:02:29 +00:00
if ( ( cmf * 256 + flg ) % 31 ! = 0 ) return e ( " bad zlib header " , " Corrupt PNG " ) ; // zlib spec
if ( flg & 32 ) return e ( " no preset dict " , " Corrupt PNG " ) ; // preset dictionary not allowed in png
if ( cm ! = 8 ) return e ( " bad compression " , " Corrupt PNG " ) ; // DEFLATE required for png
// window = 1 << (8 + cinfo)... but who cares, we fully buffer output
return 1 ;
}
2010-03-03 17:02:51 +00:00
// @TODO: should statically initialize these for optimal thread safety
2011-08-04 14:09:17 +00:00
static guint8 default_length [ 288 ] , default_distance [ 32 ] ;
2010-01-05 18:02:29 +00:00
static void init_defaults ( void )
{
int i ; // use <= to match clearly with spec
for ( i = 0 ; i < = 143 ; + + i ) default_length [ i ] = 8 ;
for ( ; i < = 255 ; + + i ) default_length [ i ] = 9 ;
for ( ; i < = 279 ; + + i ) default_length [ i ] = 7 ;
for ( ; i < = 287 ; + + i ) default_length [ i ] = 8 ;
for ( i = 0 ; i < = 31 ; + + i ) default_distance [ i ] = 5 ;
}
2010-03-03 17:02:51 +00:00
int stbi_png_partial ; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead
static int parse_zlib ( zbuf * a , int parse_header )
2010-01-05 18:02:29 +00:00
{
int final , type ;
if ( parse_header )
2010-03-03 17:02:51 +00:00
if ( ! parse_zlib_header ( a ) ) return 0 ;
a - > num_bits = 0 ;
a - > code_buffer = 0 ;
2010-01-05 18:02:29 +00:00
do {
2010-03-03 17:02:51 +00:00
final = zreceive ( a , 1 ) ;
type = zreceive ( a , 2 ) ;
2010-01-05 18:02:29 +00:00
if ( type = = 0 ) {
2010-03-03 17:02:51 +00:00
if ( ! parse_uncompressed_block ( a ) ) return 0 ;
2010-01-05 18:02:29 +00:00
} else if ( type = = 3 ) {
return 0 ;
} else {
if ( type = = 1 ) {
// use fixed code lengths
2010-03-03 17:02:51 +00:00
if ( ! default_distance [ 31 ] ) init_defaults ( ) ;
if ( ! zbuild_huffman ( & a - > z_length , default_length , 288 ) ) return 0 ;
if ( ! zbuild_huffman ( & a - > z_distance , default_distance , 32 ) ) return 0 ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
if ( ! compute_huffman_codes ( a ) ) return 0 ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
if ( ! parse_huffman_block ( a ) ) return 0 ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
if ( stbi_png_partial & & a - > zout - a - > zout_start > 65536 )
break ;
2010-01-05 18:02:29 +00:00
} while ( ! final ) ;
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int do_zlib ( zbuf * a , char * obuf , int olen , int exp , int parse_header )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
a - > zout_start = obuf ;
a - > zout = obuf ;
a - > zout_end = obuf + olen ;
a - > z_expandable = exp ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
return parse_zlib ( a , parse_header ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
char * stbi_zlib_decode_malloc_guesssize ( const char * buffer , int len , int initial_size , int * outlen )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
zbuf a ;
2010-01-05 18:02:29 +00:00
char * p = ( char * ) malloc ( initial_size ) ;
if ( p = = NULL ) return NULL ;
2011-08-04 14:09:17 +00:00
a . zbuffer = ( guint8 * ) buffer ;
a . zbuffer_end = ( guint8 * ) buffer + len ;
2010-03-03 17:02:51 +00:00
if ( do_zlib ( & a , p , initial_size , 1 , 1 ) ) {
if ( outlen ) * outlen = ( int ) ( a . zout - a . zout_start ) ;
return a . zout_start ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
free ( a . zout_start ) ;
2010-01-05 18:02:29 +00:00
return NULL ;
}
}
char * stbi_zlib_decode_malloc ( char const * buffer , int len , int * outlen )
{
2010-03-03 17:02:51 +00:00
return stbi_zlib_decode_malloc_guesssize ( buffer , len , 16384 , outlen ) ;
2010-01-05 18:02:29 +00:00
}
int stbi_zlib_decode_buffer ( char * obuffer , int olen , char const * ibuffer , int ilen )
{
2010-03-03 17:02:51 +00:00
zbuf a ;
2011-08-04 14:09:17 +00:00
a . zbuffer = ( guint8 * ) ibuffer ;
a . zbuffer_end = ( guint8 * ) ibuffer + ilen ;
2010-03-03 17:02:51 +00:00
if ( do_zlib ( & a , obuffer , olen , 0 , 1 ) )
return ( int ) ( a . zout - a . zout_start ) ;
2010-01-05 18:02:29 +00:00
else
return - 1 ;
}
char * stbi_zlib_decode_noheader_malloc ( char const * buffer , int len , int * outlen )
{
2010-03-03 17:02:51 +00:00
zbuf a ;
2010-01-05 18:02:29 +00:00
char * p = ( char * ) malloc ( 16384 ) ;
if ( p = = NULL ) return NULL ;
2011-08-04 14:09:17 +00:00
a . zbuffer = ( guint8 * ) buffer ;
a . zbuffer_end = ( guint8 * ) buffer + len ;
2010-03-03 17:02:51 +00:00
if ( do_zlib ( & a , p , 16384 , 1 , 0 ) ) {
if ( outlen ) * outlen = ( int ) ( a . zout - a . zout_start ) ;
return a . zout_start ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
free ( a . zout_start ) ;
2010-01-05 18:02:29 +00:00
return NULL ;
}
}
int stbi_zlib_decode_noheader_buffer ( char * obuffer , int olen , const char * ibuffer , int ilen )
{
2010-03-03 17:02:51 +00:00
zbuf a ;
2011-08-04 14:09:17 +00:00
a . zbuffer = ( guint8 * ) ibuffer ;
a . zbuffer_end = ( guint8 * ) ibuffer + ilen ;
2010-03-03 17:02:51 +00:00
if ( do_zlib ( & a , obuffer , olen , 0 , 0 ) )
return ( int ) ( a . zout - a . zout_start ) ;
2010-01-05 18:02:29 +00:00
else
return - 1 ;
}
// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
// simple implementation
// - only 8-bit samples
// - no CRC checking
// - allocates lots of intermediate memory
// - avoids problem of streaming data between subsystems
// - avoids explicit window management
// performance
// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
typedef struct
{
2011-08-04 14:09:17 +00:00
guint32 length ;
guint32 type ;
2010-01-05 18:02:29 +00:00
} chunk ;
# define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
2010-03-03 17:02:51 +00:00
static chunk get_chunk_header ( stbi * s )
2010-01-05 18:02:29 +00:00
{
chunk c ;
2010-03-03 17:02:51 +00:00
c . length = get32 ( s ) ;
c . type = get32 ( s ) ;
2010-01-05 18:02:29 +00:00
return c ;
}
2010-03-03 17:02:51 +00:00
static int check_png_header ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
static guint8 png_sig [ 8 ] = { 137 , 80 , 78 , 71 , 13 , 10 , 26 , 10 } ;
2010-01-05 18:02:29 +00:00
int i ;
for ( i = 0 ; i < 8 ; + + i )
2010-03-03 17:02:51 +00:00
if ( get8 ( s ) ! = png_sig [ i ] ) return e ( " bad png sig " , " Not a PNG " ) ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
typedef struct
{
stbi s ;
2011-08-04 14:09:17 +00:00
guint8 * idata , * expanded , * out ;
2010-03-03 17:02:51 +00:00
} png ;
2010-01-05 18:02:29 +00:00
enum {
F_none = 0 , F_sub = 1 , F_up = 2 , F_avg = 3 , F_paeth = 4 ,
F_avg_first , F_paeth_first ,
} ;
2011-08-04 14:09:17 +00:00
static guint8 first_row_filter [ 5 ] =
2010-01-05 18:02:29 +00:00
{
F_none , F_sub , F_none , F_avg_first , F_paeth_first
} ;
static int paeth ( int a , int b , int c )
{
int p = a + b - c ;
int pa = abs ( p - a ) ;
int pb = abs ( p - b ) ;
int pc = abs ( p - c ) ;
if ( pa < = pb & & pa < = pc ) return a ;
if ( pb < = pc ) return b ;
return c ;
}
// create the png data from post-deflated data
2011-08-04 14:09:17 +00:00
static int create_png_image_raw ( png * a , guint8 * raw , guint32 raw_len , int out_n , guint32 x , guint32 y )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
stbi * s = & a - > s ;
2011-08-04 14:09:17 +00:00
guint32 i , j , stride = x * out_n ;
2010-01-05 18:02:29 +00:00
int k ;
2010-03-03 17:02:51 +00:00
int img_n = s - > img_n ; // copy it into a local for later
assert ( out_n = = s - > img_n | | out_n = = s - > img_n + 1 ) ;
if ( stbi_png_partial ) y = 1 ;
2011-08-04 14:09:17 +00:00
a - > out = ( guint8 * ) malloc ( x * y * out_n ) ;
2010-03-03 17:02:51 +00:00
if ( ! a - > out ) return e ( " outofmem " , " Out of memory " ) ;
if ( ! stbi_png_partial ) {
if ( s - > img_x = = x & & s - > img_y = = y )
if ( raw_len ! = ( img_n * x + 1 ) * y ) return e ( " not enough pixels " , " Corrupt PNG " ) ;
else // interlaced:
if ( raw_len < ( img_n * x + 1 ) * y ) return e ( " not enough pixels " , " Corrupt PNG " ) ;
}
for ( j = 0 ; j < y ; + + j ) {
2011-08-04 14:09:17 +00:00
guint8 * cur = a - > out + stride * j ;
guint8 * prior = cur - stride ;
2010-01-05 18:02:29 +00:00
int filter = * raw + + ;
if ( filter > 4 ) return e ( " invalid filter " , " Corrupt PNG " ) ;
// if first row, use special filter that doesn't sample previous row
if ( j = = 0 ) filter = first_row_filter [ filter ] ;
// handle first pixel explicitly
for ( k = 0 ; k < img_n ; + + k ) {
switch ( filter ) {
case F_none : cur [ k ] = raw [ k ] ; break ;
case F_sub : cur [ k ] = raw [ k ] ; break ;
case F_up : cur [ k ] = raw [ k ] + prior [ k ] ; break ;
case F_avg : cur [ k ] = raw [ k ] + ( prior [ k ] > > 1 ) ; break ;
2011-08-04 14:09:17 +00:00
case F_paeth : cur [ k ] = ( guint8 ) ( raw [ k ] + paeth ( 0 , prior [ k ] , 0 ) ) ; break ;
2010-01-05 18:02:29 +00:00
case F_avg_first : cur [ k ] = raw [ k ] ; break ;
case F_paeth_first : cur [ k ] = raw [ k ] ; break ;
}
}
if ( img_n ! = out_n ) cur [ img_n ] = 255 ;
raw + = img_n ;
cur + = out_n ;
prior + = out_n ;
// this is a little gross, so that we don't switch per-pixel or per-component
if ( img_n = = out_n ) {
# define CASE(f) \
case f : \
2010-03-03 17:02:51 +00:00
for ( i = x - 1 ; i > = 1 ; - - i , raw + = img_n , cur + = img_n , prior + = img_n ) \
2010-01-05 18:02:29 +00:00
for ( k = 0 ; k < img_n ; + + k )
switch ( filter ) {
CASE ( F_none ) cur [ k ] = raw [ k ] ; break ;
CASE ( F_sub ) cur [ k ] = raw [ k ] + cur [ k - img_n ] ; break ;
CASE ( F_up ) cur [ k ] = raw [ k ] + prior [ k ] ; break ;
CASE ( F_avg ) cur [ k ] = raw [ k ] + ( ( prior [ k ] + cur [ k - img_n ] ) > > 1 ) ; break ;
2011-08-04 14:09:17 +00:00
CASE ( F_paeth ) cur [ k ] = ( guint8 ) ( raw [ k ] + paeth ( cur [ k - img_n ] , prior [ k ] , prior [ k - img_n ] ) ) ; break ;
2010-01-05 18:02:29 +00:00
CASE ( F_avg_first ) cur [ k ] = raw [ k ] + ( cur [ k - img_n ] > > 1 ) ; break ;
2011-08-04 14:09:17 +00:00
CASE ( F_paeth_first ) cur [ k ] = ( guint8 ) ( raw [ k ] + paeth ( cur [ k - img_n ] , 0 , 0 ) ) ; break ;
2010-01-05 18:02:29 +00:00
}
# undef CASE
} else {
assert ( img_n + 1 = = out_n ) ;
# define CASE(f) \
case f : \
2010-03-03 17:02:51 +00:00
for ( i = x - 1 ; i > = 1 ; - - i , cur [ img_n ] = 255 , raw + = img_n , cur + = out_n , prior + = out_n ) \
2010-01-05 18:02:29 +00:00
for ( k = 0 ; k < img_n ; + + k )
switch ( filter ) {
CASE ( F_none ) cur [ k ] = raw [ k ] ; break ;
CASE ( F_sub ) cur [ k ] = raw [ k ] + cur [ k - out_n ] ; break ;
CASE ( F_up ) cur [ k ] = raw [ k ] + prior [ k ] ; break ;
CASE ( F_avg ) cur [ k ] = raw [ k ] + ( ( prior [ k ] + cur [ k - out_n ] ) > > 1 ) ; break ;
2011-08-04 14:09:17 +00:00
CASE ( F_paeth ) cur [ k ] = ( guint8 ) ( raw [ k ] + paeth ( cur [ k - out_n ] , prior [ k ] , prior [ k - out_n ] ) ) ; break ;
2010-01-05 18:02:29 +00:00
CASE ( F_avg_first ) cur [ k ] = raw [ k ] + ( cur [ k - out_n ] > > 1 ) ; break ;
2011-08-04 14:09:17 +00:00
CASE ( F_paeth_first ) cur [ k ] = ( guint8 ) ( raw [ k ] + paeth ( cur [ k - out_n ] , 0 , 0 ) ) ; break ;
2010-01-05 18:02:29 +00:00
}
# undef CASE
}
}
return 1 ;
}
2011-08-04 14:09:17 +00:00
static int create_png_image ( png * a , guint8 * raw , guint32 raw_len , int out_n , int interlaced )
2010-03-03 17:02:51 +00:00
{
2011-08-04 14:09:17 +00:00
guint8 * final ;
2010-03-03 17:02:51 +00:00
int p ;
int save ;
if ( ! interlaced )
return create_png_image_raw ( a , raw , raw_len , out_n , a - > s . img_x , a - > s . img_y ) ;
save = stbi_png_partial ;
stbi_png_partial = 0 ;
// de-interlacing
2011-08-04 14:09:17 +00:00
final = ( guint8 * ) malloc ( a - > s . img_x * a - > s . img_y * out_n ) ;
2010-03-03 17:02:51 +00:00
for ( p = 0 ; p < 7 ; + + p ) {
int xorig [ ] = { 0 , 4 , 0 , 2 , 0 , 1 , 0 } ;
int yorig [ ] = { 0 , 0 , 4 , 0 , 2 , 0 , 1 } ;
int xspc [ ] = { 8 , 8 , 4 , 4 , 2 , 2 , 1 } ;
int yspc [ ] = { 8 , 8 , 8 , 4 , 4 , 2 , 2 } ;
int i , j , x , y ;
// pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
x = ( a - > s . img_x - xorig [ p ] + xspc [ p ] - 1 ) / xspc [ p ] ;
y = ( a - > s . img_y - yorig [ p ] + yspc [ p ] - 1 ) / yspc [ p ] ;
if ( x & & y ) {
if ( ! create_png_image_raw ( a , raw , raw_len , out_n , x , y ) ) {
free ( final ) ;
return 0 ;
}
for ( j = 0 ; j < y ; + + j )
for ( i = 0 ; i < x ; + + i )
memcpy ( final + ( j * yspc [ p ] + yorig [ p ] ) * a - > s . img_x * out_n + ( i * xspc [ p ] + xorig [ p ] ) * out_n ,
a - > out + ( j * x + i ) * out_n , out_n ) ;
free ( a - > out ) ;
raw + = ( x * out_n + 1 ) * y ;
raw_len - = ( x * out_n + 1 ) * y ;
}
}
a - > out = final ;
stbi_png_partial = save ;
return 1 ;
}
2011-08-04 14:09:17 +00:00
static int compute_transparency ( png * z , guint8 tc [ 3 ] , int out_n )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
stbi * s = & z - > s ;
2011-08-04 14:09:17 +00:00
guint32 i , pixel_count = s - > img_x * s - > img_y ;
guint8 * p = z - > out ;
2010-01-05 18:02:29 +00:00
// compute color-based transparency, assuming we've
// already got 255 as the alpha value in the output
assert ( out_n = = 2 | | out_n = = 4 ) ;
if ( out_n = = 2 ) {
for ( i = 0 ; i < pixel_count ; + + i ) {
p [ 1 ] = ( p [ 0 ] = = tc [ 0 ] ? 0 : 255 ) ;
p + = 2 ;
}
} else {
for ( i = 0 ; i < pixel_count ; + + i ) {
if ( p [ 0 ] = = tc [ 0 ] & & p [ 1 ] = = tc [ 1 ] & & p [ 2 ] = = tc [ 2 ] )
p [ 3 ] = 0 ;
p + = 4 ;
}
}
return 1 ;
}
2011-08-04 14:09:17 +00:00
static int expand_palette ( png * a , guint8 * palette , int len , int pal_img_n )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint32 i , pixel_count = a - > s . img_x * a - > s . img_y ;
guint8 * p , * temp_out , * orig = a - > out ;
2010-01-05 18:02:29 +00:00
2011-08-04 14:09:17 +00:00
p = ( guint8 * ) malloc ( pixel_count * pal_img_n ) ;
2010-01-05 18:02:29 +00:00
if ( p = = NULL ) return e ( " outofmem " , " Out of memory " ) ;
// between here and free(out) below, exitting would leak
temp_out = p ;
if ( pal_img_n = = 3 ) {
for ( i = 0 ; i < pixel_count ; + + i ) {
int n = orig [ i ] * 4 ;
p [ 0 ] = palette [ n ] ;
p [ 1 ] = palette [ n + 1 ] ;
p [ 2 ] = palette [ n + 2 ] ;
p + = 3 ;
}
} else {
for ( i = 0 ; i < pixel_count ; + + i ) {
int n = orig [ i ] * 4 ;
p [ 0 ] = palette [ n ] ;
p [ 1 ] = palette [ n + 1 ] ;
p [ 2 ] = palette [ n + 2 ] ;
p [ 3 ] = palette [ n + 3 ] ;
p + = 4 ;
}
}
2010-03-03 17:02:51 +00:00
free ( a - > out ) ;
a - > out = temp_out ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
2010-03-03 17:02:51 +00:00
static int parse_png_file ( png * z , int scan , int req_comp )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint8 palette [ 1024 ] , pal_img_n = 0 ;
guint8 has_trans = 0 , tc [ 3 ] ;
guint32 ioff = 0 , idata_limit = 0 , i , pal_len = 0 ;
2010-03-03 17:02:51 +00:00
int first = 1 , k , interlace = 0 ;
stbi * s = & z - > s ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
if ( ! check_png_header ( s ) ) return 0 ;
2010-01-05 18:02:29 +00:00
if ( scan = = SCAN_type ) return 1 ;
for ( ; ; first = 0 ) {
2010-03-03 17:02:51 +00:00
chunk c = get_chunk_header ( s ) ;
2010-01-05 18:02:29 +00:00
if ( first & & c . type ! = PNG_TYPE ( ' I ' , ' H ' , ' D ' , ' R ' ) )
return e ( " first not IHDR " , " Corrupt PNG " ) ;
switch ( c . type ) {
case PNG_TYPE ( ' I ' , ' H ' , ' D ' , ' R ' ) : {
2010-03-03 17:02:51 +00:00
int depth , color , comp , filter ;
2010-01-05 18:02:29 +00:00
if ( ! first ) return e ( " multiple IHDR " , " Corrupt PNG " ) ;
if ( c . length ! = 13 ) return e ( " bad IHDR len " , " Corrupt PNG " ) ;
2010-03-03 17:02:51 +00:00
s - > img_x = get32 ( s ) ; if ( s - > img_x > ( 1 < < 24 ) ) return e ( " too large " , " Very large image (corrupt?) " ) ;
s - > img_y = get32 ( s ) ; if ( s - > img_y > ( 1 < < 24 ) ) return e ( " too large " , " Very large image (corrupt?) " ) ;
depth = get8 ( s ) ; if ( depth ! = 8 ) return e ( " 8bit only " , " PNG not supported: 8-bit only " ) ;
color = get8 ( s ) ; if ( color > 6 ) return e ( " bad ctype " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
if ( color = = 3 ) pal_img_n = 3 ; else if ( color & 1 ) return e ( " bad ctype " , " Corrupt PNG " ) ;
2010-03-03 17:02:51 +00:00
comp = get8 ( s ) ; if ( comp ) return e ( " bad comp method " , " Corrupt PNG " ) ;
filter = get8 ( s ) ; if ( filter ) return e ( " bad filter method " , " Corrupt PNG " ) ;
interlace = get8 ( s ) ; if ( interlace > 1 ) return e ( " bad interlace method " , " Corrupt PNG " ) ;
if ( ! s - > img_x | | ! s - > img_y ) return e ( " 0-pixel image " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
if ( ! pal_img_n ) {
2010-03-03 17:02:51 +00:00
s - > img_n = ( color & 2 ? 3 : 1 ) + ( color & 4 ? 1 : 0 ) ;
if ( ( 1 < < 30 ) / s - > img_x / s - > img_n < s - > img_y ) return e ( " too large " , " Image too large to decode " ) ;
2010-01-05 18:02:29 +00:00
if ( scan = = SCAN_header ) return 1 ;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
2010-03-03 17:02:51 +00:00
s - > img_n = 1 ;
if ( ( 1 < < 30 ) / s - > img_x / 4 < s - > img_y ) return e ( " too large " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
// if SCAN_header, have to scan to see if we have a tRNS
}
break ;
}
case PNG_TYPE ( ' P ' , ' L ' , ' T ' , ' E ' ) : {
if ( c . length > 256 * 3 ) return e ( " invalid PLTE " , " Corrupt PNG " ) ;
pal_len = c . length / 3 ;
if ( pal_len * 3 ! = c . length ) return e ( " invalid PLTE " , " Corrupt PNG " ) ;
for ( i = 0 ; i < pal_len ; + + i ) {
2010-03-03 17:02:51 +00:00
palette [ i * 4 + 0 ] = get8u ( s ) ;
palette [ i * 4 + 1 ] = get8u ( s ) ;
palette [ i * 4 + 2 ] = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
palette [ i * 4 + 3 ] = 255 ;
}
break ;
}
case PNG_TYPE ( ' t ' , ' R ' , ' N ' , ' S ' ) : {
2010-03-03 17:02:51 +00:00
if ( z - > idata ) return e ( " tRNS after IDAT " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
if ( pal_img_n ) {
2010-03-03 17:02:51 +00:00
if ( scan = = SCAN_header ) { s - > img_n = 4 ; return 1 ; }
2010-01-05 18:02:29 +00:00
if ( pal_len = = 0 ) return e ( " tRNS before PLTE " , " Corrupt PNG " ) ;
if ( c . length > pal_len ) return e ( " bad tRNS len " , " Corrupt PNG " ) ;
pal_img_n = 4 ;
for ( i = 0 ; i < c . length ; + + i )
2010-03-03 17:02:51 +00:00
palette [ i * 4 + 3 ] = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
if ( ! ( s - > img_n & 1 ) ) return e ( " tRNS with alpha " , " Corrupt PNG " ) ;
2011-08-04 14:09:17 +00:00
if ( c . length ! = ( guint32 ) s - > img_n * 2 ) return e ( " bad tRNS len " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
has_trans = 1 ;
2010-03-03 17:02:51 +00:00
for ( k = 0 ; k < s - > img_n ; + + k )
2011-08-04 14:09:17 +00:00
tc [ k ] = ( guint8 ) get16 ( s ) ; // non 8-bit images will be larger
2010-01-05 18:02:29 +00:00
}
break ;
}
case PNG_TYPE ( ' I ' , ' D ' , ' A ' , ' T ' ) : {
if ( pal_img_n & & ! pal_len ) return e ( " no PLTE " , " Corrupt PNG " ) ;
2010-03-03 17:02:51 +00:00
if ( scan = = SCAN_header ) { s - > img_n = pal_img_n ; return 1 ; }
2010-01-05 18:02:29 +00:00
if ( ioff + c . length > idata_limit ) {
2011-08-04 14:09:17 +00:00
guint8 * p ;
2010-01-05 18:02:29 +00:00
if ( idata_limit = = 0 ) idata_limit = c . length > 4096 ? c . length : 4096 ;
while ( ioff + c . length > idata_limit )
idata_limit * = 2 ;
2011-08-04 14:09:17 +00:00
p = ( guint8 * ) realloc ( z - > idata , idata_limit ) ; if ( p = = NULL ) return e ( " outofmem " , " Out of memory " ) ;
2010-03-03 17:02:51 +00:00
z - > idata = p ;
2010-01-05 18:02:29 +00:00
}
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
if ( s - > img_file )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
if ( fread ( z - > idata + ioff , 1 , c . length , s - > img_file ) ! = c . length ) return e ( " outofdata " , " Corrupt PNG " ) ;
2010-01-05 18:02:29 +00:00
}
else
# endif
{
2010-03-03 17:02:51 +00:00
memcpy ( z - > idata + ioff , s - > img_buffer , c . length ) ;
s - > img_buffer + = c . length ;
2010-01-05 18:02:29 +00:00
}
ioff + = c . length ;
break ;
}
case PNG_TYPE ( ' I ' , ' E ' , ' N ' , ' D ' ) : {
2011-08-04 14:09:17 +00:00
guint32 raw_len ;
2010-01-05 18:02:29 +00:00
if ( scan ! = SCAN_load ) return 1 ;
2010-03-03 17:02:51 +00:00
if ( z - > idata = = NULL ) return e ( " no IDAT " , " Corrupt PNG " ) ;
2011-08-04 14:09:17 +00:00
z - > expanded = ( guint8 * ) stbi_zlib_decode_malloc ( ( char * ) z - > idata , ioff , ( int * ) & raw_len ) ;
2010-03-03 17:02:51 +00:00
if ( z - > expanded = = NULL ) return 0 ; // zlib should set error
free ( z - > idata ) ; z - > idata = NULL ;
if ( ( req_comp = = s - > img_n + 1 & & req_comp ! = 3 & & ! pal_img_n ) | | has_trans )
s - > img_out_n = s - > img_n + 1 ;
2010-01-05 18:02:29 +00:00
else
2010-03-03 17:02:51 +00:00
s - > img_out_n = s - > img_n ;
if ( ! create_png_image ( z , z - > expanded , raw_len , s - > img_out_n , interlace ) ) return 0 ;
2010-01-05 18:02:29 +00:00
if ( has_trans )
2010-03-03 17:02:51 +00:00
if ( ! compute_transparency ( z , tc , s - > img_out_n ) ) return 0 ;
2010-01-05 18:02:29 +00:00
if ( pal_img_n ) {
// pal_img_n == 3 or 4
2010-03-03 17:02:51 +00:00
s - > img_n = pal_img_n ; // record the actual colors we had
s - > img_out_n = pal_img_n ;
if ( req_comp > = 3 ) s - > img_out_n = req_comp ;
if ( ! expand_palette ( z , palette , pal_len , s - > img_out_n ) )
2010-01-05 18:02:29 +00:00
return 0 ;
}
2010-03-03 17:02:51 +00:00
free ( z - > expanded ) ; z - > expanded = NULL ;
2010-01-05 18:02:29 +00:00
return 1 ;
}
default :
// if critical, fail
if ( ( c . type & ( 1 < < 29 ) ) = = 0 ) {
# ifndef STBI_NO_FAILURE_STRINGS
2010-03-03 17:02:51 +00:00
// not threadsafe
2010-01-05 18:02:29 +00:00
static char invalid_chunk [ ] = " XXXX chunk not known " ;
2011-08-04 14:09:17 +00:00
invalid_chunk [ 0 ] = ( guint8 ) ( c . type > > 24 ) ;
invalid_chunk [ 1 ] = ( guint8 ) ( c . type > > 16 ) ;
invalid_chunk [ 2 ] = ( guint8 ) ( c . type > > 8 ) ;
invalid_chunk [ 3 ] = ( guint8 ) ( c . type > > 0 ) ;
2010-01-05 18:02:29 +00:00
# endif
return e ( invalid_chunk , " PNG not supported: unknown chunk type " ) ;
}
2010-03-03 17:02:51 +00:00
skip ( s , c . length ) ;
2010-01-05 18:02:29 +00:00
break ;
}
// end of chunk, read and skip CRC
2010-03-03 17:02:51 +00:00
get32 ( s ) ;
2010-01-05 18:02:29 +00:00
}
}
2010-03-03 17:02:51 +00:00
static unsigned char * do_png ( png * p , int * x , int * y , int * n , int req_comp )
2010-01-05 18:02:29 +00:00
{
unsigned char * result = NULL ;
2010-03-03 17:02:51 +00:00
p - > expanded = NULL ;
p - > idata = NULL ;
p - > out = NULL ;
2010-01-05 18:02:29 +00:00
if ( req_comp < 0 | | req_comp > 4 ) return epuc ( " bad req_comp " , " Internal error " ) ;
2010-03-03 17:02:51 +00:00
if ( parse_png_file ( p , SCAN_load , req_comp ) ) {
result = p - > out ;
p - > out = NULL ;
if ( req_comp & & req_comp ! = p - > s . img_out_n ) {
result = convert_format ( result , p - > s . img_out_n , req_comp , p - > s . img_x , p - > s . img_y ) ;
p - > s . img_out_n = req_comp ;
2010-01-05 18:02:29 +00:00
if ( result = = NULL ) return result ;
}
2010-03-03 17:02:51 +00:00
* x = p - > s . img_x ;
* y = p - > s . img_y ;
if ( n ) * n = p - > s . img_n ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
free ( p - > out ) ; p - > out = NULL ;
free ( p - > expanded ) ; p - > expanded = NULL ;
free ( p - > idata ) ; p - > idata = NULL ;
2010-01-05 18:02:29 +00:00
return result ;
}
# ifndef STBI_NO_STDIO
unsigned char * stbi_png_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
png p ;
start_file ( & p . s , f ) ;
return do_png ( & p , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
unsigned char * stbi_png_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
unsigned char * data ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return NULL ;
data = stbi_png_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return data ;
}
# endif
unsigned char * stbi_png_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
png p ;
start_mem ( & p . s , buffer , len ) ;
return do_png ( & p , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# ifndef STBI_NO_STDIO
int stbi_png_test_file ( FILE * f )
{
2010-03-03 17:02:51 +00:00
png p ;
2010-01-05 18:02:29 +00:00
int n , r ;
n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & p . s , f ) ;
r = parse_png_file ( & p , SCAN_type , STBI_default ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
int stbi_png_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
png p ;
start_mem ( & p . s , buffer , len ) ;
return parse_png_file ( & p , SCAN_type , STBI_default ) ;
2010-01-05 18:02:29 +00:00
}
// TODO: load header from png
# ifndef STBI_NO_STDIO
2010-03-03 17:02:51 +00:00
int stbi_png_info ( char const * filename , int * x , int * y , int * comp )
{
png p ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return 0 ;
start_file ( & p . s , f ) ;
if ( parse_png_file ( & p , SCAN_header , 0 ) ) {
if ( x ) * x = p . s . img_x ;
if ( y ) * y = p . s . img_y ;
if ( comp ) * comp = p . s . img_n ;
fclose ( f ) ;
return 1 ;
}
fclose ( f ) ;
return 0 ;
}
2010-01-05 18:02:29 +00:00
extern int stbi_png_info_from_file ( FILE * f , int * x , int * y , int * comp ) ;
# endif
extern int stbi_png_info_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp ) ;
// Microsoft/Windows BMP image
2010-03-03 17:02:51 +00:00
static int bmp_test ( stbi * s )
2010-01-05 18:02:29 +00:00
{
int sz ;
2010-03-03 17:02:51 +00:00
if ( get8 ( s ) ! = ' B ' ) return 0 ;
if ( get8 ( s ) ! = ' M ' ) return 0 ;
get32le ( s ) ; // discard filesize
get16le ( s ) ; // discard reserved
get16le ( s ) ; // discard reserved
get32le ( s ) ; // discard data offset
sz = get32le ( s ) ;
2010-01-05 18:02:29 +00:00
if ( sz = = 12 | | sz = = 40 | | sz = = 56 | | sz = = 108 ) return 1 ;
return 0 ;
}
# ifndef STBI_NO_STDIO
int stbi_bmp_test_file ( FILE * f )
{
2010-03-03 17:02:51 +00:00
stbi s ;
2010-01-05 18:02:29 +00:00
int r , n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & s , f ) ;
r = bmp_test ( & s ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
int stbi_bmp_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return bmp_test ( & s ) ;
2010-01-05 18:02:29 +00:00
}
// returns 0..31 for the highest set bit
static int high_bit ( unsigned int z )
{
int n = 0 ;
if ( z = = 0 ) return - 1 ;
if ( z > = 0x10000 ) n + = 16 , z > > = 16 ;
if ( z > = 0x00100 ) n + = 8 , z > > = 8 ;
if ( z > = 0x00010 ) n + = 4 , z > > = 4 ;
if ( z > = 0x00004 ) n + = 2 , z > > = 2 ;
if ( z > = 0x00002 ) n + = 1 , z > > = 1 ;
return n ;
}
static int bitcount ( unsigned int a )
{
a = ( a & 0x55555555 ) + ( ( a > > 1 ) & 0x55555555 ) ; // max 2
a = ( a & 0x33333333 ) + ( ( a > > 2 ) & 0x33333333 ) ; // max 4
a = ( a + ( a > > 4 ) ) & 0x0f0f0f0f ; // max 8 per 4, now 8 bits
a = ( a + ( a > > 8 ) ) ; // max 16 per 8 bits
a = ( a + ( a > > 16 ) ) ; // max 32 per 8 bits
return a & 0xff ;
}
static int shiftsigned ( int v , int shift , int bits )
{
int result ;
int z = 0 ;
if ( shift < 0 ) v < < = - shift ;
else v > > = shift ;
result = v ;
z = bits ;
while ( z < 8 ) {
result + = v > > z ;
z + = bits ;
}
return result ;
}
2010-03-03 17:02:51 +00:00
static stbi_uc * bmp_load ( stbi * s , int * x , int * y , int * comp , int req_comp )
2010-01-05 18:02:29 +00:00
{
2011-08-04 14:09:17 +00:00
guint8 * out ;
2010-03-03 17:02:51 +00:00
unsigned int mr = 0 , mg = 0 , mb = 0 , ma = 0 , fake_a = 0 ;
2010-01-05 18:02:29 +00:00
stbi_uc pal [ 256 ] [ 4 ] ;
int psize = 0 , i , j , compress = 0 , width ;
int bpp , flip_vertically , pad , target , offset , hsz ;
2010-03-03 17:02:51 +00:00
if ( get8 ( s ) ! = ' B ' | | get8 ( s ) ! = ' M ' ) return epuc ( " not BMP " , " Corrupt BMP " ) ;
get32le ( s ) ; // discard filesize
get16le ( s ) ; // discard reserved
get16le ( s ) ; // discard reserved
offset = get32le ( s ) ;
hsz = get32le ( s ) ;
2010-01-05 18:02:29 +00:00
if ( hsz ! = 12 & & hsz ! = 40 & & hsz ! = 56 & & hsz ! = 108 ) return epuc ( " unknown BMP " , " BMP type not supported: unknown " ) ;
failure_reason = " bad BMP " ;
if ( hsz = = 12 ) {
2010-03-03 17:02:51 +00:00
s - > img_x = get16le ( s ) ;
s - > img_y = get16le ( s ) ;
2010-01-05 18:02:29 +00:00
} else {
2010-03-03 17:02:51 +00:00
s - > img_x = get32le ( s ) ;
s - > img_y = get32le ( s ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
if ( get16le ( s ) ! = 1 ) return 0 ;
bpp = get16le ( s ) ;
2010-01-05 18:02:29 +00:00
if ( bpp = = 1 ) return epuc ( " monochrome " , " BMP type not supported: 1-bit " ) ;
2010-03-03 17:02:51 +00:00
flip_vertically = ( ( int ) s - > img_y ) > 0 ;
s - > img_y = abs ( ( int ) s - > img_y ) ;
2010-01-05 18:02:29 +00:00
if ( hsz = = 12 ) {
if ( bpp < 24 )
psize = ( offset - 14 - 24 ) / 3 ;
} else {
2010-03-03 17:02:51 +00:00
compress = get32le ( s ) ;
2010-01-05 18:02:29 +00:00
if ( compress = = 1 | | compress = = 2 ) return epuc ( " BMP RLE " , " BMP type not supported: RLE " ) ;
2010-03-03 17:02:51 +00:00
get32le ( s ) ; // discard sizeof
get32le ( s ) ; // discard hres
get32le ( s ) ; // discard vres
get32le ( s ) ; // discard colorsused
get32le ( s ) ; // discard max important
2010-01-05 18:02:29 +00:00
if ( hsz = = 40 | | hsz = = 56 ) {
if ( hsz = = 56 ) {
2010-03-03 17:02:51 +00:00
get32le ( s ) ;
get32le ( s ) ;
get32le ( s ) ;
get32le ( s ) ;
2010-01-05 18:02:29 +00:00
}
if ( bpp = = 16 | | bpp = = 32 ) {
mr = mg = mb = 0 ;
if ( compress = = 0 ) {
if ( bpp = = 32 ) {
mr = 0xff < < 16 ;
mg = 0xff < < 8 ;
mb = 0xff < < 0 ;
2010-03-03 17:02:51 +00:00
ma = 0xff < < 24 ;
fake_a = 1 ; // @TODO: check for cases like alpha value is all 0 and switch it to 255
2010-01-05 18:02:29 +00:00
} else {
mr = 31 < < 10 ;
mg = 31 < < 5 ;
mb = 31 < < 0 ;
}
} else if ( compress = = 3 ) {
2010-03-03 17:02:51 +00:00
mr = get32le ( s ) ;
mg = get32le ( s ) ;
mb = get32le ( s ) ;
2010-01-05 18:02:29 +00:00
// not documented, but generated by photoshop and handled by mspaint
if ( mr = = mg & & mg = = mb ) {
// ?!?!?
return NULL ;
}
} else
return NULL ;
}
} else {
assert ( hsz = = 108 ) ;
2010-03-03 17:02:51 +00:00
mr = get32le ( s ) ;
mg = get32le ( s ) ;
mb = get32le ( s ) ;
ma = get32le ( s ) ;
get32le ( s ) ; // discard color space
2010-01-05 18:02:29 +00:00
for ( i = 0 ; i < 12 ; + + i )
2010-03-03 17:02:51 +00:00
get32le ( s ) ; // discard color space parameters
2010-01-05 18:02:29 +00:00
}
if ( bpp < 16 )
psize = ( offset - 14 - hsz ) > > 2 ;
}
2010-03-03 17:02:51 +00:00
s - > img_n = ma ? 4 : 3 ;
2010-01-05 18:02:29 +00:00
if ( req_comp & & req_comp > = 3 ) // we can directly decode 3 or 4
target = req_comp ;
else
2010-03-03 17:02:51 +00:00
target = s - > img_n ; // if they want monochrome, we'll post-convert
out = ( stbi_uc * ) malloc ( target * s - > img_x * s - > img_y ) ;
2010-01-05 18:02:29 +00:00
if ( ! out ) return epuc ( " outofmem " , " Out of memory " ) ;
if ( bpp < 16 ) {
int z = 0 ;
2010-03-03 17:02:51 +00:00
if ( psize = = 0 | | psize > 256 ) { free ( out ) ; return epuc ( " invalid " , " Corrupt BMP " ) ; }
2010-01-05 18:02:29 +00:00
for ( i = 0 ; i < psize ; + + i ) {
2010-03-03 17:02:51 +00:00
pal [ i ] [ 2 ] = get8 ( s ) ;
pal [ i ] [ 1 ] = get8 ( s ) ;
pal [ i ] [ 0 ] = get8 ( s ) ;
if ( hsz ! = 12 ) get8 ( s ) ;
2010-01-05 18:02:29 +00:00
pal [ i ] [ 3 ] = 255 ;
}
2010-03-03 17:02:51 +00:00
skip ( s , offset - 14 - hsz - psize * ( hsz = = 12 ? 3 : 4 ) ) ;
if ( bpp = = 4 ) width = ( s - > img_x + 1 ) > > 1 ;
else if ( bpp = = 8 ) width = s - > img_x ;
else { free ( out ) ; return epuc ( " bad bpp " , " Corrupt BMP " ) ; }
2010-01-05 18:02:29 +00:00
pad = ( - width ) & 3 ;
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < ( int ) s - > img_y ; + + j ) {
for ( i = 0 ; i < ( int ) s - > img_x ; i + = 2 ) {
int v = get8 ( s ) , v2 = 0 ;
2010-01-05 18:02:29 +00:00
if ( bpp = = 4 ) {
v2 = v & 15 ;
v > > = 4 ;
}
out [ z + + ] = pal [ v ] [ 0 ] ;
out [ z + + ] = pal [ v ] [ 1 ] ;
out [ z + + ] = pal [ v ] [ 2 ] ;
if ( target = = 4 ) out [ z + + ] = 255 ;
2010-03-03 17:02:51 +00:00
if ( i + 1 = = ( int ) s - > img_x ) break ;
v = ( bpp = = 8 ) ? get8 ( s ) : v2 ;
2010-01-05 18:02:29 +00:00
out [ z + + ] = pal [ v ] [ 0 ] ;
out [ z + + ] = pal [ v ] [ 1 ] ;
out [ z + + ] = pal [ v ] [ 2 ] ;
if ( target = = 4 ) out [ z + + ] = 255 ;
}
2010-03-03 17:02:51 +00:00
skip ( s , pad ) ;
2010-01-05 18:02:29 +00:00
}
} else {
int rshift = 0 , gshift = 0 , bshift = 0 , ashift = 0 , rcount = 0 , gcount = 0 , bcount = 0 , acount = 0 ;
int z = 0 ;
int easy = 0 ;
2010-03-03 17:02:51 +00:00
skip ( s , offset - 14 - hsz ) ;
if ( bpp = = 24 ) width = 3 * s - > img_x ;
else if ( bpp = = 16 ) width = 2 * s - > img_x ;
2010-01-05 18:02:29 +00:00
else /* bpp = 32 and pad = 0 */ width = 0 ;
pad = ( - width ) & 3 ;
if ( bpp = = 24 ) {
easy = 1 ;
} else if ( bpp = = 32 ) {
if ( mb = = 0xff & & mg = = 0xff00 & & mr = = 0xff000000 & & ma = = 0xff000000 )
easy = 2 ;
}
if ( ! easy ) {
if ( ! mr | | ! mg | | ! mb ) return epuc ( " bad masks " , " Corrupt BMP " ) ;
// right shift amt to put high bit in position #7
rshift = high_bit ( mr ) - 7 ; rcount = bitcount ( mr ) ;
gshift = high_bit ( mg ) - 7 ; gcount = bitcount ( mr ) ;
bshift = high_bit ( mb ) - 7 ; bcount = bitcount ( mr ) ;
ashift = high_bit ( ma ) - 7 ; acount = bitcount ( mr ) ;
}
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < ( int ) s - > img_y ; + + j ) {
2010-01-05 18:02:29 +00:00
if ( easy ) {
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < ( int ) s - > img_x ; + + i ) {
2010-01-05 18:02:29 +00:00
int a ;
2010-03-03 17:02:51 +00:00
out [ z + 2 ] = get8 ( s ) ;
out [ z + 1 ] = get8 ( s ) ;
out [ z + 0 ] = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
z + = 3 ;
2010-03-03 17:02:51 +00:00
a = ( easy = = 2 ? get8 ( s ) : 255 ) ;
2010-01-05 18:02:29 +00:00
if ( target = = 4 ) out [ z + + ] = a ;
}
} else {
2010-03-03 17:02:51 +00:00
for ( i = 0 ; i < ( int ) s - > img_x ; + + i ) {
2011-08-04 14:09:17 +00:00
guint32 v = ( bpp = = 16 ? get16le ( s ) : get32le ( s ) ) ;
2010-01-05 18:02:29 +00:00
int a ;
out [ z + + ] = shiftsigned ( v & mr , rshift , rcount ) ;
out [ z + + ] = shiftsigned ( v & mg , gshift , gcount ) ;
out [ z + + ] = shiftsigned ( v & mb , bshift , bcount ) ;
a = ( ma ? shiftsigned ( v & ma , ashift , acount ) : 255 ) ;
if ( target = = 4 ) out [ z + + ] = a ;
}
}
2010-03-03 17:02:51 +00:00
skip ( s , pad ) ;
2010-01-05 18:02:29 +00:00
}
}
if ( flip_vertically ) {
stbi_uc t ;
2010-03-03 17:02:51 +00:00
for ( j = 0 ; j < ( int ) s - > img_y > > 1 ; + + j ) {
stbi_uc * p1 = out + j * s - > img_x * target ;
stbi_uc * p2 = out + ( s - > img_y - 1 - j ) * s - > img_x * target ;
for ( i = 0 ; i < ( int ) s - > img_x * target ; + + i ) {
2010-01-05 18:02:29 +00:00
t = p1 [ i ] , p1 [ i ] = p2 [ i ] , p2 [ i ] = t ;
}
}
}
if ( req_comp & & req_comp ! = target ) {
2010-03-03 17:02:51 +00:00
out = convert_format ( out , target , req_comp , s - > img_x , s - > img_y ) ;
2010-01-05 18:02:29 +00:00
if ( out = = NULL ) return out ; // convert_format frees input on failure
}
2010-03-03 17:02:51 +00:00
* x = s - > img_x ;
* y = s - > img_y ;
2010-01-05 18:02:29 +00:00
if ( comp ) * comp = target ;
return out ;
}
# ifndef STBI_NO_STDIO
stbi_uc * stbi_bmp_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
stbi_uc * data ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return NULL ;
data = stbi_bmp_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return data ;
}
stbi_uc * stbi_bmp_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_file ( & s , f ) ;
return bmp_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# endif
stbi_uc * stbi_bmp_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return bmp_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
// Targa Truevision - TGA
// by Jonathan Dummer
2010-03-03 17:02:51 +00:00
static int tga_test ( stbi * s )
2010-01-05 18:02:29 +00:00
{
int sz ;
2010-03-03 17:02:51 +00:00
get8u ( s ) ; // discard Offset
sz = get8u ( s ) ; // color type
2010-01-05 18:02:29 +00:00
if ( sz > 1 ) return 0 ; // only RGB or indexed allowed
2010-03-03 17:02:51 +00:00
sz = get8u ( s ) ; // image type
2010-01-05 18:02:29 +00:00
if ( ( sz ! = 1 ) & & ( sz ! = 2 ) & & ( sz ! = 3 ) & & ( sz ! = 9 ) & & ( sz ! = 10 ) & & ( sz ! = 11 ) ) return 0 ; // only RGB or grey allowed, +/- RLE
2010-03-03 17:02:51 +00:00
get16 ( s ) ; // discard palette start
get16 ( s ) ; // discard palette length
get8 ( s ) ; // discard bits per palette color entry
get16 ( s ) ; // discard x origin
get16 ( s ) ; // discard y origin
if ( get16 ( s ) < 1 ) return 0 ; // test width
if ( get16 ( s ) < 1 ) return 0 ; // test height
sz = get8 ( s ) ; // bits per pixel
2010-01-05 18:02:29 +00:00
if ( ( sz ! = 8 ) & & ( sz ! = 16 ) & & ( sz ! = 24 ) & & ( sz ! = 32 ) ) return 0 ; // only RGB or RGBA or grey allowed
return 1 ; // seems to have passed everything
}
# ifndef STBI_NO_STDIO
int stbi_tga_test_file ( FILE * f )
{
2010-03-03 17:02:51 +00:00
stbi s ;
2010-01-05 18:02:29 +00:00
int r , n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & s , f ) ;
r = tga_test ( & s ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
int stbi_tga_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return tga_test ( & s ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static stbi_uc * tga_load ( stbi * s , int * x , int * y , int * comp , int req_comp )
2010-01-05 18:02:29 +00:00
{
// read in the TGA header stuff
2010-03-03 17:02:51 +00:00
int tga_offset = get8u ( s ) ;
int tga_indexed = get8u ( s ) ;
int tga_image_type = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
int tga_is_RLE = 0 ;
2010-03-03 17:02:51 +00:00
int tga_palette_start = get16le ( s ) ;
int tga_palette_len = get16le ( s ) ;
int tga_palette_bits = get8u ( s ) ;
int tga_x_origin = get16le ( s ) ;
int tga_y_origin = get16le ( s ) ;
int tga_width = get16le ( s ) ;
int tga_height = get16le ( s ) ;
int tga_bits_per_pixel = get8u ( s ) ;
int tga_inverted = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
// image data
unsigned char * tga_data ;
unsigned char * tga_palette = NULL ;
int i , j ;
unsigned char raw_data [ 4 ] ;
unsigned char trans_data [ 4 ] ;
int RLE_count = 0 ;
int RLE_repeating = 0 ;
int read_next_pixel = 1 ;
// do a tiny bit of precessing
if ( tga_image_type > = 8 )
{
tga_image_type - = 8 ;
tga_is_RLE = 1 ;
}
/* int tga_alpha_bits = tga_inverted & 15; */
tga_inverted = 1 - ( ( tga_inverted > > 5 ) & 1 ) ;
// error check
if ( //(tga_indexed) ||
( tga_width < 1 ) | | ( tga_height < 1 ) | |
( tga_image_type < 1 ) | | ( tga_image_type > 3 ) | |
( ( tga_bits_per_pixel ! = 8 ) & & ( tga_bits_per_pixel ! = 16 ) & &
( tga_bits_per_pixel ! = 24 ) & & ( tga_bits_per_pixel ! = 32 ) )
)
{
return NULL ;
}
// If I'm paletted, then I'll use the number of bits from the palette
if ( tga_indexed )
{
tga_bits_per_pixel = tga_palette_bits ;
}
// tga info
* x = tga_width ;
* y = tga_height ;
if ( ( req_comp < 1 ) | | ( req_comp > 4 ) )
{
// just use whatever the file was
req_comp = tga_bits_per_pixel / 8 ;
* comp = req_comp ;
} else
{
// force a new number of components
* comp = tga_bits_per_pixel / 8 ;
}
tga_data = ( unsigned char * ) malloc ( tga_width * tga_height * req_comp ) ;
// skip to the data's starting position (offset usually = 0)
2010-03-03 17:02:51 +00:00
skip ( s , tga_offset ) ;
2010-01-05 18:02:29 +00:00
// do I need to load a palette?
if ( tga_indexed )
{
// any data to skip? (offset usually = 0)
2010-03-03 17:02:51 +00:00
skip ( s , tga_palette_start ) ;
2010-01-05 18:02:29 +00:00
// load the palette
tga_palette = ( unsigned char * ) malloc ( tga_palette_len * tga_palette_bits / 8 ) ;
2010-03-03 17:02:51 +00:00
getn ( s , tga_palette , tga_palette_len * tga_palette_bits / 8 ) ;
2010-01-05 18:02:29 +00:00
}
// load the data
for ( i = 0 ; i < tga_width * tga_height ; + + i )
{
// if I'm in RLE mode, do I need to get a RLE chunk?
if ( tga_is_RLE )
{
if ( RLE_count = = 0 )
{
// yep, get the next byte as a RLE command
2010-03-03 17:02:51 +00:00
int RLE_cmd = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
RLE_count = 1 + ( RLE_cmd & 127 ) ;
RLE_repeating = RLE_cmd > > 7 ;
read_next_pixel = 1 ;
} else if ( ! RLE_repeating )
{
read_next_pixel = 1 ;
}
} else
{
read_next_pixel = 1 ;
}
// OK, if I need to read a pixel, do it now
if ( read_next_pixel )
{
// load however much data we did have
if ( tga_indexed )
{
// read in 1 byte, then perform the lookup
2010-03-03 17:02:51 +00:00
int pal_idx = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
if ( pal_idx > = tga_palette_len )
{
// invalid index
pal_idx = 0 ;
}
pal_idx * = tga_bits_per_pixel / 8 ;
for ( j = 0 ; j * 8 < tga_bits_per_pixel ; + + j )
{
raw_data [ j ] = tga_palette [ pal_idx + j ] ;
}
} else
{
// read in the data raw
for ( j = 0 ; j * 8 < tga_bits_per_pixel ; + + j )
{
2010-03-03 17:02:51 +00:00
raw_data [ j ] = get8u ( s ) ;
2010-01-05 18:02:29 +00:00
}
}
// convert raw to the intermediate format
switch ( tga_bits_per_pixel )
{
case 8 :
// Luminous => RGBA
trans_data [ 0 ] = raw_data [ 0 ] ;
trans_data [ 1 ] = raw_data [ 0 ] ;
trans_data [ 2 ] = raw_data [ 0 ] ;
trans_data [ 3 ] = 255 ;
break ;
case 16 :
// Luminous,Alpha => RGBA
trans_data [ 0 ] = raw_data [ 0 ] ;
trans_data [ 1 ] = raw_data [ 0 ] ;
trans_data [ 2 ] = raw_data [ 0 ] ;
trans_data [ 3 ] = raw_data [ 1 ] ;
break ;
case 24 :
// BGR => RGBA
trans_data [ 0 ] = raw_data [ 2 ] ;
trans_data [ 1 ] = raw_data [ 1 ] ;
trans_data [ 2 ] = raw_data [ 0 ] ;
trans_data [ 3 ] = 255 ;
break ;
case 32 :
// BGRA => RGBA
trans_data [ 0 ] = raw_data [ 2 ] ;
trans_data [ 1 ] = raw_data [ 1 ] ;
trans_data [ 2 ] = raw_data [ 0 ] ;
trans_data [ 3 ] = raw_data [ 3 ] ;
break ;
}
// clear the reading flag for the next pixel
read_next_pixel = 0 ;
} // end of reading a pixel
// convert to final format
switch ( req_comp )
{
case 1 :
// RGBA => Luminance
tga_data [ i * req_comp + 0 ] = compute_y ( trans_data [ 0 ] , trans_data [ 1 ] , trans_data [ 2 ] ) ;
break ;
case 2 :
// RGBA => Luminance,Alpha
tga_data [ i * req_comp + 0 ] = compute_y ( trans_data [ 0 ] , trans_data [ 1 ] , trans_data [ 2 ] ) ;
tga_data [ i * req_comp + 1 ] = trans_data [ 3 ] ;
break ;
case 3 :
// RGBA => RGB
tga_data [ i * req_comp + 0 ] = trans_data [ 0 ] ;
tga_data [ i * req_comp + 1 ] = trans_data [ 1 ] ;
tga_data [ i * req_comp + 2 ] = trans_data [ 2 ] ;
break ;
case 4 :
// RGBA => RGBA
tga_data [ i * req_comp + 0 ] = trans_data [ 0 ] ;
tga_data [ i * req_comp + 1 ] = trans_data [ 1 ] ;
tga_data [ i * req_comp + 2 ] = trans_data [ 2 ] ;
tga_data [ i * req_comp + 3 ] = trans_data [ 3 ] ;
break ;
}
// in case we're in RLE mode, keep counting down
- - RLE_count ;
}
// do I need to invert the image?
if ( tga_inverted )
{
for ( j = 0 ; j * 2 < tga_height ; + + j )
{
int index1 = j * tga_width * req_comp ;
int index2 = ( tga_height - 1 - j ) * tga_width * req_comp ;
for ( i = tga_width * req_comp ; i > 0 ; - - i )
{
unsigned char temp = tga_data [ index1 ] ;
tga_data [ index1 ] = tga_data [ index2 ] ;
tga_data [ index2 ] = temp ;
+ + index1 ;
+ + index2 ;
}
}
}
// clear my palette, if I had one
if ( tga_palette ! = NULL )
{
free ( tga_palette ) ;
}
// the things I do to get rid of an error message, and yet keep
// Microsoft's C compilers happy... [8^(
tga_palette_start = tga_palette_len = tga_palette_bits =
tga_x_origin = tga_y_origin = 0 ;
// OK, done
return tga_data ;
}
# ifndef STBI_NO_STDIO
stbi_uc * stbi_tga_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
stbi_uc * data ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return NULL ;
data = stbi_tga_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return data ;
}
stbi_uc * stbi_tga_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_file ( & s , f ) ;
return tga_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# endif
stbi_uc * stbi_tga_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return tga_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
// *************************************************************************************************
// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB
2010-03-03 17:02:51 +00:00
static int psd_test ( stbi * s )
2010-01-05 18:02:29 +00:00
{
2010-03-03 17:02:51 +00:00
if ( get32 ( s ) ! = 0x38425053 ) return 0 ; // "8BPS"
2010-01-05 18:02:29 +00:00
else return 1 ;
}
# ifndef STBI_NO_STDIO
int stbi_psd_test_file ( FILE * f )
{
2010-03-03 17:02:51 +00:00
stbi s ;
2010-01-05 18:02:29 +00:00
int r , n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & s , f ) ;
r = psd_test ( & s ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
int stbi_psd_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return psd_test ( & s ) ;
2010-01-05 18:02:29 +00:00
}
2010-03-03 17:02:51 +00:00
static stbi_uc * psd_load ( stbi * s , int * x , int * y , int * comp , int req_comp )
2010-01-05 18:02:29 +00:00
{
int pixelCount ;
int channelCount , compression ;
int channel , i , count , len ;
int w , h ;
2011-08-04 14:09:17 +00:00
guint8 * out ;
2010-01-05 18:02:29 +00:00
// Check identifier
2010-03-03 17:02:51 +00:00
if ( get32 ( s ) ! = 0x38425053 ) // "8BPS"
2010-01-05 18:02:29 +00:00
return epuc ( " not PSD " , " Corrupt PSD image " ) ;
// Check file type version.
2010-03-03 17:02:51 +00:00
if ( get16 ( s ) ! = 1 )
2010-01-05 18:02:29 +00:00
return epuc ( " wrong version " , " Unsupported version of PSD image " ) ;
// Skip 6 reserved bytes.
2010-03-03 17:02:51 +00:00
skip ( s , 6 ) ;
2010-01-05 18:02:29 +00:00
// Read the number of channels (R, G, B, A, etc).
2010-03-03 17:02:51 +00:00
channelCount = get16 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( channelCount < 0 | | channelCount > 16 )
return epuc ( " wrong channel count " , " Unsupported number of channels in PSD image " ) ;
// Read the rows and columns of the image.
2010-03-03 17:02:51 +00:00
h = get32 ( s ) ;
w = get32 ( s ) ;
2010-01-05 18:02:29 +00:00
// Make sure the depth is 8 bits.
2010-03-03 17:02:51 +00:00
if ( get16 ( s ) ! = 8 )
2010-01-05 18:02:29 +00:00
return epuc ( " unsupported bit depth " , " PSD bit depth is not 8 bit " ) ;
// Make sure the color mode is RGB.
// Valid options are:
// 0: Bitmap
// 1: Grayscale
// 2: Indexed color
// 3: RGB color
// 4: CMYK color
// 7: Multichannel
// 8: Duotone
// 9: Lab color
2010-03-03 17:02:51 +00:00
if ( get16 ( s ) ! = 3 )
2010-01-05 18:02:29 +00:00
return epuc ( " wrong color format " , " PSD is not in RGB color format " ) ;
// Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
2010-03-03 17:02:51 +00:00
skip ( s , get32 ( s ) ) ;
2010-01-05 18:02:29 +00:00
// Skip the image resources. (resolution, pen tool paths, etc)
2010-03-03 17:02:51 +00:00
skip ( s , get32 ( s ) ) ;
2010-01-05 18:02:29 +00:00
// Skip the reserved data.
2010-03-03 17:02:51 +00:00
skip ( s , get32 ( s ) ) ;
2010-01-05 18:02:29 +00:00
// Find out if the data is compressed.
// Known values:
// 0: no compression
// 1: RLE compressed
2010-03-03 17:02:51 +00:00
compression = get16 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( compression > 1 )
2010-03-03 17:02:51 +00:00
return epuc ( " bad compression " , " PSD has an unknown compression format " ) ;
2010-01-05 18:02:29 +00:00
// Create the destination image.
out = ( stbi_uc * ) malloc ( 4 * w * h ) ;
if ( ! out ) return epuc ( " outofmem " , " Out of memory " ) ;
pixelCount = w * h ;
// Initialize the data to zero.
//memset( out, 0, pixelCount * 4 );
// Finally, the image data.
if ( compression ) {
// RLE as used by .PSD and .TIFF
// Loop until you get the number of unpacked bytes you are expecting:
// Read the next source byte into n.
// If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
// Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
// Else if n is 128, noop.
// Endloop
// The RLE-compressed data is preceeded by a 2-byte data count for each row in the data,
// which we're going to just skip.
2010-03-03 17:02:51 +00:00
skip ( s , h * channelCount * 2 ) ;
2010-01-05 18:02:29 +00:00
// Read the RLE data by channel.
for ( channel = 0 ; channel < 4 ; channel + + ) {
2011-08-04 14:09:17 +00:00
guint8 * p ;
2010-01-05 18:02:29 +00:00
p = out + channel ;
if ( channel > = channelCount ) {
// Fill this channel with default data.
for ( i = 0 ; i < pixelCount ; i + + ) * p = ( channel = = 3 ? 255 : 0 ) , p + = 4 ;
} else {
// Read the RLE data.
count = 0 ;
while ( count < pixelCount ) {
2010-03-03 17:02:51 +00:00
len = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( len = = 128 ) {
// No-op.
} else if ( len < 128 ) {
// Copy next len+1 bytes literally.
len + + ;
count + = len ;
while ( len ) {
2010-03-03 17:02:51 +00:00
* p = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
p + = 4 ;
len - - ;
}
} else if ( len > 128 ) {
2011-08-04 14:09:17 +00:00
guint32 val ;
2010-01-05 18:02:29 +00:00
// Next -len+1 bytes in the dest are replicated from next source byte.
// (Interpret len as a negative 8-bit int.)
len ^ = 0x0FF ;
len + = 2 ;
2010-03-03 17:02:51 +00:00
val = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
count + = len ;
while ( len ) {
* p = val ;
p + = 4 ;
len - - ;
}
}
}
}
}
} else {
// We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
// where each channel consists of an 8-bit value for each pixel in the image.
// Read the data by channel.
for ( channel = 0 ; channel < 4 ; channel + + ) {
2011-08-04 14:09:17 +00:00
guint8 * p ;
2010-01-05 18:02:29 +00:00
p = out + channel ;
if ( channel > channelCount ) {
// Fill this channel with default data.
for ( i = 0 ; i < pixelCount ; i + + ) * p = channel = = 3 ? 255 : 0 , p + = 4 ;
} else {
// Read the data.
count = 0 ;
for ( i = 0 ; i < pixelCount ; i + + )
2010-03-03 17:02:51 +00:00
* p = get8 ( s ) , p + = 4 ;
2010-01-05 18:02:29 +00:00
}
}
}
if ( req_comp & & req_comp ! = 4 ) {
2010-03-03 17:02:51 +00:00
out = convert_format ( out , 4 , req_comp , w , h ) ;
2010-01-05 18:02:29 +00:00
if ( out = = NULL ) return out ; // convert_format frees input on failure
}
if ( comp ) * comp = channelCount ;
* y = h ;
* x = w ;
return out ;
}
# ifndef STBI_NO_STDIO
stbi_uc * stbi_psd_load ( char const * filename , int * x , int * y , int * comp , int req_comp )
{
stbi_uc * data ;
FILE * f = fopen ( filename , " rb " ) ;
if ( ! f ) return NULL ;
data = stbi_psd_load_from_file ( f , x , y , comp , req_comp ) ;
fclose ( f ) ;
return data ;
}
stbi_uc * stbi_psd_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_file ( & s , f ) ;
return psd_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# endif
stbi_uc * stbi_psd_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return psd_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
// *************************************************************************************************
// Radiance RGBE HDR loader
// originally by Nicolas Schulz
# ifndef STBI_NO_HDR
2010-03-03 17:02:51 +00:00
static int hdr_test ( stbi * s )
2010-01-05 18:02:29 +00:00
{
char * signature = " #?RADIANCE \n " ;
int i ;
for ( i = 0 ; signature [ i ] ; + + i )
2010-03-03 17:02:51 +00:00
if ( get8 ( s ) ! = signature [ i ] )
2010-01-05 18:02:29 +00:00
return 0 ;
return 1 ;
}
int stbi_hdr_test_memory ( stbi_uc const * buffer , int len )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return hdr_test ( & s ) ;
2010-01-05 18:02:29 +00:00
}
# ifndef STBI_NO_STDIO
int stbi_hdr_test_file ( FILE * f )
{
2010-03-03 17:02:51 +00:00
stbi s ;
2010-01-05 18:02:29 +00:00
int r , n = ftell ( f ) ;
2010-03-03 17:02:51 +00:00
start_file ( & s , f ) ;
r = hdr_test ( & s ) ;
2010-01-05 18:02:29 +00:00
fseek ( f , n , SEEK_SET ) ;
return r ;
}
# endif
# define HDR_BUFLEN 1024
2010-03-03 17:02:51 +00:00
static char * hdr_gettoken ( stbi * z , char * buffer )
2010-01-05 18:02:29 +00:00
{
int len = 0 ;
char * s = buffer , c = ' \0 ' ;
2010-03-03 17:02:51 +00:00
c = get8 ( z ) ;
2010-01-05 18:02:29 +00:00
2010-03-03 17:02:51 +00:00
while ( ! at_eof ( z ) & & c ! = ' \n ' ) {
2010-01-05 18:02:29 +00:00
buffer [ len + + ] = c ;
if ( len = = HDR_BUFLEN - 1 ) {
// flush to end of line
2010-03-03 17:02:51 +00:00
while ( ! at_eof ( z ) & & get8 ( z ) ! = ' \n ' )
2010-01-05 18:02:29 +00:00
;
break ;
}
2010-03-03 17:02:51 +00:00
c = get8 ( z ) ;
2010-01-05 18:02:29 +00:00
}
buffer [ len ] = 0 ;
return buffer ;
}
static void hdr_convert ( float * output , stbi_uc * input , int req_comp )
{
if ( input [ 3 ] ! = 0 ) {
float f1 ;
// Exponent
f1 = ( float ) ldexp ( 1.0f , input [ 3 ] - ( int ) ( 128 + 8 ) ) ;
if ( req_comp < = 2 )
output [ 0 ] = ( input [ 0 ] + input [ 1 ] + input [ 2 ] ) * f1 / 3 ;
else {
output [ 0 ] = input [ 0 ] * f1 ;
output [ 1 ] = input [ 1 ] * f1 ;
output [ 2 ] = input [ 2 ] * f1 ;
}
if ( req_comp = = 2 ) output [ 1 ] = 1 ;
if ( req_comp = = 4 ) output [ 3 ] = 1 ;
} else {
switch ( req_comp ) {
case 4 : output [ 3 ] = 1 ; /* fallthrough */
case 3 : output [ 0 ] = output [ 1 ] = output [ 2 ] = 0 ;
break ;
case 2 : output [ 1 ] = 1 ; /* fallthrough */
case 1 : output [ 0 ] = 0 ;
break ;
}
}
}
2010-03-03 17:02:51 +00:00
static float * hdr_load ( stbi * s , int * x , int * y , int * comp , int req_comp )
2010-01-05 18:02:29 +00:00
{
char buffer [ HDR_BUFLEN ] ;
char * token ;
int valid = 0 ;
int width , height ;
stbi_uc * scanline ;
float * hdr_data ;
int len ;
unsigned char count , value ;
int i , j , k , c1 , c2 , z ;
// Check identifier
2010-03-03 17:02:51 +00:00
if ( strcmp ( hdr_gettoken ( s , buffer ) , " #?RADIANCE " ) ! = 0 )
2010-01-05 18:02:29 +00:00
return epf ( " not HDR " , " Corrupt HDR image " ) ;
// Parse header
while ( 1 ) {
2010-03-03 17:02:51 +00:00
token = hdr_gettoken ( s , buffer ) ;
2010-01-05 18:02:29 +00:00
if ( token [ 0 ] = = 0 ) break ;
if ( strcmp ( token , " FORMAT=32-bit_rle_rgbe " ) = = 0 ) valid = 1 ;
}
if ( ! valid ) return epf ( " unsupported format " , " Unsupported HDR format " ) ;
// Parse width and height
// can't use sscanf() if we're not using stdio!
2010-03-03 17:02:51 +00:00
token = hdr_gettoken ( s , buffer ) ;
2010-01-05 18:02:29 +00:00
if ( strncmp ( token , " -Y " , 3 ) ) return epf ( " unsupported data layout " , " Unsupported HDR format " ) ;
token + = 3 ;
height = strtol ( token , & token , 10 ) ;
while ( * token = = ' ' ) + + token ;
if ( strncmp ( token , " +X " , 3 ) ) return epf ( " unsupported data layout " , " Unsupported HDR format " ) ;
token + = 3 ;
width = strtol ( token , NULL , 10 ) ;
* x = width ;
* y = height ;
* comp = 3 ;
if ( req_comp = = 0 ) req_comp = 3 ;
// Read data
hdr_data = ( float * ) malloc ( height * width * req_comp * sizeof ( float ) ) ;
// Load image data
// image data is stored as some number of sca
if ( width < 8 | | width > = 32768 ) {
// Read flat data
for ( j = 0 ; j < height ; + + j ) {
for ( i = 0 ; i < width ; + + i ) {
stbi_uc rgbe [ 4 ] ;
main_decode_loop :
2010-03-03 17:02:51 +00:00
getn ( s , rgbe , 4 ) ;
2010-01-05 18:02:29 +00:00
hdr_convert ( hdr_data + j * width * req_comp + i * req_comp , rgbe , req_comp ) ;
}
}
} else {
// Read RLE-encoded data
scanline = NULL ;
for ( j = 0 ; j < height ; + + j ) {
2010-03-03 17:02:51 +00:00
c1 = get8 ( s ) ;
c2 = get8 ( s ) ;
len = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( c1 ! = 2 | | c2 ! = 2 | | ( len & 0x80 ) ) {
// not run-length encoded, so we have to actually use THIS data as a decoded
// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
2010-03-03 17:02:51 +00:00
stbi_uc rgbe [ 4 ] = { c1 , c2 , len , get8 ( s ) } ;
2010-01-05 18:02:29 +00:00
hdr_convert ( hdr_data , rgbe , req_comp ) ;
i = 1 ;
j = 0 ;
free ( scanline ) ;
goto main_decode_loop ; // yes, this is fucking insane; blame the fucking insane format
}
len < < = 8 ;
2010-03-03 17:02:51 +00:00
len | = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( len ! = width ) { free ( hdr_data ) ; free ( scanline ) ; return epf ( " invalid decoded scanline length " , " corrupt HDR " ) ; }
if ( scanline = = NULL ) scanline = ( stbi_uc * ) malloc ( width * 4 ) ;
for ( k = 0 ; k < 4 ; + + k ) {
i = 0 ;
while ( i < width ) {
2010-03-03 17:02:51 +00:00
count = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
if ( count > 128 ) {
// Run
2010-03-03 17:02:51 +00:00
value = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
count - = 128 ;
for ( z = 0 ; z < count ; + + z )
scanline [ i + + * 4 + k ] = value ;
} else {
// Dump
for ( z = 0 ; z < count ; + + z )
2010-03-03 17:02:51 +00:00
scanline [ i + + * 4 + k ] = get8 ( s ) ;
2010-01-05 18:02:29 +00:00
}
}
}
for ( i = 0 ; i < width ; + + i )
hdr_convert ( hdr_data + ( j * width + i ) * req_comp , scanline + i * 4 , req_comp ) ;
}
free ( scanline ) ;
}
return hdr_data ;
}
# ifndef STBI_NO_STDIO
float * stbi_hdr_load_from_file ( FILE * f , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_file ( & s , f ) ;
return hdr_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# endif
float * stbi_hdr_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * comp , int req_comp )
{
2010-03-03 17:02:51 +00:00
stbi s ;
start_mem ( & s , buffer , len ) ;
return hdr_load ( & s , x , y , comp , req_comp ) ;
2010-01-05 18:02:29 +00:00
}
# endif // STBI_NO_HDR
/////////////////////// write image ///////////////////////
# ifndef STBI_NO_WRITE
2011-08-04 14:09:17 +00:00
static void write8 ( FILE * f , int x ) { guint8 z = ( guint8 ) x ; fwrite ( & z , 1 , 1 , f ) ; }
2010-01-05 18:02:29 +00:00
static void writefv ( FILE * f , char * fmt , va_list v )
{
while ( * fmt ) {
switch ( * fmt + + ) {
case ' ' : break ;
2011-08-04 14:09:17 +00:00
case ' 1 ' : { guint8 x = va_arg ( v , int ) ; write8 ( f , x ) ; break ; }
case ' 2 ' : { gint16 x = va_arg ( v , int ) ; write8 ( f , x ) ; write8 ( f , x > > 8 ) ; break ; }
case ' 4 ' : { gint32 x = va_arg ( v , int ) ; write8 ( f , x ) ; write8 ( f , x > > 8 ) ; write8 ( f , x > > 16 ) ; write8 ( f , x > > 24 ) ; break ; }
2010-01-05 18:02:29 +00:00
default :
assert ( 0 ) ;
va_end ( v ) ;
return ;
}
}
}
static void writef ( FILE * f , char * fmt , . . . )
{
va_list v ;
va_start ( v , fmt ) ;
writefv ( f , fmt , v ) ;
va_end ( v ) ;
}
static void write_pixels ( FILE * f , int rgb_dir , int vdir , int x , int y , int comp , void * data , int write_alpha , int scanline_pad )
{
2011-08-04 14:09:17 +00:00
guint8 bg [ 3 ] = { 255 , 0 , 255 } , px [ 3 ] ;
guint32 zero = 0 ;
2010-01-05 18:02:29 +00:00
int i , j , k , j_end ;
if ( vdir < 0 )
j_end = - 1 , j = y - 1 ;
else
j_end = y , j = 0 ;
for ( ; j ! = j_end ; j + = vdir ) {
for ( i = 0 ; i < x ; + + i ) {
2011-08-04 14:09:17 +00:00
guint8 * d = ( guint8 * ) data + ( j * x + i ) * comp ;
2010-01-05 18:02:29 +00:00
if ( write_alpha < 0 )
fwrite ( & d [ comp - 1 ] , 1 , 1 , f ) ;
switch ( comp ) {
case 1 :
case 2 : writef ( f , " 111 " , d [ 0 ] , d [ 0 ] , d [ 0 ] ) ;
break ;
case 4 :
if ( ! write_alpha ) {
for ( k = 0 ; k < 3 ; + + k )
px [ k ] = bg [ k ] + ( ( d [ k ] - bg [ k ] ) * d [ 3 ] ) / 255 ;
writef ( f , " 111 " , px [ 1 - rgb_dir ] , px [ 1 ] , px [ 1 + rgb_dir ] ) ;
break ;
}
/* FALLTHROUGH */
case 3 :
writef ( f , " 111 " , d [ 1 - rgb_dir ] , d [ 1 ] , d [ 1 + rgb_dir ] ) ;
break ;
}
if ( write_alpha > 0 )
fwrite ( & d [ comp - 1 ] , 1 , 1 , f ) ;
}
fwrite ( & zero , scanline_pad , 1 , f ) ;
}
}
static int outfile ( char const * filename , int rgb_dir , int vdir , int x , int y , int comp , void * data , int alpha , int pad , char * fmt , . . . )
{
FILE * f = fopen ( filename , " wb " ) ;
if ( f ) {
va_list v ;
va_start ( v , fmt ) ;
writefv ( f , fmt , v ) ;
va_end ( v ) ;
write_pixels ( f , rgb_dir , vdir , x , y , comp , data , alpha , pad ) ;
fclose ( f ) ;
}
return f ! = NULL ;
}
int stbi_write_bmp ( char const * filename , int x , int y , int comp , void * data )
{
int pad = ( - x * 3 ) & 3 ;
return outfile ( filename , - 1 , - 1 , x , y , comp , data , 0 , pad ,
" 11 4 22 4 " " 4 44 22 444444 " ,
' B ' , ' M ' , 14 + 40 + ( x * 3 + pad ) * y , 0 , 0 , 14 + 40 , // file header
40 , x , y , 1 , 24 , 0 , 0 , 0 , 0 , 0 , 0 ) ; // bitmap header
}
int stbi_write_tga ( char const * filename , int x , int y , int comp , void * data )
{
int has_alpha = ! ( comp & 1 ) ;
return outfile ( filename , - 1 , - 1 , x , y , comp , data , has_alpha , 0 ,
" 111 221 2222 11 " , 0 , 0 , 2 , 0 , 0 , 0 , 0 , 0 , x , y , 24 + 8 * has_alpha , 8 * has_alpha ) ;
}
// any other image formats that do interleaved rgb data?
// PNG: requires adler32,crc32 -- significant amount of code
// PSD: no, channels output separately
// TIFF: no, stripwise-interleaved... i think
# endif // STBI_NO_WRITE
2010-03-03 17:02:51 +00:00
# endif // STBI_HEADER_FILE_ONLY