@ -353,6 +353,10 @@ typedef struct
STBIDEF stbi_uc * stbi_load_from_memory ( stbi_uc const * buffer , int len , int * x , int * y , int * channels_in_file , int desired_channels ) ;
STBIDEF stbi_uc * stbi_load_from_callbacks ( stbi_io_callbacks const * clbk , void * user , int * x , int * y , int * channels_in_file , int desired_channels ) ;
# ifndef STBI_NO_GIF
STBIDEF stbi_uc * stbi_load_gif_from_memory ( stbi_uc const * buffer , int len , int * * delays , int * x , int * y , int * z , int * comp , int req_comp ) ;
# endif
# ifndef STBI_NO_STDIO
STBIDEF stbi_uc * stbi_load ( char const * filename , int * x , int * y , int * channels_in_file , int desired_channels ) ;
@ -819,6 +823,7 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
# ifndef STBI_NO_GIF
static int stbi__gif_test ( stbi__context * s ) ;
static void * stbi__gif_load ( stbi__context * s , int * x , int * y , int * comp , int req_comp , stbi__result_info * ri ) ;
static void * stbi__load_gif_main ( stbi__context * s , int * * delays , int * x , int * y , int * z , int * comp , int req_comp ) ;
static int stbi__gif_info ( stbi__context * s , int * x , int * y , int * comp ) ;
# endif
@ -1054,6 +1059,18 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
}
}
static void stbi__vertical_flip_slices ( void * image , int w , int h , int z , int bytes_per_pixel )
{
int slice ;
int slice_size = w * h * bytes_per_pixel ;
stbi_uc * bytes = ( stbi_uc * ) image ;
for ( slice = 0 ; slice < z ; + + slice ) {
stbi__vertical_flip ( bytes , w , h , bytes_per_pixel ) ;
bytes + = slice_size ;
}
}
static unsigned char * stbi__load_and_postprocess_8bit ( stbi__context * s , int * x , int * y , int * comp , int req_comp )
{
stbi__result_info ri ;
@ -1205,6 +1222,21 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u
return stbi__load_and_postprocess_8bit ( & s , x , y , comp , req_comp ) ;
}
# ifndef STBI_NO_GIF
STBIDEF stbi_uc * stbi_load_gif_from_memory ( stbi_uc const * buffer , int len , int * * delays , int * x , int * y , int * z , int * comp , int req_comp )
{
stbi__context s ;
stbi__start_mem ( & s , buffer , len ) ;
unsigned char * result = ( unsigned char * ) stbi__load_gif_main ( & s , delays , x , y , z , comp , req_comp ) ;
if ( stbi__vertically_flip_on_load ) {
stbi__vertical_flip_slices ( result , * x , * y , * z , * comp ) ;
}
return result ;
}
# endif
# ifndef STBI_NO_LINEAR
static float * stbi__loadf_main ( stbi__context * s , int * x , int * y , int * comp , int req_comp )
{
@ -6038,11 +6070,13 @@ typedef struct
typedef struct
{
int w , h ;
stbi_uc * out , * old_out ; // output buffer (always 4 components)
int flags , bgindex , ratio , transparent , eflags , delay ;
stbi_uc * out ; // output buffer (always 4 components)
stbi_uc * background ; // The current "background" as far as a gif is concerned
stbi_uc * history ;
int flags , bgindex , ratio , transparent , eflags ;
stbi_uc pal [ 256 ] [ 4 ] ;
stbi_uc lpal [ 256 ] [ 4 ] ;
stbi__gif_lzw codes [ 4096 ] ;
stbi__gif_lzw codes [ 8192 ] ;
stbi_uc * color_table ;
int parse , step ;
int lflags ;
@ -6050,6 +6084,7 @@ typedef struct
int max_x , max_y ;
int cur_x , cur_y ;
int line_size ;
int delay ;
} stbi__gif ;
static int stbi__gif_test_raw ( stbi__context * s )
@ -6125,6 +6160,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
static void stbi__out_gif_code ( stbi__gif * g , stbi__uint16 code )
{
stbi_uc * p , * c ;
int idx ;
// recurse to decode the prefixes, since the linked-list is backwards,
// and working backwards through an interleaved image would be nasty
@ -6133,10 +6169,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
if ( g - > cur_y > = g - > max_y ) return ;
p = & g - > out [ g - > cur_x + g - > cur_y ] ;
c = & g - > color_table [ g - > codes [ code ] . suffix * 4 ] ;
idx = g - > cur_x + g - > cur_y ;
p = & g - > out [ idx ] ;
g - > history [ idx / 4 ] = 1 ;
if ( c [ 3 ] > = 128 ) {
c = & g - > color_table [ g - > codes [ code ] . suffix * 4 ] ;
if ( c [ 3 ] > 128 ) { // don't render transparent pixels;
p [ 0 ] = c [ 2 ] ;
p [ 1 ] = c [ 1 ] ;
p [ 2 ] = c [ 0 ] ;
@ -6210,11 +6248,16 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
stbi__skip ( s , len ) ;
return g - > out ;
} else if ( code < = avail ) {
if ( first ) return stbi__errpuc ( " no clear code " , " Corrupt GIF " ) ;
if ( first ) {
return stbi__errpuc ( " no clear code " , " Corrupt GIF " ) ;
}
if ( oldcode > = 0 ) {
p = & g - > codes [ avail + + ] ;
if ( avail > 4096 ) return stbi__errpuc ( " too many codes " , " Corrupt GIF " ) ;
if ( avail > 8192 ) {
return stbi__errpuc ( " too many codes " , " Corrupt GIF " ) ;
}
p - > prefix = ( stbi__int16 ) oldcode ;
p - > first = g - > codes [ oldcode ] . first ;
p - > suffix = ( code = = avail ) ? p - > first : g - > codes [ code ] . first ;
@ -6236,62 +6279,72 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
}
}
static void stbi__fill_gif_background ( stbi__gif * g , int x0 , int y0 , int x1 , int y1 )
{
int x , y ;
stbi_uc * c = g - > pal [ g - > bgindex ] ;
for ( y = y0 ; y < y1 ; y + = 4 * g - > w ) {
for ( x = x0 ; x < x1 ; x + = 4 ) {
stbi_uc * p = & g - > out [ y + x ] ;
p [ 0 ] = c [ 2 ] ;
p [ 1 ] = c [ 1 ] ;
p [ 2 ] = c [ 0 ] ;
p [ 3 ] = 0 ;
}
}
}
// this function is designed to support animated gifs, although stb_image doesn't support it
static stbi_uc * stbi__gif_load_next ( stbi__context * s , stbi__gif * g , int * comp , int req_comp )
{
int i ;
stbi_uc * prev_out = 0 ;
if ( g - > out = = 0 & & ! stbi__gif_header ( s , g , comp , 0 ) )
return 0 ; // stbi__g_failure_reason set by stbi__gif_header
if ( ! stbi__mad3sizes_valid ( g - > w , g - > h , 4 , 0 ) )
return stbi__errpuc ( " too large " , " GIF too large " ) ;
// two back is the image from two frames ago, used for a very specific disposal format
static stbi_uc * stbi__gif_load_next ( stbi__context * s , stbi__gif * g , int * comp , int req_comp , stbi_uc * two_back )
{
int dispose ;
int first_frame ;
int pi ;
int pcount ;
// on first frame, any non-written pixels get the background colour (non-transparent)
first_frame = 0 ;
if ( g - > out = = 0 ) {
if ( ! stbi__gif_header ( s , g , comp , 0 ) ) return 0 ; // stbi__g_failure_reason set by stbi__gif_header
g - > out = ( stbi_uc * ) stbi__malloc ( 4 * g - > w * g - > h ) ;
g - > background = ( stbi_uc * ) stbi__malloc ( 4 * g - > w * g - > h ) ;
g - > history = ( stbi_uc * ) stbi__malloc ( g - > w * g - > h ) ;
if ( g - > out = = 0 ) return stbi__errpuc ( " outofmem " , " Out of memory " ) ;
// image is treated as "tranparent" at the start - ie, nothing overwrites the current background;
// background colour is only used for pixels that are not rendered first frame, after that "background"
// color refers to teh color that was there the previous frame.
memset ( g - > out , 0x00 , 4 * g - > w * g - > h ) ;
memset ( g - > background , 0x00 , 4 * g - > w * g - > h ) ; // state of the background (starts transparent)
memset ( g - > history , 0x00 , g - > w * g - > h ) ; // pixels that were affected previous frame
first_frame = 1 ;
} else {
// second frame - how do we dispoase of the previous one?
dispose = ( g - > eflags & 0x1C ) > > 2 ;
pcount = g - > w * g - > h ;
prev_out = g - > out ;
g - > out = ( stbi_uc * ) stbi__malloc_mad3 ( 4 , g - > w , g - > h , 0 ) ;
if ( g - > out = = 0 ) return stbi__errpuc ( " outofmem " , " Out of memory " ) ;
if ( ( dispose = = 4 ) & & ( two_back = = 0 ) ) {
dispose = 2 ; // if I don't have an image to revert back to, default to the old background
}
switch ( ( g - > eflags & 0x1C ) > > 2 ) {
case 0 : // unspecified (also always used on 1st frame)
stbi__fill_gif_background ( g , 0 , 0 , 4 * g - > w , 4 * g - > w * g - > h ) ;
break ;
case 1 : // do not dispose
if ( prev_out ) memcpy ( g - > out , prev_out , 4 * g - > w * g - > h ) ;
g - > old_out = prev_out ;
break ;
case 2 : // dispose to background
if ( prev_out ) memcpy ( g - > out , prev_out , 4 * g - > w * g - > h ) ;
stbi__fill_gif_background ( g , g - > start_x , g - > start_y , g - > max_x , g - > max_y ) ;
break ;
case 3 : // dispose to previous
if ( g - > old_out ) {
for ( i = g - > start_y ; i < g - > max_y ; i + = 4 * g - > w )
memcpy ( & g - > out [ i + g - > start_x ] , & g - > old_out [ i + g - > start_x ] , g - > max_x - g - > start_x ) ;
if ( dispose = = 4 ) { // use previous graphic
for ( pi = 0 ; pi < pcount ; + + pi ) {
if ( g - > history [ pi ] ) {
memcpy ( & g - > out [ pi * 4 ] , & two_back [ pi * 4 ] , 4 ) ;
}
}
break ;
} else if ( dispose = = 2 ) {
// restore what was changed last frame to background before that frame;
for ( pi = 0 ; pi < pcount ; + + pi ) {
if ( g - > history [ pi ] ) {
memcpy ( & g - > out [ pi * 4 ] , & g - > background [ pi * 4 ] , 4 ) ;
}
}
} else {
// This is a non-disposal case eithe way, so just
// leave the pixels as is, and they will become the new background
// 1: do not dispose? Same as 4?
// 0: not specified.
}
// background is what out is after the undoing of the previou frame;
memcpy ( g - > background , g - > out , 4 * g - > w * g - > h ) ;
}
// clear my history;
memset ( g - > history , 0x00 , g - > w * g - > h ) ; // pixels that were affected previous frame
for ( ; ; ) {
switch ( stbi__get8 ( s ) ) {
int tag = stbi__get8 ( s ) ;
switch ( tag ) {
case 0x2C : /* Image Descriptor */
{
int prev_trans = - 1 ;
stbi__int32 x , y , w , h ;
stbi_uc * o ;
@ -6324,19 +6377,24 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
stbi__gif_parse_colortable ( s , g - > lpal , 2 < < ( g - > lflags & 7 ) , g - > eflags & 0x01 ? g - > transparent : - 1 ) ;
g - > color_table = ( stbi_uc * ) g - > lpal ;
} else if ( g - > flags & 0x80 ) {
if ( g - > transparent > = 0 & & ( g - > eflags & 0x01 ) ) {
prev_trans = g - > pal [ g - > transparent ] [ 3 ] ;
g - > pal [ g - > transparent ] [ 3 ] = 0 ;
}
g - > color_table = ( stbi_uc * ) g - > pal ;
} else
return stbi__errpuc ( " missing color table " , " Corrupt GIF " ) ;
return stbi__errpuc ( " missing color table " , " Corrupt GIF " ) ;
o = stbi__process_gif_raster ( s , g ) ;
if ( o = = NULL ) return NULL ;
if ( prev_trans ! = - 1 )
g - > pal [ g - > transparent ] [ 3 ] = ( stbi_uc ) prev_trans ;
// if this was the first frame,
pcount = g - > w * g - > h ;
if ( first_frame & & ( g - > bgindex > = 0 ) ) {
// if first frame, any pixel not drawn to gets the background color
for ( pi = 0 ; pi < pcount ; + + pi ) {
if ( g - > history [ pi ] = = 0 ) {
g - > pal [ g - > bgindex ] [ 3 ] = 255 ; // just in case it was made transparent, undo that; It will be reset next frame if need be;
memcpy ( & g - > out [ pi * 4 ] , & g - > pal [ g - > bgindex ] , 4 ) ;
}
}
}
return o ;
}
@ -6344,19 +6402,35 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
case 0x21 : // Comment Extension.
{
int len ;
if ( stbi__get8 ( s ) = = 0xF9 ) { // Graphic Control Extension.
int ext = stbi__get8 ( s ) ;
if ( ext = = 0xF9 ) { // Graphic Control Extension.
len = stbi__get8 ( s ) ;
if ( len = = 4 ) {
g - > eflags = stbi__get8 ( s ) ;
g - > delay = stbi__get16le ( s ) ;
g - > transparent = stbi__get8 ( s ) ;
g - > delay = 10 * stbi__get16le ( s ) ; // delay - 1/100th of a second, saving as 1/1000ths.
// unset old transparent
if ( g - > transparent > = 0 ) {
g - > pal [ g - > transparent ] [ 3 ] = 255 ;
}
if ( g - > eflags & 0x01 ) {
g - > transparent = stbi__get8 ( s ) ;
if ( g - > transparent > = 0 ) {
g - > pal [ g - > transparent ] [ 3 ] = 0 ;
}
} else {
// don't need transparent
stbi__skip ( s , 1 ) ;
g - > transparent = - 1 ;
}
} else {
stbi__skip ( s , len ) ;
break ;
}
}
while ( ( len = stbi__get8 ( s ) ) ! = 0 )
}
while ( ( len = stbi__get8 ( s ) ) ! = 0 ) {
stbi__skip ( s , len ) ;
}
break ;
}
@ -6367,28 +6441,92 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
return stbi__errpuc ( " unknown code " , " Corrupt GIF " ) ;
}
}
}
static void * stbi__load_gif_main ( stbi__context * s , int * * delays , int * x , int * y , int * z , int * comp , int req_comp )
{
if ( stbi__gif_test ( s ) ) {
int layers = 0 ;
stbi_uc * u = 0 ;
stbi_uc * out = 0 ;
stbi_uc * two_back = 0 ;
stbi__gif g ;
int stride ;
memset ( & g , 0 , sizeof ( g ) ) ;
if ( delays ) {
* delays = 0 ;
}
STBI_NOTUSED ( req_comp ) ;
do {
u = stbi__gif_load_next ( s , & g , comp , req_comp , two_back ) ;
if ( u = = ( stbi_uc * ) s ) u = 0 ; // end of animated gif marker
if ( u ) {
* x = g . w ;
* y = g . h ;
+ + layers ;
stride = g . w * g . h * 4 ;
if ( out ) {
out = ( stbi_uc * ) STBI_REALLOC ( out , layers * stride ) ;
if ( delays ) {
* delays = ( int * ) STBI_REALLOC ( * delays , sizeof ( int ) * layers ) ;
}
} else {
out = ( stbi_uc * ) stbi__malloc ( layers * stride ) ;
if ( delays ) {
* delays = ( int * ) stbi__malloc ( layers * sizeof ( int ) ) ;
}
}
memcpy ( out + ( ( layers - 1 ) * stride ) , u , stride ) ;
if ( layers > = 2 ) {
two_back = out - 2 * stride ;
}
if ( delays ) {
( * delays ) [ layers - 1U ] = g . delay ;
}
}
} while ( u ! = nullptr ) ;
// free temp buffer;
STBI_FREE ( g . out ) ;
STBI_FREE ( g . history ) ;
STBI_FREE ( g . background ) ;
// do the final conversion after loading everything;
if ( req_comp & & req_comp ! = 4 )
out = stbi__convert_format ( out , 4 , req_comp , layers * g . w , g . h ) ;
* z = layers ;
return out ;
} else {
return stbi__errpuc ( " not GIF " , " Image was not as a gif type. " ) ;
}
}
static void * stbi__gif_load ( stbi__context * s , int * x , int * y , int * comp , int req_comp , stbi__result_info * ri )
{
stbi_uc * u = 0 ;
stbi__gif * g = ( stbi__gif * ) stbi__malloc ( sizeof ( stbi__gif ) ) ;
memset ( g , 0 , sizeof ( * g ) ) ;
STBI_NOTUSED ( ri ) ;
stbi__gif g ;
memset ( & g , 0 , sizeof ( g ) ) ;
u = stbi__gif_load_next ( s , g , comp , req_comp ) ;
u = stbi__gif_load_next ( s , & g , comp , req_comp , 0 ) ;
if ( u = = ( stbi_uc * ) s ) u = 0 ; // end of animated gif marker
if ( u ) {
* x = g - > w ;
* y = g - > h ;
* x = g . w ;
* y = g . h ;
// moved conversion to after successful load so that the same
// can be done for multiple frames.
if ( req_comp & & req_comp ! = 4 )
u = stbi__convert_format ( u , 4 , req_comp , g - > w , g - > h ) ;
u = stbi__convert_format ( u , 4 , req_comp , g . w , g . h ) ;
}
else if ( g - > out )
STBI_FREE ( g - > out ) ;
STBI_FREE ( g ) ;
// free buffers needed for multiple frame loading;
STBI_FREE ( g . history ) ;
STBI_FREE ( g . background ) ;
return u ;
}