@ -1,5 +1,5 @@
/* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG / BMP / TGA images to C stdio - Sean Barrett 2010 - 2015
/* stb_image_write - v1.08 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG / BMP / TGA / JPEG / HDR images to C stdio - Sean Barrett 2010 - 2015
no warranty implied ; use at your own risk
Before # including ,
@ -10,43 +10,64 @@
Will probably not work correctly with strict - aliasing optimizations .
If using a modern Microsoft Compiler , non - safe versions of CRT calls may cause
compilation warnings or even errors . To avoid this , also before # including ,
# define STBI_MSC_SECURE_CRT
ABOUT :
This header file is a library for writing images to C stdio . It could be
adapted to write to memory or a general streaming interface ; let me know .
The PNG output is not optimal ; it is 20 - 50 % larger than the file
written by a decent optimizing implementation . This library is designed
for source code compactness and simplicity , not optimal image file size
or run - time performance .
written by a decent optimizing implementation ; though providing a custom
zlib compress function ( see STBIW_ZLIB_COMPRESS ) can mitigate that .
This library is designed for source code compactness and simplicity ,
not optimal image file size or run - time performance .
BUILDING :
You can # define STBIW_ASSERT ( x ) before the # include to avoid using assert . h .
You can # define STBIW_MALLOC ( ) , STBIW_REALLOC ( ) , and STBIW_FREE ( ) to replace
malloc , realloc , free .
You can define STBIW_MEMMOVE ( ) to replace memmove ( )
You can # define STBIW_MEMMOVE ( ) to replace memmove ( )
You can # define STBIW_ZLIB_COMPRESS to use a custom zlib - style compress function
for PNG compression ( instead of the builtin one ) , it must have the following signature :
unsigned char * my_compress ( unsigned char * data , int data_len , int * out_len , int quality ) ;
The returned data will be freed with STBIW_FREE ( ) ( free ( ) by default ) ,
so it must be heap allocated with STBIW_MALLOC ( ) ( malloc ( ) by default ) ,
USAGE :
There are four functions , one for each image file format :
There are five functions , one for each image file format :
int stbi_write_png ( char const * filename , int w , int h , int comp , const void * data , int stride_in_bytes ) ;
int stbi_write_bmp ( char const * filename , int w , int h , int comp , const void * data ) ;
int stbi_write_tga ( char const * filename , int w , int h , int comp , const void * data ) ;
int stbi_write_hdr ( char const * filename , int w , int h , int comp , const float * data ) ;
int stbi_write_jpg ( char const * filename , int w , int h , int comp , const float * data , int quality ) ;
void stbi_flip_vertically_on_write ( int flag ) ; // flag is non-zero to flip data vertically
There are also four equivalent functions that use an arbitrary write function . You are
There are also five equivalent functions that use an arbitrary write function . You are
expected to open / close your file - equivalent before and after calling these :
int stbi_write_png_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const void * data , int stride_in_bytes ) ;
int stbi_write_bmp_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const void * data ) ;
int stbi_write_tga_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const void * data ) ;
int stbi_write_hdr_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const float * data ) ;
int stbi_write_jpg_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const void * data , int quality ) ;
where the callback is :
void stbi_write_func ( void * context , void * data , int size ) ;
You can configure it with these global variables :
int stbi_write_tga_with_rle ; // defaults to true; set to 0 to disable RLE
int stbi_write_png_compression_level ; // defaults to 8; set to higher for more compression
int stbi_write_force_png_filter ; // defaults to -1; set to 0..5 to force a filter mode
You can define STBI_WRITE_NO_STDIO to disable the file variant of these
functions , so the library will not use stdio . h at all . However , this will
also disable HDR writing , because it requires stdio for formatted output .
@ -73,27 +94,33 @@ USAGE:
writer , both because it is in BGR order and because it may have padding
at the end of the line . )
PNG allows you to set the deflate compression level by setting the global
variable ' stbi_write_png_level ' ( it defaults to 8 ) .
HDR expects linear float data . Since the format is always 32 - bit rgb ( e )
data , alpha ( if provided ) is discarded , and for monochrome data it is
replicated across all three channels .
TGA supports RLE or non - RLE compressed data . To use non - RLE - compressed
data , set the global variable ' stbi_write_tga_with_rle ' to 0.
JPEG does ignore alpha channels in input data ; quality is between 1 and 100.
Higher quality looks better but results in a bigger image .
JPEG baseline ( no JPEG progressive ) .
CREDITS :
PNG / BMP / TGA
Sean Barrett
HDR
Baldur Karlsson
TGA monochrome :
Jean - Sebastien Guay
misc enhancements :
Tim Kelsey
TGA RLE
Alan Hickman
initial file IO callback implementation
Emmanuel Julien
Sean Barrett - PNG / BMP / TGA
Baldur Karlsson - HDR
Jean - Sebastien Guay - TGA monochrome
Tim Kelsey - misc enhancements
Alan Hickman - TGA RLE
Emmanuel Julien - initial file IO callback implementation
Jon Olick - original jo_jpeg . cpp code
Daniel Gibson - integrate JPEG , allow external zlib
Aarni Koskela - allow choosing PNG filter
bugfixes :
github : Chribba
Guillaume Chereau
@ -105,6 +132,7 @@ CREDITS:
Thatcher Ulrich
github : poppolopoppo
Patrick Boettcher
github : xeekworx
LICENSE
@ -123,14 +151,18 @@ extern "C" {
# define STBIWDEF static
# else
# define STBIWDEF extern
extern int stbi_write_tga_with_rle ;
# endif
STBIWDEF int stbi_write_tga_with_rle ;
STBIWDEF int stbi_write_png_comperssion_level ;
STBIWDEF int stbi_write_force_png_filter ;
# ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_png ( char const * filename , int w , int h , int comp , const void * data , int stride_in_bytes ) ;
STBIWDEF int stbi_write_bmp ( char const * filename , int w , int h , int comp , const void * data ) ;
STBIWDEF int stbi_write_tga ( char const * filename , int w , int h , int comp , const void * data ) ;
STBIWDEF int stbi_write_hdr ( char const * filename , int w , int h , int comp , const float * data ) ;
STBIWDEF int stbi_write_jpg ( char const * filename , int x , int y , int comp , const void * data , int quality ) ;
# endif
typedef void stbi_write_func ( void * context , void * data , int size ) ;
@ -139,6 +171,9 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w,
STBIWDEF int stbi_write_bmp_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const void * data ) ;
STBIWDEF int stbi_write_tga_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const void * data ) ;
STBIWDEF int stbi_write_hdr_to_func ( stbi_write_func * func , void * context , int w , int h , int comp , const float * data ) ;
STBIWDEF int stbi_write_jpg_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const void * data , int quality ) ;
STBIWDEF void stbi_flip_vertically_on_write ( int flip_boolean ) ;
# ifdef __cplusplus
}
@ -197,6 +232,23 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w,
# define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
# ifdef STB_IMAGE_WRITE_STATIC
static stbi__flip_vertically_on_write = 0 ;
static int stbi_write_png_compression level = 8 ;
static int stbi_write_tga_with_rle = 1 ;
static int stbi_write_force_png_filter = - 1 ;
# else
int stbi_write_png_compression_level = 8 ;
int stbi__flip_vertically_on_write = 0 ;
int stbi_write_tga_with_rle = 1 ;
int stbi_write_force_png_filter = - 1 ;
# endif
STBIWDEF void stbi_flip_vertically_on_write ( int flag )
{
stbi__flip_vertically_on_write = flag ;
}
typedef struct
{
stbi_write_func * func ;
@ -219,7 +271,12 @@ static void stbi__stdio_write(void *context, void *data, int size)
static int stbi__start_write_file ( stbi__write_context * s , const char * filename )
{
FILE * f = fopen ( filename , " wb " ) ;
FILE * f ;
# ifdef STBI_MSC_SECURE_CRT
fopen_s ( & f , filename , " wb " ) ;
# else
f = fopen ( filename , " wb " ) ;
# endif
stbi__start_write_callbacks ( s , stbi__stdio_write , ( void * ) f ) ;
return f ! = NULL ;
}
@ -234,12 +291,6 @@ static void stbi__end_write_file(stbi__write_context *s)
typedef unsigned int stbiw_uint32 ;
typedef int stb_image_write_test [ sizeof ( stbiw_uint32 ) = = 4 ? 1 : - 1 ] ;
# ifdef STB_IMAGE_WRITE_STATIC
static int stbi_write_tga_with_rle = 1 ;
# else
int stbi_write_tga_with_rle = 1 ;
# endif
static void stbiw__writefv ( stbi__write_context * s , const char * fmt , va_list v )
{
while ( * fmt ) {
@ -277,6 +328,11 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
va_end ( v ) ;
}
static void stbiw__putc ( stbi__write_context * s , unsigned char c )
{
s - > func ( s - > context , & c , 1 ) ;
}
static void stbiw__write3 ( stbi__write_context * s , unsigned char a , unsigned char b , unsigned char c )
{
unsigned char arr [ 3 ] ;
@ -325,6 +381,9 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i
if ( y < = 0 )
return ;
if ( stbi__flip_vertically_on_write )
vdir * = - 1 ;
if ( vdir < 0 )
j_end = - 1 , j = y - 1 ;
else
@ -396,11 +455,21 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
" 111 221 2222 11 " , 0 , 0 , format , 0 , 0 , 0 , 0 , 0 , x , y , ( colorbytes + has_alpha ) * 8 , has_alpha * 8 ) ;
} else {
int i , j , k ;
int jend , jdir ;
stbiw__writef ( s , " 111 221 2222 11 " , 0 , 0 , format + 8 , 0 , 0 , 0 , 0 , 0 , x , y , ( colorbytes + has_alpha ) * 8 , has_alpha * 8 ) ;
for ( j = y - 1 ; j > = 0 ; - - j ) {
unsigned char * row = ( unsigned char * ) data + j * x * comp ;
if ( stbi__flip_vertically_on_write ) {
j = 0 ;
jend = y ;
jdir = 1 ;
} else {
j = y - 1 ;
jend = - 1 ;
jdir = - 1 ;
}
for ( ; j ! = jend ; j + = jdir ) {
unsigned char * row = ( unsigned char * ) data + j * x * comp ;
int len ;
for ( i = 0 ; i < x ; i + = len ) {
@ -450,7 +519,7 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
return 1 ;
}
int stbi_write_tga_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const void * data )
STBIWDEF int stbi_write_tga_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const void * data )
{
stbi__write_context s ;
stbi__start_write_callbacks ( & s , func , context ) ;
@ -458,7 +527,7 @@ int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, i
}
# ifndef STBI_WRITE_NO_STDIO
int stbi_write_tga ( char const * filename , int x , int y , int comp , const void * data )
STBIWDEF int stbi_write_tga ( char const * filename , int x , int y , int comp , const void * data )
{
stbi__write_context s ;
if ( stbi__start_write_file ( & s , filename ) ) {
@ -610,17 +679,21 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
char header [ ] = " #?RADIANCE \n # Written by stb_image_write.h \n FORMAT=32-bit_rle_rgbe \n " ;
s - > func ( s - > context , header , sizeof ( header ) - 1 ) ;
# ifdef STBI_MSC_SECURE_CRT
len = sprintf_s ( buffer , " EXPOSURE= 1.0000000000000 \n \n -Y %d +X %d \n " , y , x ) ;
# else
len = sprintf ( buffer , " EXPOSURE= 1.0000000000000 \n \n -Y %d +X %d \n " , y , x ) ;
# endif
s - > func ( s - > context , buffer , len ) ;
for ( i = 0 ; i < y ; i + + )
stbiw__write_hdr_scanline ( s , x , comp , scratch , data + comp * i * x ) ;
stbiw__write_hdr_scanline ( s , x , comp , scratch , data + comp * x * ( stb i__flip_vertically_on_write ? y - 1 - i : i ) * x ) ;
STBIW_FREE ( scratch ) ;
return 1 ;
}
}
int stbi_write_hdr_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const float * data )
STBIWDEF int stbi_write_hdr_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const float * data )
{
stbi__write_context s ;
stbi__start_write_callbacks ( & s , func , context ) ;
@ -628,7 +701,7 @@ int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, i
}
# ifndef STBI_WRITE_NO_STDIO
int stbi_write_hdr ( char const * filename , int x , int y , int comp , const float * data )
STBIWDEF int stbi_write_hdr ( char const * filename , int x , int y , int comp , const float * data )
{
stbi__write_context s ;
if ( stbi__start_write_file ( & s , filename ) ) {
@ -646,6 +719,7 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da
// PNG writer
//
# ifndef STBIW_ZLIB_COMPRESS
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
# define stbiw__sbraw(a) ((int *) (a) - 2)
# define stbiw__sbm(a) stbiw__sbraw(a)[0]
@ -726,8 +800,14 @@ static unsigned int stbiw__zhash(unsigned char *data)
# define stbiw__ZHASH 16384
# endif // STBIW_ZLIB_COMPRESS
unsigned char * stbi_zlib_compress ( unsigned char * data , int data_len , int * out_len , int quality )
{
# ifdef STBIW_ZLIB_COMPRESS
// user provided a zlib compress implementation, use that
return STBIW_ZLIB_COMPRESS ( data , data_len , out_len , quality ) ;
# else // use builtin
static unsigned short lengthc [ ] = { 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 , 259 } ;
static unsigned char lengtheb [ ] = { 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 } ;
static unsigned short distc [ ] = { 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 , 32768 } ;
@ -829,6 +909,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
// make returned pointer freeable
STBIW_MEMMOVE ( stbiw__sbraw ( out ) , out , * out_len ) ;
return ( unsigned char * ) stbiw__sbraw ( out ) ;
# endif // STBIW_ZLIB_COMPRESS
}
static unsigned int stbiw__crc32 ( unsigned char * buffer , int len )
@ -895,61 +976,87 @@ static unsigned char stbiw__paeth(int a, int b, int c)
}
// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
static void stbiw__encode_png_line ( unsigned char * pixels , int stride_bytes , int width , int height , int y , int n , int filter_type , signed char * line_buffer )
{
static int mapping [ ] = { 0 , 1 , 2 , 3 , 4 } ;
static int firstmap [ ] = { 0 , 1 , 0 , 5 , 6 } ;
int * mymap = ( y ! = 0 ) ? mapping : firstmap ;
int i ;
int type = mymap [ filter_type ] ;
unsigned char * z = pixels + stride_bytes * ( stbi__flip_vertically_on_write ? height - 1 - y : y ) ;
for ( i = 0 ; i < n ; + + i ) {
switch ( type ) {
case 0 : line_buffer [ i ] = z [ i ] ; break ;
case 1 : line_buffer [ i ] = z [ i ] ; break ;
case 2 : line_buffer [ i ] = z [ i ] - z [ i - stride_bytes ] ; break ;
case 3 : line_buffer [ i ] = z [ i ] - ( z [ i - stride_bytes ] > > 1 ) ; break ;
case 4 : line_buffer [ i ] = ( signed char ) ( z [ i ] - stbiw__paeth ( 0 , z [ i - stride_bytes ] , 0 ) ) ; break ;
case 5 : line_buffer [ i ] = z [ i ] ; break ;
case 6 : line_buffer [ i ] = z [ i ] ; break ;
}
}
for ( i = n ; i < width * n ; + + i ) {
switch ( type ) {
case 0 : line_buffer [ i ] = z [ i ] ; break ;
case 1 : line_buffer [ i ] = z [ i ] - z [ i - n ] ; break ;
case 2 : line_buffer [ i ] = z [ i ] - z [ i - stride_bytes ] ; break ;
case 3 : line_buffer [ i ] = z [ i ] - ( ( z [ i - n ] + z [ i - stride_bytes ] ) > > 1 ) ; break ;
case 4 : line_buffer [ i ] = z [ i ] - stbiw__paeth ( z [ i - n ] , z [ i - stride_bytes ] , z [ i - stride_bytes - n ] ) ; break ;
case 5 : line_buffer [ i ] = z [ i ] - ( z [ i - n ] > > 1 ) ; break ;
case 6 : line_buffer [ i ] = z [ i ] - stbiw__paeth ( z [ i - n ] , 0 , 0 ) ; break ;
}
}
}
unsigned char * stbi_write_png_to_mem ( unsigned char * pixels , int stride_bytes , int x , int y , int n , int * out_len )
{
int force_filter = stbi_write_force_png_filter ;
int ctype [ 5 ] = { - 1 , 0 , 4 , 2 , 6 } ;
unsigned char sig [ 8 ] = { 137 , 80 , 78 , 71 , 13 , 10 , 26 , 10 } ;
unsigned char * out , * o , * filt , * zlib ;
signed char * line_buffer ;
int i , j , k , p , zlen ;
int j , zlen ;
if ( stride_bytes = = 0 )
stride_bytes = x * n ;
if ( force_filter > = 5 ) {
force_filter = - 1 ;
}
filt = ( unsigned char * ) STBIW_MALLOC ( ( x * n + 1 ) * y ) ; if ( ! filt ) return 0 ;
line_buffer = ( signed char * ) STBIW_MALLOC ( x * n ) ; if ( ! line_buffer ) { STBIW_FREE ( filt ) ; return 0 ; }
for ( j = 0 ; j < y ; + + j ) {
static int mapping [ ] = { 0 , 1 , 2 , 3 , 4 } ;
static int firstmap [ ] = { 0 , 1 , 0 , 5 , 6 } ;
int * mymap = ( j ! = 0 ) ? mapping : firstmap ;
int best = 0 , bestval = 0x7fffffff ;
for ( p = 0 ; p < 2 ; + + p ) {
for ( k = p ? best : 0 ; k < 5 ; + + k ) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass
int type = mymap [ k ] , est = 0 ;
unsigned char * z = pixels + stride_bytes * j ;
for ( i = 0 ; i < n ; + + i )
switch ( type ) {
case 0 : line_buffer [ i ] = z [ i ] ; break ;
case 1 : line_buffer [ i ] = z [ i ] ; break ;
case 2 : line_buffer [ i ] = z [ i ] - z [ i - stride_bytes ] ; break ;
case 3 : line_buffer [ i ] = z [ i ] - ( z [ i - stride_bytes ] > > 1 ) ; break ;
case 4 : line_buffer [ i ] = ( signed char ) ( z [ i ] - stbiw__paeth ( 0 , z [ i - stride_bytes ] , 0 ) ) ; break ;
case 5 : line_buffer [ i ] = z [ i ] ; break ;
case 6 : line_buffer [ i ] = z [ i ] ; break ;
}
for ( i = n ; i < x * n ; + + i ) {
switch ( type ) {
case 0 : line_buffer [ i ] = z [ i ] ; break ;
case 1 : line_buffer [ i ] = z [ i ] - z [ i - n ] ; break ;
case 2 : line_buffer [ i ] = z [ i ] - z [ i - stride_bytes ] ; break ;
case 3 : line_buffer [ i ] = z [ i ] - ( ( z [ i - n ] + z [ i - stride_bytes ] ) > > 1 ) ; break ;
case 4 : line_buffer [ i ] = z [ i ] - stbiw__paeth ( z [ i - n ] , z [ i - stride_bytes ] , z [ i - stride_bytes - n ] ) ; break ;
case 5 : line_buffer [ i ] = z [ i ] - ( z [ i - n ] > > 1 ) ; break ;
case 6 : line_buffer [ i ] = z [ i ] - stbiw__paeth ( z [ i - n ] , 0 , 0 ) ; break ;
}
}
if ( p ) break ;
for ( i = 0 ; i < x * n ; + + i )
int filter_type ;
if ( force_filter > - 1 ) {
filter_type = force_filter ;
stbiw__encode_png_line ( pixels , stride_bytes , x , y , j , n , force_filter , line_buffer ) ;
} else { // Estimate the best filter by running through all of them:
int best_filter = 0 , best_filter_val = 0x7fffffff , est , i ;
for ( filter_type = 0 ; filter_type < 5 ; filter_type + + ) {
stbiw__encode_png_line ( pixels , stride_bytes , x , y , j , n , filter_type , line_buffer ) ;
// Estimate the entropy of the line using this filter; the less, the better.
est = 0 ;
for ( i = 0 ; i < x * n ; + + i ) {
est + = abs ( ( signed char ) line_buffer [ i ] ) ;
if ( est < bestval ) { bestval = est ; best = k ; }
}
if ( est < best_filter_val ) {
best_filter_val = est ;
best_filter = filter_type ;
}
}
if ( filter_type ! = best_filter ) { // If the last iteration already got us the best filter, don't redo it
stbiw__encode_png_line ( pixels , stride_bytes , x , y , j , n , best_filter , line_buffer ) ;
filter_type = best_filter ;
}
}
// when we get here, best contains the filter type, and line_buffer contains the data
filt [ j * ( x * n + 1 ) ] = ( unsigned char ) best ;
// when we get here, filter_type contains the filter type, and line_buffer contains the data
filt [ j * ( x * n + 1 ) ] = ( unsigned char ) filter_type ;
STBIW_MEMMOVE ( filt + j * ( x * n + 1 ) + 1 , line_buffer , x * n ) ;
}
STBIW_FREE ( line_buffer ) ;
zlib = stbi_zlib_compress ( filt , y * ( x * n + 1 ) , & zlen , 8 ) ; // increase 8 to get smaller but use more memory
zlib = stbi_zlib_compress ( filt , y * ( x * n + 1 ) , & zlen , stbi_write_png_compression_level ) ;
STBIW_FREE ( filt ) ;
if ( ! zlib ) return 0 ;
@ -994,7 +1101,11 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const
int len ;
unsigned char * png = stbi_write_png_to_mem ( ( unsigned char * ) data , stride_bytes , x , y , comp , & len ) ;
if ( png = = NULL ) return 0 ;
# ifdef STBI_MSC_SECURE_CRT
fopen_s ( & f , filename , " wb " ) ;
# else
f = fopen ( filename , " wb " ) ;
# endif
if ( ! f ) { STBIW_FREE ( png ) ; return 0 ; }
fwrite ( png , 1 , len , f ) ;
fclose ( f ) ;
@ -1013,9 +1124,361 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x,
return 1 ;
}
/* ***************************************************************************
*
* JPEG writer
*
* This is based on Jon Olick ' s jo_jpeg . cpp :
* public domain Simple , Minimalistic JPEG writer - http : //www.jonolick.com/code.html
*/
static const unsigned char stbiw__jpg_ZigZag [ ] = { 0 , 1 , 5 , 6 , 14 , 15 , 27 , 28 , 2 , 4 , 7 , 13 , 16 , 26 , 29 , 42 , 3 , 8 , 12 , 17 , 25 , 30 , 41 , 43 , 9 , 11 , 18 ,
24 , 31 , 40 , 44 , 53 , 10 , 19 , 23 , 32 , 39 , 45 , 52 , 54 , 20 , 22 , 33 , 38 , 46 , 51 , 55 , 60 , 21 , 34 , 37 , 47 , 50 , 56 , 59 , 61 , 35 , 36 , 48 , 49 , 57 , 58 , 62 , 63 } ;
static void stbiw__jpg_writeBits ( stbi__write_context * s , int * bitBufP , int * bitCntP , const unsigned short * bs ) {
int bitBuf = * bitBufP , bitCnt = * bitCntP ;
bitCnt + = bs [ 1 ] ;
bitBuf | = bs [ 0 ] < < ( 24 - bitCnt ) ;
while ( bitCnt > = 8 ) {
unsigned char c = ( bitBuf > > 16 ) & 255 ;
stbiw__putc ( s , c ) ;
if ( c = = 255 ) {
stbiw__putc ( s , 0 ) ;
}
bitBuf < < = 8 ;
bitCnt - = 8 ;
}
* bitBufP = bitBuf ;
* bitCntP = bitCnt ;
}
static void stbiw__jpg_DCT ( float * d0p , float * d1p , float * d2p , float * d3p , float * d4p , float * d5p , float * d6p , float * d7p ) {
float d0 = * d0p , d1 = * d1p , d2 = * d2p , d3 = * d3p , d4 = * d4p , d5 = * d5p , d6 = * d6p , d7 = * d7p ;
float z1 , z2 , z3 , z4 , z5 , z11 , z13 ;
float tmp0 = d0 + d7 ;
float tmp7 = d0 - d7 ;
float tmp1 = d1 + d6 ;
float tmp6 = d1 - d6 ;
float tmp2 = d2 + d5 ;
float tmp5 = d2 - d5 ;
float tmp3 = d3 + d4 ;
float tmp4 = d3 - d4 ;
// Even part
float tmp10 = tmp0 + tmp3 ; // phase 2
float tmp13 = tmp0 - tmp3 ;
float tmp11 = tmp1 + tmp2 ;
float tmp12 = tmp1 - tmp2 ;
d0 = tmp10 + tmp11 ; // phase 3
d4 = tmp10 - tmp11 ;
z1 = ( tmp12 + tmp13 ) * 0.707106781f ; // c4
d2 = tmp13 + z1 ; // phase 5
d6 = tmp13 - z1 ;
// Odd part
tmp10 = tmp4 + tmp5 ; // phase 2
tmp11 = tmp5 + tmp6 ;
tmp12 = tmp6 + tmp7 ;
// The rotator is modified from fig 4-8 to avoid extra negations.
z5 = ( tmp10 - tmp12 ) * 0.382683433f ; // c6
z2 = tmp10 * 0.541196100f + z5 ; // c2-c6
z4 = tmp12 * 1.306562965f + z5 ; // c2+c6
z3 = tmp11 * 0.707106781f ; // c4
z11 = tmp7 + z3 ; // phase 5
z13 = tmp7 - z3 ;
* d5p = z13 + z2 ; // phase 6
* d3p = z13 - z2 ;
* d1p = z11 + z4 ;
* d7p = z11 - z4 ;
* d0p = d0 ; * d2p = d2 ; * d4p = d4 ; * d6p = d6 ;
}
static void stbiw__jpg_calcBits ( int val , unsigned short bits [ 2 ] ) {
int tmp1 = val < 0 ? - val : val ;
val = val < 0 ? val - 1 : val ;
bits [ 1 ] = 1 ;
while ( tmp1 > > = 1 ) {
+ + bits [ 1 ] ;
}
bits [ 0 ] = val & ( ( 1 < < bits [ 1 ] ) - 1 ) ;
}
static int stbiw__jpg_processDU ( stbi__write_context * s , int * bitBuf , int * bitCnt , float * CDU , float * fdtbl , int DC , const unsigned short HTDC [ 256 ] [ 2 ] , const unsigned short HTAC [ 256 ] [ 2 ] ) {
const unsigned short EOB [ 2 ] = { HTAC [ 0x00 ] [ 0 ] , HTAC [ 0x00 ] [ 1 ] } ;
const unsigned short M16zeroes [ 2 ] = { HTAC [ 0xF0 ] [ 0 ] , HTAC [ 0xF0 ] [ 1 ] } ;
int dataOff , i , diff , end0pos ;
int DU [ 64 ] ;
// DCT rows
for ( dataOff = 0 ; dataOff < 64 ; dataOff + = 8 ) {
stbiw__jpg_DCT ( & CDU [ dataOff ] , & CDU [ dataOff + 1 ] , & CDU [ dataOff + 2 ] , & CDU [ dataOff + 3 ] , & CDU [ dataOff + 4 ] , & CDU [ dataOff + 5 ] , & CDU [ dataOff + 6 ] , & CDU [ dataOff + 7 ] ) ;
}
// DCT columns
for ( dataOff = 0 ; dataOff < 8 ; + + dataOff ) {
stbiw__jpg_DCT ( & CDU [ dataOff ] , & CDU [ dataOff + 8 ] , & CDU [ dataOff + 16 ] , & CDU [ dataOff + 24 ] , & CDU [ dataOff + 32 ] , & CDU [ dataOff + 40 ] , & CDU [ dataOff + 48 ] , & CDU [ dataOff + 56 ] ) ;
}
// Quantize/descale/zigzag the coefficients
for ( i = 0 ; i < 64 ; + + i ) {
float v = CDU [ i ] * fdtbl [ i ] ;
// DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
DU [ stbiw__jpg_ZigZag [ i ] ] = ( int ) ( v < 0 ? v - 0.5f : v + 0.5f ) ;
}
// Encode DC
diff = DU [ 0 ] - DC ;
if ( diff = = 0 ) {
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , HTDC [ 0 ] ) ;
} else {
unsigned short bits [ 2 ] ;
stbiw__jpg_calcBits ( diff , bits ) ;
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , HTDC [ bits [ 1 ] ] ) ;
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , bits ) ;
}
// Encode ACs
end0pos = 63 ;
for ( ; ( end0pos > 0 ) & & ( DU [ end0pos ] = = 0 ) ; - - end0pos ) {
}
// end0pos = first element in reverse order !=0
if ( end0pos = = 0 ) {
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , EOB ) ;
return DU [ 0 ] ;
}
for ( i = 1 ; i < = end0pos ; + + i ) {
int startpos = i ;
int nrzeroes ;
unsigned short bits [ 2 ] ;
for ( ; DU [ i ] = = 0 & & i < = end0pos ; + + i ) {
}
nrzeroes = i - startpos ;
if ( nrzeroes > = 16 ) {
int lng = nrzeroes > > 4 ;
int nrmarker ;
for ( nrmarker = 1 ; nrmarker < = lng ; + + nrmarker )
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , M16zeroes ) ;
nrzeroes & = 15 ;
}
stbiw__jpg_calcBits ( DU [ i ] , bits ) ;
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , HTAC [ ( nrzeroes < < 4 ) + bits [ 1 ] ] ) ;
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , bits ) ;
}
if ( end0pos ! = 63 ) {
stbiw__jpg_writeBits ( s , bitBuf , bitCnt , EOB ) ;
}
return DU [ 0 ] ;
}
static int stbi_write_jpg_core ( stbi__write_context * s , int width , int height , int comp , const void * data , int quality ) {
// Constants that don't pollute global namespace
static const unsigned char std_dc_luminance_nrcodes [ ] = { 0 , 0 , 1 , 5 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
static const unsigned char std_dc_luminance_values [ ] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 } ;
static const unsigned char std_ac_luminance_nrcodes [ ] = { 0 , 0 , 2 , 1 , 3 , 3 , 2 , 4 , 3 , 5 , 5 , 4 , 4 , 0 , 0 , 1 , 0x7d } ;
static const unsigned char std_ac_luminance_values [ ] = {
0x01 , 0x02 , 0x03 , 0x00 , 0x04 , 0x11 , 0x05 , 0x12 , 0x21 , 0x31 , 0x41 , 0x06 , 0x13 , 0x51 , 0x61 , 0x07 , 0x22 , 0x71 , 0x14 , 0x32 , 0x81 , 0x91 , 0xa1 , 0x08 ,
0x23 , 0x42 , 0xb1 , 0xc1 , 0x15 , 0x52 , 0xd1 , 0xf0 , 0x24 , 0x33 , 0x62 , 0x72 , 0x82 , 0x09 , 0x0a , 0x16 , 0x17 , 0x18 , 0x19 , 0x1a , 0x25 , 0x26 , 0x27 , 0x28 ,
0x29 , 0x2a , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3a , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 ,
0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 ,
0x8a , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , 0xb2 , 0xb3 , 0xb4 , 0xb5 , 0xb6 ,
0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 , 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 , 0xd7 , 0xd8 , 0xd9 , 0xda , 0xe1 , 0xe2 ,
0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea , 0xf1 , 0xf2 , 0xf3 , 0xf4 , 0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa
} ;
static const unsigned char std_dc_chrominance_nrcodes [ ] = { 0 , 0 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 } ;
static const unsigned char std_dc_chrominance_values [ ] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 } ;
static const unsigned char std_ac_chrominance_nrcodes [ ] = { 0 , 0 , 2 , 1 , 2 , 4 , 4 , 3 , 4 , 7 , 5 , 4 , 4 , 0 , 1 , 2 , 0x77 } ;
static const unsigned char std_ac_chrominance_values [ ] = {
0x00 , 0x01 , 0x02 , 0x03 , 0x11 , 0x04 , 0x05 , 0x21 , 0x31 , 0x06 , 0x12 , 0x41 , 0x51 , 0x07 , 0x61 , 0x71 , 0x13 , 0x22 , 0x32 , 0x81 , 0x08 , 0x14 , 0x42 , 0x91 ,
0xa1 , 0xb1 , 0xc1 , 0x09 , 0x23 , 0x33 , 0x52 , 0xf0 , 0x15 , 0x62 , 0x72 , 0xd1 , 0x0a , 0x16 , 0x24 , 0x34 , 0xe1 , 0x25 , 0xf1 , 0x17 , 0x18 , 0x19 , 0x1a , 0x26 ,
0x27 , 0x28 , 0x29 , 0x2a , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3a , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 ,
0x59 , 0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 ,
0x88 , 0x89 , 0x8a , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , 0xb2 , 0xb3 , 0xb4 ,
0xb5 , 0xb6 , 0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 , 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 , 0xd7 , 0xd8 , 0xd9 , 0xda ,
0xe2 , 0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea , 0xf2 , 0xf3 , 0xf4 , 0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa
} ;
// Huffman tables
static const unsigned short YDC_HT [ 256 ] [ 2 ] = { { 0 , 2 } , { 2 , 3 } , { 3 , 3 } , { 4 , 3 } , { 5 , 3 } , { 6 , 3 } , { 14 , 4 } , { 30 , 5 } , { 62 , 6 } , { 126 , 7 } , { 254 , 8 } , { 510 , 9 } } ;
static const unsigned short UVDC_HT [ 256 ] [ 2 ] = { { 0 , 2 } , { 1 , 2 } , { 2 , 2 } , { 6 , 3 } , { 14 , 4 } , { 30 , 5 } , { 62 , 6 } , { 126 , 7 } , { 254 , 8 } , { 510 , 9 } , { 1022 , 10 } , { 2046 , 11 } } ;
static const unsigned short YAC_HT [ 256 ] [ 2 ] = {
{ 10 , 4 } , { 0 , 2 } , { 1 , 2 } , { 4 , 3 } , { 11 , 4 } , { 26 , 5 } , { 120 , 7 } , { 248 , 8 } , { 1014 , 10 } , { 65410 , 16 } , { 65411 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 12 , 4 } , { 27 , 5 } , { 121 , 7 } , { 502 , 9 } , { 2038 , 11 } , { 65412 , 16 } , { 65413 , 16 } , { 65414 , 16 } , { 65415 , 16 } , { 65416 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 28 , 5 } , { 249 , 8 } , { 1015 , 10 } , { 4084 , 12 } , { 65417 , 16 } , { 65418 , 16 } , { 65419 , 16 } , { 65420 , 16 } , { 65421 , 16 } , { 65422 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 58 , 6 } , { 503 , 9 } , { 4085 , 12 } , { 65423 , 16 } , { 65424 , 16 } , { 65425 , 16 } , { 65426 , 16 } , { 65427 , 16 } , { 65428 , 16 } , { 65429 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 59 , 6 } , { 1016 , 10 } , { 65430 , 16 } , { 65431 , 16 } , { 65432 , 16 } , { 65433 , 16 } , { 65434 , 16 } , { 65435 , 16 } , { 65436 , 16 } , { 65437 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 122 , 7 } , { 2039 , 11 } , { 65438 , 16 } , { 65439 , 16 } , { 65440 , 16 } , { 65441 , 16 } , { 65442 , 16 } , { 65443 , 16 } , { 65444 , 16 } , { 65445 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 123 , 7 } , { 4086 , 12 } , { 65446 , 16 } , { 65447 , 16 } , { 65448 , 16 } , { 65449 , 16 } , { 65450 , 16 } , { 65451 , 16 } , { 65452 , 16 } , { 65453 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 250 , 8 } , { 4087 , 12 } , { 65454 , 16 } , { 65455 , 16 } , { 65456 , 16 } , { 65457 , 16 } , { 65458 , 16 } , { 65459 , 16 } , { 65460 , 16 } , { 65461 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 504 , 9 } , { 32704 , 15 } , { 65462 , 16 } , { 65463 , 16 } , { 65464 , 16 } , { 65465 , 16 } , { 65466 , 16 } , { 65467 , 16 } , { 65468 , 16 } , { 65469 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 505 , 9 } , { 65470 , 16 } , { 65471 , 16 } , { 65472 , 16 } , { 65473 , 16 } , { 65474 , 16 } , { 65475 , 16 } , { 65476 , 16 } , { 65477 , 16 } , { 65478 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 506 , 9 } , { 65479 , 16 } , { 65480 , 16 } , { 65481 , 16 } , { 65482 , 16 } , { 65483 , 16 } , { 65484 , 16 } , { 65485 , 16 } , { 65486 , 16 } , { 65487 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 1017 , 10 } , { 65488 , 16 } , { 65489 , 16 } , { 65490 , 16 } , { 65491 , 16 } , { 65492 , 16 } , { 65493 , 16 } , { 65494 , 16 } , { 65495 , 16 } , { 65496 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 1018 , 10 } , { 65497 , 16 } , { 65498 , 16 } , { 65499 , 16 } , { 65500 , 16 } , { 65501 , 16 } , { 65502 , 16 } , { 65503 , 16 } , { 65504 , 16 } , { 65505 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 2040 , 11 } , { 65506 , 16 } , { 65507 , 16 } , { 65508 , 16 } , { 65509 , 16 } , { 65510 , 16 } , { 65511 , 16 } , { 65512 , 16 } , { 65513 , 16 } , { 65514 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 65515 , 16 } , { 65516 , 16 } , { 65517 , 16 } , { 65518 , 16 } , { 65519 , 16 } , { 65520 , 16 } , { 65521 , 16 } , { 65522 , 16 } , { 65523 , 16 } , { 65524 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 2041 , 11 } , { 65525 , 16 } , { 65526 , 16 } , { 65527 , 16 } , { 65528 , 16 } , { 65529 , 16 } , { 65530 , 16 } , { 65531 , 16 } , { 65532 , 16 } , { 65533 , 16 } , { 65534 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 }
} ;
static const unsigned short UVAC_HT [ 256 ] [ 2 ] = {
{ 0 , 2 } , { 1 , 2 } , { 4 , 3 } , { 10 , 4 } , { 24 , 5 } , { 25 , 5 } , { 56 , 6 } , { 120 , 7 } , { 500 , 9 } , { 1014 , 10 } , { 4084 , 12 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 11 , 4 } , { 57 , 6 } , { 246 , 8 } , { 501 , 9 } , { 2038 , 11 } , { 4085 , 12 } , { 65416 , 16 } , { 65417 , 16 } , { 65418 , 16 } , { 65419 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 26 , 5 } , { 247 , 8 } , { 1015 , 10 } , { 4086 , 12 } , { 32706 , 15 } , { 65420 , 16 } , { 65421 , 16 } , { 65422 , 16 } , { 65423 , 16 } , { 65424 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 27 , 5 } , { 248 , 8 } , { 1016 , 10 } , { 4087 , 12 } , { 65425 , 16 } , { 65426 , 16 } , { 65427 , 16 } , { 65428 , 16 } , { 65429 , 16 } , { 65430 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 58 , 6 } , { 502 , 9 } , { 65431 , 16 } , { 65432 , 16 } , { 65433 , 16 } , { 65434 , 16 } , { 65435 , 16 } , { 65436 , 16 } , { 65437 , 16 } , { 65438 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 59 , 6 } , { 1017 , 10 } , { 65439 , 16 } , { 65440 , 16 } , { 65441 , 16 } , { 65442 , 16 } , { 65443 , 16 } , { 65444 , 16 } , { 65445 , 16 } , { 65446 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 121 , 7 } , { 2039 , 11 } , { 65447 , 16 } , { 65448 , 16 } , { 65449 , 16 } , { 65450 , 16 } , { 65451 , 16 } , { 65452 , 16 } , { 65453 , 16 } , { 65454 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 122 , 7 } , { 2040 , 11 } , { 65455 , 16 } , { 65456 , 16 } , { 65457 , 16 } , { 65458 , 16 } , { 65459 , 16 } , { 65460 , 16 } , { 65461 , 16 } , { 65462 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 249 , 8 } , { 65463 , 16 } , { 65464 , 16 } , { 65465 , 16 } , { 65466 , 16 } , { 65467 , 16 } , { 65468 , 16 } , { 65469 , 16 } , { 65470 , 16 } , { 65471 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 503 , 9 } , { 65472 , 16 } , { 65473 , 16 } , { 65474 , 16 } , { 65475 , 16 } , { 65476 , 16 } , { 65477 , 16 } , { 65478 , 16 } , { 65479 , 16 } , { 65480 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 504 , 9 } , { 65481 , 16 } , { 65482 , 16 } , { 65483 , 16 } , { 65484 , 16 } , { 65485 , 16 } , { 65486 , 16 } , { 65487 , 16 } , { 65488 , 16 } , { 65489 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 505 , 9 } , { 65490 , 16 } , { 65491 , 16 } , { 65492 , 16 } , { 65493 , 16 } , { 65494 , 16 } , { 65495 , 16 } , { 65496 , 16 } , { 65497 , 16 } , { 65498 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 506 , 9 } , { 65499 , 16 } , { 65500 , 16 } , { 65501 , 16 } , { 65502 , 16 } , { 65503 , 16 } , { 65504 , 16 } , { 65505 , 16 } , { 65506 , 16 } , { 65507 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 2041 , 11 } , { 65508 , 16 } , { 65509 , 16 } , { 65510 , 16 } , { 65511 , 16 } , { 65512 , 16 } , { 65513 , 16 } , { 65514 , 16 } , { 65515 , 16 } , { 65516 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 16352 , 14 } , { 65517 , 16 } , { 65518 , 16 } , { 65519 , 16 } , { 65520 , 16 } , { 65521 , 16 } , { 65522 , 16 } , { 65523 , 16 } , { 65524 , 16 } , { 65525 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } ,
{ 1018 , 10 } , { 32707 , 15 } , { 65526 , 16 } , { 65527 , 16 } , { 65528 , 16 } , { 65529 , 16 } , { 65530 , 16 } , { 65531 , 16 } , { 65532 , 16 } , { 65533 , 16 } , { 65534 , 16 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 } , { 0 , 0 }
} ;
static const int YQT [ ] = { 16 , 11 , 10 , 16 , 24 , 40 , 51 , 61 , 12 , 12 , 14 , 19 , 26 , 58 , 60 , 55 , 14 , 13 , 16 , 24 , 40 , 57 , 69 , 56 , 14 , 17 , 22 , 29 , 51 , 87 , 80 , 62 , 18 , 22 ,
37 , 56 , 68 , 109 , 103 , 77 , 24 , 35 , 55 , 64 , 81 , 104 , 113 , 92 , 49 , 64 , 78 , 87 , 103 , 121 , 120 , 101 , 72 , 92 , 95 , 98 , 112 , 100 , 103 , 99 } ;
static const int UVQT [ ] = { 17 , 18 , 24 , 47 , 99 , 99 , 99 , 99 , 18 , 21 , 26 , 66 , 99 , 99 , 99 , 99 , 24 , 26 , 56 , 99 , 99 , 99 , 99 , 99 , 47 , 66 , 99 , 99 , 99 , 99 , 99 , 99 ,
99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 , 99 } ;
static const float aasf [ ] = { 1.0f * 2.828427125f , 1.387039845f * 2.828427125f , 1.306562965f * 2.828427125f , 1.175875602f * 2.828427125f ,
1.0f * 2.828427125f , 0.785694958f * 2.828427125f , 0.541196100f * 2.828427125f , 0.275899379f * 2.828427125f } ;
int row , col , i , k ;
float fdtbl_Y [ 64 ] , fdtbl_UV [ 64 ] ;
unsigned char YTable [ 64 ] , UVTable [ 64 ] ;
if ( ! data | | ! width | | ! height | | comp > 4 | | comp < 1 ) {
return 0 ;
}
quality = quality ? quality : 90 ;
quality = quality < 1 ? 1 : quality > 100 ? 100 : quality ;
quality = quality < 50 ? 5000 / quality : 200 - quality * 2 ;
for ( i = 0 ; i < 64 ; + + i ) {
int uvti , yti = ( YQT [ i ] * quality + 50 ) / 100 ;
YTable [ stbiw__jpg_ZigZag [ i ] ] = ( unsigned char ) ( yti < 1 ? 1 : yti > 255 ? 255 : yti ) ;
uvti = ( UVQT [ i ] * quality + 50 ) / 100 ;
UVTable [ stbiw__jpg_ZigZag [ i ] ] = ( unsigned char ) ( uvti < 1 ? 1 : uvti > 255 ? 255 : uvti ) ;
}
for ( row = 0 , k = 0 ; row < 8 ; + + row ) {
for ( col = 0 ; col < 8 ; + + col , + + k ) {
fdtbl_Y [ k ] = 1 / ( YTable [ stbiw__jpg_ZigZag [ k ] ] * aasf [ row ] * aasf [ col ] ) ;
fdtbl_UV [ k ] = 1 / ( UVTable [ stbiw__jpg_ZigZag [ k ] ] * aasf [ row ] * aasf [ col ] ) ;
}
}
// Write Headers
{
static const unsigned char head0 [ ] = { 0xFF , 0xD8 , 0xFF , 0xE0 , 0 , 0x10 , ' J ' , ' F ' , ' I ' , ' F ' , 0 , 1 , 1 , 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0xFF , 0xDB , 0 , 0x84 , 0 } ;
static const unsigned char head2 [ ] = { 0xFF , 0xDA , 0 , 0xC , 3 , 1 , 0 , 2 , 0x11 , 3 , 0x11 , 0 , 0x3F , 0 } ;
const unsigned char head1 [ ] = { 0xFF , 0xC0 , 0 , 0x11 , 8 , ( unsigned char ) ( height > > 8 ) , STBIW_UCHAR ( height ) , ( unsigned char ) ( width > > 8 ) , STBIW_UCHAR ( width ) ,
3 , 1 , 0x11 , 0 , 2 , 0x11 , 1 , 3 , 0x11 , 1 , 0xFF , 0xC4 , 0x01 , 0xA2 , 0 } ;
s - > func ( s - > context , ( void * ) head0 , sizeof ( head0 ) ) ;
s - > func ( s - > context , ( void * ) YTable , sizeof ( YTable ) ) ;
stbiw__putc ( s , 1 ) ;
s - > func ( s - > context , UVTable , sizeof ( UVTable ) ) ;
s - > func ( s - > context , ( void * ) head1 , sizeof ( head1 ) ) ;
s - > func ( s - > context , ( void * ) ( std_dc_luminance_nrcodes + 1 ) , sizeof ( std_dc_luminance_nrcodes ) - 1 ) ;
s - > func ( s - > context , ( void * ) std_dc_luminance_values , sizeof ( std_dc_luminance_values ) ) ;
stbiw__putc ( s , 0x10 ) ; // HTYACinfo
s - > func ( s - > context , ( void * ) ( std_ac_luminance_nrcodes + 1 ) , sizeof ( std_ac_luminance_nrcodes ) - 1 ) ;
s - > func ( s - > context , ( void * ) std_ac_luminance_values , sizeof ( std_ac_luminance_values ) ) ;
stbiw__putc ( s , 1 ) ; // HTUDCinfo
s - > func ( s - > context , ( void * ) ( std_dc_chrominance_nrcodes + 1 ) , sizeof ( std_dc_chrominance_nrcodes ) - 1 ) ;
s - > func ( s - > context , ( void * ) std_dc_chrominance_values , sizeof ( std_dc_chrominance_values ) ) ;
stbiw__putc ( s , 0x11 ) ; // HTUACinfo
s - > func ( s - > context , ( void * ) ( std_ac_chrominance_nrcodes + 1 ) , sizeof ( std_ac_chrominance_nrcodes ) - 1 ) ;
s - > func ( s - > context , ( void * ) std_ac_chrominance_values , sizeof ( std_ac_chrominance_values ) ) ;
s - > func ( s - > context , ( void * ) head2 , sizeof ( head2 ) ) ;
}
// Encode 8x8 macroblocks
{
static const unsigned short fillBits [ ] = { 0x7F , 7 } ;
const unsigned char * imageData = ( const unsigned char * ) data ;
int DCY = 0 , DCU = 0 , DCV = 0 ;
int bitBuf = 0 , bitCnt = 0 ;
// comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0 , ofsB = comp > 2 ? 2 : 0 ;
int x , y , pos ;
for ( y = 0 ; y < height ; y + = 8 ) {
for ( x = 0 ; x < width ; x + = 8 ) {
float YDU [ 64 ] , UDU [ 64 ] , VDU [ 64 ] ;
for ( row = y , pos = 0 ; row < y + 8 ; + + row ) {
for ( col = x ; col < x + 8 ; + + col , + + pos ) {
int p = ( stbi__flip_vertically_on_write ? height - 1 - row : row ) * width * comp + col * comp ;
float r , g , b ;
if ( row > = height ) {
p - = width * comp * ( row + 1 - height ) ;
}
if ( col > = width ) {
p - = comp * ( col + 1 - width ) ;
}
r = imageData [ p + 0 ] ;
g = imageData [ p + ofsG ] ;
b = imageData [ p + ofsB ] ;
YDU [ pos ] = + 0.29900f * r + 0.58700f * g + 0.11400f * b - 128 ;
UDU [ pos ] = - 0.16874f * r - 0.33126f * g + 0.50000f * b ;
VDU [ pos ] = + 0.50000f * r - 0.41869f * g - 0.08131f * b ;
}
}
DCY = stbiw__jpg_processDU ( s , & bitBuf , & bitCnt , YDU , fdtbl_Y , DCY , YDC_HT , YAC_HT ) ;
DCU = stbiw__jpg_processDU ( s , & bitBuf , & bitCnt , UDU , fdtbl_UV , DCU , UVDC_HT , UVAC_HT ) ;
DCV = stbiw__jpg_processDU ( s , & bitBuf , & bitCnt , VDU , fdtbl_UV , DCV , UVDC_HT , UVAC_HT ) ;
}
}
// Do the bit alignment of the EOI marker
stbiw__jpg_writeBits ( s , & bitBuf , & bitCnt , fillBits ) ;
}
// EOI
stbiw__putc ( s , 0xFF ) ;
stbiw__putc ( s , 0xD9 ) ;
return 1 ;
}
STBIWDEF int stbi_write_jpg_to_func ( stbi_write_func * func , void * context , int x , int y , int comp , const void * data , int quality )
{
stbi__write_context s ;
stbi__start_write_callbacks ( & s , func , context ) ;
return stbi_write_jpg_core ( & s , x , y , comp , ( void * ) data , quality ) ;
}
# ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_jpg ( char const * filename , int x , int y , int comp , const void * data , int quality )
{
stbi__write_context s ;
if ( stbi__start_write_file ( & s , filename ) ) {
int r = stbi_write_jpg_core ( & s , x , y , comp , data , quality ) ;
stbi__end_write_file ( & s ) ;
return r ;
} else
return 0 ;
}
# endif
# endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
1.08 ( 2018 - 01 - 29 )
add stbi__flip_vertically_on_write , external zlib , zlib quality , choose PNG filter
1.07 ( 2017 - 07 - 24 )
doc fix
1.06 ( 2017 - 07 - 23 )
writing JPEG ( using Jon Olick ' s code )
1.05 ? ? ?
1.04 ( 2017 - 03 - 03 )
monochrome BMP expansion
1.03 ? ? ?