@ -7,9 +7,9 @@
# define STBIR_ASSERT(x) to avoid using assert.h.
# define STBIR_ASSERT(x) to avoid using assert.h.
# define STBIR_MALLOC(context,size ) and STBIR_FREE(context,ptr ) to avoid using stdlib.h malloc.
# define STBIR_MALLOC(size, context) and STBIR_FREE(ptr, context) to avoid using stdlib.h malloc.
Each function makes exactly one call to malloc / free , so to avoid allocations ,
Each function makes exactly one call to malloc / free , so to avoid allocations ,
pass in a temp memory block as context and return that from MALLOC .
pass in a temp memory block as context and return that from MALLOC .
QUICK NOTES :
QUICK NOTES :
Written with emphasis on usage and speed . Only the resize operation is
Written with emphasis on usage and speed . Only the resize operation is
@ -139,7 +139,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels
typedef enum
typedef enum
{
{
STBIR_FILTER_DEFAULT = 0 , // use same filter type that easy-to-use API chooses
STBIR_FILTER_DEFAULT = 0 , // use same filter type that easy-to-use API chooses
STBIR_FILTER_BOX = 1 ,
STBIR_FILTER_BOX = 1 , // Is actually a trapezoid. See https://developer.nvidia.com/content/non-power-two-mipmapping
STBIR_FILTER_BILINEAR = 2 ,
STBIR_FILTER_BILINEAR = 2 ,
STBIR_FILTER_BICUBIC = 3 , // A cubic b spline
STBIR_FILTER_BICUBIC = 3 , // A cubic b spline
STBIR_FILTER_CATMULLROM = 4 ,
STBIR_FILTER_CATMULLROM = 4 ,
@ -241,6 +241,15 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int
# define STBIR_PROGRESS_REPORT(float_0_to_1)
# define STBIR_PROGRESS_REPORT(float_0_to_1)
# endif
# endif
// This value is added to alpha just before premultiplication to avoid
// zeroing out color values. It is equivalent to 2^-80. If you don't want
// that behavior (it may interfere if you have floating point images with
// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to
// disable it.
# ifndef STBIR_EPSILON
# define STBIR_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20))
# endif
//
//
//
//
//// end header file /////////////////////////////////////////////////////
//// end header file /////////////////////////////////////////////////////
@ -274,8 +283,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int
# ifndef STBIR_MALLOC
# ifndef STBIR_MALLOC
# include <stdlib.h>
# include <stdlib.h>
# define STBIR_MALLOC(c, x) malloc(x)
# define STBIR_MALLOC(x,c ) malloc(x)
# define STBIR_FREE(c, x) free(x)
# define STBIR_FREE(x,c ) free(x)
# endif
# endif
# ifndef _MSC_VER
# ifndef _MSC_VER
@ -312,6 +321,8 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]
# define STBIR_MAX_CHANNELS 16
# define STBIR_MAX_CHANNELS 16
# endif
# endif
# define STBIR__UNUSED_PARAM(s) s=s;
// must match stbir_datatype
// must match stbir_datatype
static unsigned char stbir__type_size [ ] = {
static unsigned char stbir__type_size [ ] = {
1 , // STBIR_TYPE_UINT8
1 , // STBIR_TYPE_UINT8
@ -321,12 +332,13 @@ static unsigned char stbir__type_size[] = {
} ;
} ;
// Kernel function centered at 0
// Kernel function centered at 0
typedef float ( stbir__kernel_fn ) ( float x ) ;
typedef float ( stbir__kernel_fn ) ( float x , float scale ) ;
typedef float ( stbir__support_fn ) ( float scale ) ;
typedef struct
typedef struct
{
{
stbir__kernel_fn * kernel ;
stbir__kernel_fn * kernel ;
float support ;
stbir__support_fn * support ;
} stbir__filter_info ;
} stbir__filter_info ;
// When upsampling, the contributors are which source pixels contribute.
// When upsampling, the contributors are which source pixels contribute.
@ -384,6 +396,14 @@ typedef struct
float * ring_buffer ;
float * ring_buffer ;
float * encode_buffer ; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
float * encode_buffer ; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds.
int horizontal_contributors_size ;
int horizontal_coefficients_size ;
int vertical_coefficients_size ;
int decode_buffer_size ;
int horizontal_buffer_size ;
int ring_buffer_size ;
int encode_buffer_size ;
} stbir__info ;
} stbir__info ;
static stbir__inline int stbir__min ( int a , int b )
static stbir__inline int stbir__min ( int a , int b )
@ -437,7 +457,7 @@ static float stbir__srgb_uchar_to_linear_float[256] = {
// sRGB transition values, scaled by 1<<28
// sRGB transition values, scaled by 1<<28
static int stbir__srgb_offset_to_linear_scaled [ 256 ] =
static int stbir__srgb_offset_to_linear_scaled [ 256 ] =
{
{
40579 , 121738 , 202897 , 284056 , 365216 , 446375 , 527534 , 608693 ,
40579 , 121738 , 202897 , 284056 , 365216 , 446375 , 527534 , 608693 ,
689852 , 771011 , 852421 , 938035 , 1028466 , 1123787 , 1224073 , 1329393 ,
689852 , 771011 , 852421 , 938035 , 1028466 , 1123787 , 1224073 , 1329393 ,
1439819 , 1555418 , 1676257 , 1802402 , 1933917 , 2070867 , 2213313 , 2361317 ,
1439819 , 1555418 , 1676257 , 1802402 , 1933917 , 2070867 , 2213313 , 2361317 ,
2514938 , 2674237 , 2839271 , 3010099 , 3186776 , 3369359 , 3557903 , 3752463 ,
2514938 , 2674237 , 2839271 , 3010099 , 3186776 , 3369359 , 3557903 , 3752463 ,
@ -491,30 +511,57 @@ static unsigned char stbir__linear_to_srgb_uchar(float f)
{
{
int x = ( int ) ( f * ( 1 < < 28 ) ) ; // has headroom so you don't need to clamp
int x = ( int ) ( f * ( 1 < < 28 ) ) ; // has headroom so you don't need to clamp
int v = 0 ;
int v = 0 ;
int i ;
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 128 ] ) v + = 128 ;
// Everything below 128 is off by 1. This fixes that.
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 64 ] ) v + = 64 ;
int fix = 0 ;
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 32 ] ) v + = 32 ;
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 16 ] ) v + = 16 ;
// Adding 1 to 0 with the fix gives incorrect results for input 0. This fixes that.
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 8 ] ) v + = 8 ;
if ( x < 81000 )
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 4 ] ) v + = 4 ;
return 0 ;
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 2 ] ) v + = 2 ;
if ( x > = stbir__srgb_offset_to_linear_scaled [ v + 1 ] ) v + = 1 ;
i = 128 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ; else fix = 1 ;
return ( unsigned char ) v ;
i = v + 64 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 32 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 16 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 8 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 4 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 2 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
i = v + 1 ; if ( x > = stbir__srgb_offset_to_linear_scaled [ i ] ) v = i ;
return ( unsigned char ) v + fix ;
}
}
static float stbir__filter_box ( float x )
static float stbir__filter_trapezoid ( float x , float scale )
{
{
if ( x < = - 0.5f )
STBIR__DEBUG_ASSERT ( scale < = 1 ) ;
return 0 ;
x = ( float ) fabs ( x ) ;
else if ( x > 0.5f )
float halfscale = scale / 2 ;
float t = 0.5f + halfscale ;
if ( x > = t )
return 0 ;
return 0 ;
else
else
return 1 ;
{
float r = 0.5f - halfscale ;
if ( x < = r )
return 1 ;
else
return ( t - x ) / scale ;
}
}
static float stbir__support_trapezoid ( float scale )
{
STBIR__DEBUG_ASSERT ( scale < = 1 ) ;
return 0.5f + scale / 2 ;
}
}
static float stbir__filter_bilinear ( float x )
static float stbir__filter_bilinear ( float x , float s )
{
{
STBIR__UNUSED_PARAM ( s )
x = ( float ) fabs ( x ) ;
x = ( float ) fabs ( x ) ;
if ( x < = 1.0f )
if ( x < = 1.0f )
@ -523,20 +570,24 @@ static float stbir__filter_bilinear(float x)
return 0 ;
return 0 ;
}
}
static float stbir__filter_bicubic ( float x )
static float stbir__filter_bicubic ( float x , float s )
{
{
STBIR__UNUSED_PARAM ( s )
x = ( float ) fabs ( x ) ;
x = ( float ) fabs ( x ) ;
if ( x < 1.0f )
if ( x < 1.0f )
return 0.66666666666f + x * x * ( 0.5f * x - 1 ) ;
return ( 4 + x * x * ( 3 * x - 6 ) ) / 6 ;
else if ( x < 2.0f )
else if ( x < 2.0f )
return 1.3333333333f + x * ( - 2 + x * ( 1 - 0.16666666f * x ) ) ;
return ( 8 + x * ( - 1 2 + x * ( 6 - x ) ) ) / 6 ;
return ( 0.0f ) ;
return ( 0.0f ) ;
}
}
static float stbir__filter_catmullrom ( float x )
static float stbir__filter_catmullrom ( float x , float s )
{
{
STBIR__UNUSED_PARAM ( s )
x = ( float ) fabs ( x ) ;
x = ( float ) fabs ( x ) ;
if ( x < 1.0f )
if ( x < 1.0f )
@ -547,25 +598,45 @@ static float stbir__filter_catmullrom(float x)
return ( 0.0f ) ;
return ( 0.0f ) ;
}
}
static float stbir__filter_mitchell ( float x )
static float stbir__filter_mitchell ( float x , float s )
{
{
STBIR__UNUSED_PARAM ( s )
x = ( float ) fabs ( x ) ;
x = ( float ) fabs ( x ) ;
if ( x < 1.0f )
if ( x < 1.0f )
return 0.8888888888f + x * x * ( 1.1666666666666f * x - 2.0f ) ;
return ( 16 + x * x * ( 21 * x - 36 ) ) / 18 ;
else if ( x < 2.0f )
else if ( x < 2.0f )
return 1.777777777777f + x * ( - 3.3333333333f + x * ( 2 - 0.3888888888888f * x ) ) ;
return ( 32 + x * ( - 60 + x * ( 36 - 7 * x ) ) ) / 18 ;
return ( 0.0f ) ;
return ( 0.0f ) ;
}
}
static float stbir__support_zero ( float s )
{
STBIR__UNUSED_PARAM ( s )
return 0 ;
}
static float stbir__support_one ( float s )
{
STBIR__UNUSED_PARAM ( s )
return 1 ;
}
static float stbir__support_two ( float s )
{
STBIR__UNUSED_PARAM ( s )
return 2 ;
}
static stbir__filter_info stbir__filter_info_table [ ] = {
static stbir__filter_info stbir__filter_info_table [ ] = {
{ NULL , 0.0f } ,
{ NULL , stbir__support_zero } ,
{ stbir__filter_box , 0.5f } ,
{ stbir__filter_trapezoid , stbir__support_trapezoid } ,
{ stbir__filter_bilinear , 1.0f } ,
{ stbir__filter_bilinear , stbir__support_one } ,
{ stbir__filter_bicubic , 2.0f } ,
{ stbir__filter_bicubic , stbir__support_two } ,
{ stbir__filter_catmullrom , 2.0f } ,
{ stbir__filter_catmullrom , stbir__support_two } ,
{ stbir__filter_mitchell , 2.0f } ,
{ stbir__filter_mitchell , stbir__support_two } ,
} ;
} ;
stbir__inline static int stbir__use_upsampling ( float ratio )
stbir__inline static int stbir__use_upsampling ( float ratio )
@ -585,42 +656,42 @@ stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info)
// This is the maximum number of input samples that can affect an output sample
// This is the maximum number of input samples that can affect an output sample
// with the given filter
// with the given filter
stbir__inline static int stbir__get_filter_pixel_width ( stbir_filter filter , int input_w , int output_w , float scale )
stbir__inline static int stbir__get_filter_pixel_width ( stbir_filter filter , float scale )
{
{
STBIR_ASSERT ( filter ! = 0 ) ;
STBIR_ASSERT ( filter ! = 0 ) ;
STBIR_ASSERT ( filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ;
STBIR_ASSERT ( filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ;
if ( stbir__use_upsampling ( scale ) )
if ( stbir__use_upsampling ( scale ) )
return ( int ) ceil ( stbir__filter_info_table [ filter ] . support * 2 ) ;
return ( int ) ceil ( stbir__filter_info_table [ filter ] . support ( 1 / scale ) * 2 ) ;
else
else
return ( int ) ceil ( stbir__filter_info_table [ filter ] . support * 2 / scale ) ;
return ( int ) ceil ( stbir__filter_info_table [ filter ] . support ( scale ) * 2 / scale ) ;
}
}
stbir__inline static int stbir__get_filter_pixel_width_horizontal ( stbir__info * stbir_info )
stbir__inline static int stbir__get_filter_pixel_width_horizontal ( stbir__info * stbir_info )
{
{
return stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > input_w , stbir_info - > output_w , stbir_info - > horizontal_scale ) ;
return stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > horizontal_scale ) ;
}
}
stbir__inline static int stbir__get_filter_pixel_width_vertical ( stbir__info * stbir_info )
stbir__inline static int stbir__get_filter_pixel_width_vertical ( stbir__info * stbir_info )
{
{
return stbir__get_filter_pixel_width ( stbir_info - > vertical_filter , stbir_info - > input_h , stbir_info - > output_h , stbir_info - > vertical_scale ) ;
return stbir__get_filter_pixel_width ( stbir_info - > vertical_filter , stbir_info - > vertical_scale ) ;
}
}
// This is how much to expand buffers to account for filters seeking outside
// This is how much to expand buffers to account for filters seeking outside
// the image boundaries.
// the image boundaries.
stbir__inline static int stbir__get_filter_pixel_margin ( stbir_filter filter , int input_w , int output_w , float scale )
stbir__inline static int stbir__get_filter_pixel_margin ( stbir_filter filter , float scale )
{
{
return stbir__get_filter_pixel_width ( filter , input_w , output_w , scale ) / 2 ;
return stbir__get_filter_pixel_width ( filter , scale ) / 2 ;
}
}
stbir__inline static int stbir__get_filter_pixel_margin_horizontal ( stbir__info * stbir_info )
stbir__inline static int stbir__get_filter_pixel_margin_horizontal ( stbir__info * stbir_info )
{
{
return stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > input_w , stbir_info - > output_w , stbir_info - > horizontal_scale ) / 2 ;
return stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > horizontal_scale ) / 2 ;
}
}
stbir__inline static int stbir__get_filter_pixel_margin_vertical ( stbir__info * stbir_info )
stbir__inline static int stbir__get_filter_pixel_margin_vertical ( stbir__info * stbir_info )
{
{
return stbir__get_filter_pixel_width ( stbir_info - > vertical_filter , stbir_info - > input_h , stbir_info - > output_h , stbir_info - > vertical_scale ) / 2 ;
return stbir__get_filter_pixel_width ( stbir_info - > vertical_filter , stbir_info - > vertical_scale ) / 2 ;
}
}
stbir__inline static int stbir__get_horizontal_contributors ( stbir__info * info )
stbir__inline static int stbir__get_horizontal_contributors ( stbir__info * info )
@ -628,13 +699,13 @@ stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info)
if ( stbir__use_upsampling ( info - > horizontal_scale ) )
if ( stbir__use_upsampling ( info - > horizontal_scale ) )
return info - > output_w ;
return info - > output_w ;
else
else
return ( info - > input_w + stbir__get_filter_pixel_margin ( info - > horizontal_filter , info - > input_w , info - > output_w , info - > horizontal_scale ) * 2 ) ;
return ( info - > input_w + stbir__get_filter_pixel_margin ( info - > horizontal_filter , info - > horizontal_scale ) * 2 ) ;
}
}
stbir__inline static int stbir__get_total_coefficients ( stbir__info * info )
stbir__inline static int stbir__get_total_coefficients ( stbir__info * info )
{
{
return stbir__get_horizontal_contributors ( info )
return stbir__get_horizontal_contributors ( info )
* stbir__get_filter_pixel_width ( info - > horizontal_filter , info - > input_w , info - > output_w , info - > horizontal_scale ) ;
* stbir__get_filter_pixel_width ( info - > horizontal_filter , info - > horizontal_scale ) ;
}
}
stbir__inline static stbir__contributors * stbir__get_contributor ( stbir__info * stbir_info , int n )
stbir__inline static stbir__contributors * stbir__get_contributor ( stbir__info * stbir_info , int n )
@ -645,7 +716,7 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* st
stbir__inline static float * stbir__get_coefficient ( stbir__info * stbir_info , int n , int c )
stbir__inline static float * stbir__get_coefficient ( stbir__info * stbir_info , int n , int c )
{
{
int width = stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > input_w , stbir_info - > output_w , stbir_info - > horizontal_scale ) ;
int width = stbir__get_filter_pixel_width ( stbir_info - > horizontal_filter , stbir_info - > horizontal_scale ) ;
return & stbir_info - > horizontal_coefficients [ width * n + c ] ;
return & stbir_info - > horizontal_coefficients [ width * n + c ] ;
}
}
@ -745,13 +816,13 @@ static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radi
* out_last_pixel = ( int ) ( floor ( out_pixel_influence_upperbound - 0.5 ) ) ;
* out_last_pixel = ( int ) ( floor ( out_pixel_influence_upperbound - 0.5 ) ) ;
}
}
static void stbir__calculate_coefficients_upsample ( stbir__info * stbir_info , stbir_filter filter , int in_first_pixel , int in_last_pixel , float in_center_of_out , stbir__contributors * contributor , float * coefficient_group )
static void stbir__calculate_coefficients_upsample ( stbir__info * stbir_info , stbir_filter filter , float scale , int in_first_pixel , int in_last_pixel , float in_center_of_out , stbir__contributors * contributor , float * coefficient_group )
{
{
int i ;
int i ;
float total_filter = 0 ;
float total_filter = 0 ;
float filter_scale ;
float filter_scale ;
STBIR__DEBUG_ASSERT ( in_last_pixel - in_first_pixel < = stbir__get_filter_pixel_width_horizontal ( stbir_info ) ) ;
STBIR__DEBUG_ASSERT ( in_last_pixel - in_first_pixel < = ( int ) ceil ( stbir__filter_info_table [ filter ] . support ( 1 / scale ) * 2 ) ) ; // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical.
contributor - > n0 = in_first_pixel ;
contributor - > n0 = in_first_pixel ;
contributor - > n1 = in_last_pixel ;
contributor - > n1 = in_last_pixel ;
@ -761,7 +832,7 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi
for ( i = 0 ; i < = in_last_pixel - in_first_pixel ; i + + )
for ( i = 0 ; i < = in_last_pixel - in_first_pixel ; i + + )
{
{
float in_pixel_center = ( float ) ( i + in_first_pixel ) + 0.5f ;
float in_pixel_center = ( float ) ( i + in_first_pixel ) + 0.5f ;
total_filter + = coefficient_group [ i ] = stbir__filter_info_table [ filter ] . kernel ( in_center_of_out - in_pixel_center ) ;
total_filter + = coefficient_group [ i ] = stbir__filter_info_table [ filter ] . kernel ( in_center_of_out - in_pixel_center , 1 / scale ) ;
}
}
STBIR__DEBUG_ASSERT ( total_filter > 0.9 ) ;
STBIR__DEBUG_ASSERT ( total_filter > 0.9 ) ;
@ -778,7 +849,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
{
{
int i ;
int i ;
STBIR__DEBUG_ASSERT ( out_last_pixel - out_first_pixel < = stbir__get_filter_pixel_width_horizontal ( stbir_inf o) ) ;
STBIR__DEBUG_ASSERT ( out_last_pixel - out_first_pixel < = ( int ) ceil ( stbir__filter_info_table [ filter ] . support ( scale_ratio ) * 2 / scale_rati o) ) ; // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical.
contributor - > n0 = out_first_pixel ;
contributor - > n0 = out_first_pixel ;
contributor - > n1 = out_last_pixel ;
contributor - > n1 = out_last_pixel ;
@ -789,19 +860,19 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
{
{
float out_pixel_center = ( float ) ( i + out_first_pixel ) + 0.5f ;
float out_pixel_center = ( float ) ( i + out_first_pixel ) + 0.5f ;
float x = out_pixel_center - out_center_of_in ;
float x = out_pixel_center - out_center_of_in ;
coefficient_group [ i ] = stbir__filter_info_table [ filter ] . kernel ( x ) * scale_ratio ;
coefficient_group [ i ] = stbir__filter_info_table [ filter ] . kernel ( x , scale_ratio ) * scale_ratio ;
}
}
}
}
# ifdef STBIR_DEBUG
static void stbir__normalize_downsample_coefficients ( stbir__info * stbir_info )
static void stbir__check_downsample_coefficients ( stbir__info * stbir_info )
{
{
int num_contributors = stbir__get_horizontal_contributors ( stbir_info ) ;
int i ;
int i ;
for ( i = 0 ; i < stbir_info - > output_w ; i + + )
for ( i = 0 ; i < stbir_info - > output_w ; i + + )
{
{
float total = 0 ;
float total = 0 ;
int j ;
int j ;
for ( j = 0 ; j < stbir__get_horizontal_contributors ( stbir_info ) ; j + + )
for ( j = 0 ; j < num_contributors ; j + + )
{
{
if ( i > = stbir_info - > horizontal_contributors [ j ] . n0 & & i < = stbir_info - > horizontal_contributors [ j ] . n1 )
if ( i > = stbir_info - > horizontal_contributors [ j ] . n0 & & i < = stbir_info - > horizontal_contributors [ j ] . n1 )
{
{
@ -813,10 +884,28 @@ static void stbir__check_downsample_coefficients(stbir__info* stbir_info)
}
}
STBIR__DEBUG_ASSERT ( total > 0.9f ) ;
STBIR__DEBUG_ASSERT ( total > 0.9f ) ;
STBIR__DEBUG_ASSERT ( total < = 1.0f + 1.0f / ( pow ( 2.0f , 8.0f * stbir__type_size [ stbir_info - > type ] ) - 1 ) ) ;
STBIR__DEBUG_ASSERT ( total < 1.1f ) ;
float scale = 1 / total ;
for ( j = 0 ; j < num_contributors ; j + + )
{
if ( i > = stbir_info - > horizontal_contributors [ j ] . n0 & & i < = stbir_info - > horizontal_contributors [ j ] . n1 )
* stbir__get_coefficient ( stbir_info , j , i - stbir_info - > horizontal_contributors [ j ] . n0 ) * = scale ;
else if ( i < stbir_info - > horizontal_contributors [ j ] . n0 )
break ;
}
}
// Using min to avoid writing into invalid pixels.
for ( i = 0 ; i < num_contributors ; i + + )
{
stbir__contributors * contributors = & stbir_info - > horizontal_contributors [ i ] ;
STBIR__DEBUG_ASSERT ( contributors - > n1 > = contributors - > n0 ) ;
contributors - > n1 = stbir__min ( contributors - > n1 , stbir_info - > output_w - 1 ) ;
}
}
}
}
# endif
// Each scan line uses the same kernel values so we should calculate the kernel
// Each scan line uses the same kernel values so we should calculate the kernel
// values once and then we can use them for every scan line.
// values once and then we can use them for every scan line.
@ -829,7 +918,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info)
if ( stbir__use_width_upsampling ( stbir_info ) )
if ( stbir__use_width_upsampling ( stbir_info ) )
{
{
float out_pixels_radius = stbir__filter_info_table [ stbir_info - > horizontal_filter ] . support * scale_ratio ;
float out_pixels_radius = stbir__filter_info_table [ stbir_info - > horizontal_filter ] . support ( 1 / scale_ratio ) * scale_ratio ;
// Looping through out pixels
// Looping through out pixels
for ( n = 0 ; n < total_contributors ; n + + )
for ( n = 0 ; n < total_contributors ; n + + )
@ -839,12 +928,12 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info)
stbir__calculate_sample_range_upsample ( n , out_pixels_radius , scale_ratio , stbir_info - > horizontal_shift , & in_first_pixel , & in_last_pixel , & in_center_of_out ) ;
stbir__calculate_sample_range_upsample ( n , out_pixels_radius , scale_ratio , stbir_info - > horizontal_shift , & in_first_pixel , & in_last_pixel , & in_center_of_out ) ;
stbir__calculate_coefficients_upsample ( stbir_info , stbir_info - > horizontal_filter , in_first_pixel , in_last_pixel , in_center_of_out , stbir__get_contributor ( stbir_info , n ) , stbir__get_coefficient ( stbir_info , n , 0 ) ) ;
stbir__calculate_coefficients_upsample ( stbir_info , stbir_info - > horizontal_filter , scale_ratio , in_first_pixel , in_last_pixel , in_center_of_out , stbir__get_contributor ( stbir_info , n ) , stbir__get_coefficient ( stbir_info , n , 0 ) ) ;
}
}
}
}
else
else
{
{
float in_pixels_radius = stbir__filter_info_table [ stbir_info - > horizontal_filter ] . support / scale_ratio ;
float in_pixels_radius = stbir__filter_info_table [ stbir_info - > horizontal_filter ] . support ( scale_ratio ) / scale_ratio ;
// Looping through in pixels
// Looping through in pixels
for ( n = 0 ; n < total_contributors ; n + + )
for ( n = 0 ; n < total_contributors ; n + + )
@ -858,9 +947,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info)
stbir__calculate_coefficients_downsample ( stbir_info , stbir_info - > horizontal_filter , scale_ratio , out_first_pixel , out_last_pixel , out_center_of_in , stbir__get_contributor ( stbir_info , n ) , stbir__get_coefficient ( stbir_info , n , 0 ) ) ;
stbir__calculate_coefficients_downsample ( stbir_info , stbir_info - > horizontal_filter , scale_ratio , out_first_pixel , out_last_pixel , out_center_of_in , stbir__get_contributor ( stbir_info , n ) , stbir__get_coefficient ( stbir_info , n , 0 ) ) ;
}
}
# ifdef STBIR_DEBUG
stbir__normalize_downsample_coefficients ( stbir_info ) ;
stbir__check_downsample_coefficients ( stbir_info ) ;
# endif
}
}
}
}
@ -1002,12 +1089,16 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
break ;
break ;
}
}
# ifndef STBIR_NO_ALPHA_EPSILON
if ( ! ( stbir_info - > flags & STBIR_FLAG_PREMULTIPLIED_ALPHA ) )
if ( ! ( stbir_info - > flags & STBIR_FLAG_PREMULTIPLIED_ALPHA ) )
{
{
for ( x = - stbir__get_filter_pixel_margin_horizontal ( stbir_info ) ; x < max_x ; x + + )
for ( x = - stbir__get_filter_pixel_margin_horizontal ( stbir_info ) ; x < max_x ; x + + )
{
{
int decode_pixel_index = x * channels ;
int decode_pixel_index = x * channels ;
float alpha = decode_buffer [ decode_pixel_index + alpha_channel ] ;
// If the alpha value is 0 it will clobber the color values. Make sure it's not.
float alpha = ( decode_buffer [ decode_pixel_index + alpha_channel ] + = STBIR_EPSILON ) ;
for ( c = 0 ; c < channels ; c + + )
for ( c = 0 ; c < channels ; c + + )
{
{
if ( c = = alpha_channel )
if ( c = = alpha_channel )
@ -1017,6 +1108,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
}
}
}
}
}
}
# endif
if ( edge_horizontal = = STBIR_EDGE_ZERO )
if ( edge_horizontal = = STBIR_EDGE_ZERO )
{
{
@ -1123,12 +1215,10 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
int in_x = x - filter_pixel_margin ;
int in_x = x - filter_pixel_margin ;
int in_pixel_index = in_x * channels ;
int in_pixel_index = in_x * channels ;
int max_n = stbir__min ( n1 , output_w - 1 ) ;
int max_n = n1 ;
int coefficient_group = x * kernel_pixel_width ;
int coefficient_group = x * kernel_pixel_width ;
STBIR__DEBUG_ASSERT ( n1 > = n0 ) ;
// Using max to avoid writing into invalid pixels.
// Using min and max to avoid writing into invalid pixels.
for ( k = stbir__max ( n0 , 0 ) ; k < = max_n ; k + + )
for ( k = stbir__max ( n0 , 0 ) ; k < = max_n ; k + + )
{
{
int coefficient_index = ( k - n0 ) + coefficient_group ;
int coefficient_index = ( k - n0 ) + coefficient_group ;
@ -1186,71 +1276,83 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
int x ;
int x ;
int n ;
int n ;
if ( ! ( stbir_info - > flags & STBIR_FLAG_PREMULTIPLIED_ALPHA ) )
# ifndef STBIR_NO_ALPHA_EPSILON
if ( ! ( stbir_info - > flags & STBIR_FLAG_PREMULTIPLIED_ALPHA ) )
{
{
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
float alpha = encode_buffer [ encode_pixel_index + alpha_channel ] ;
float alpha = encode_buffer [ pixel_index + alpha_channel ] ;
STBIR__DEBUG_ASSERT ( alpha > 0 ) ;
float reciprocal_alpha = alpha ? 1.0f / alpha : 0 ;
float reciprocal_alpha = alpha ? 1.0f / alpha : 0 ;
// @TODO: if final alpha=0, we actually want to have ignored alpha... set alpha to sRGB_to_linear(1/255)/(2^24) so floats will discard it?
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
if ( n ! = alpha_channel )
if ( n ! = alpha_channel )
encode_buffer [ encode_pixel_index + n ] * = reciprocal_alpha ;
encode_buffer [ pixel_index + n ] * = reciprocal_alpha ;
// We added in a small epsilon to prevent the color channel from being deleted with zero alpha. Remove it now.
encode_buffer [ pixel_index + alpha_channel ] - = STBIR_EPSILON ;
}
}
}
}
# endif
switch ( decode )
switch ( decode )
{
{
case STBIR__DECODE ( STBIR_TYPE_UINT8 , STBIR_COLORSPACE_LINEAR ) :
case STBIR__DECODE ( STBIR_TYPE_UINT8 , STBIR_COLORSPACE_LINEAR ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned char * ) output_buffer ) [ output_pixel_index + n ] = ( unsigned char ) ( stbir__saturate ( encode_buffer [ encode_pixel_index + n ] ) * 255 ) ;
{
int index = pixel_index + n ;
( ( unsigned char * ) output_buffer ) [ index ] = ( unsigned char ) ( round ( stbir__saturate ( encode_buffer [ index ] ) * 255 ) ) ;
}
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_UINT8 , STBIR_COLORSPACE_SRGB ) :
case STBIR__DECODE ( STBIR_TYPE_UINT8 , STBIR_COLORSPACE_SRGB ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned char * ) output_buffer ) [ output_pixel_index + n ] = stbir__linear_to_srgb_uchar ( encode_buffer [ encode_pixel_index + n ] ) ;
{
int index = pixel_index + n ;
( ( unsigned char * ) output_buffer ) [ index ] = stbir__linear_to_srgb_uchar ( encode_buffer [ index ] ) ;
}
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
( ( unsigned char * ) output_buffer ) [ output_pixel_index + alpha_channel ] = ( unsigned char ) ( stbir__saturate ( encode_buffer [ encode_pixel_index + alpha_channel ] ) * 255 ) ;
( ( unsigned char * ) output_buffer ) [ pixel_index + alpha_channel ] = ( unsigned char ) ( round ( stbir__saturate ( encode_buffer [ pixel_index + alpha_channel ] ) * 255 ) ) ;
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_UINT16 , STBIR_COLORSPACE_LINEAR ) :
case STBIR__DECODE ( STBIR_TYPE_UINT16 , STBIR_COLORSPACE_LINEAR ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned short * ) output_buffer ) [ output_pixel_index + n ] = ( unsigned short ) ( stbir__saturate ( encode_buffer [ encode_pixel_index + n ] ) * 65535 ) ;
{
int index = pixel_index + n ;
( ( unsigned short * ) output_buffer ) [ index ] = ( unsigned short ) ( round ( stbir__saturate ( encode_buffer [ index ] ) * 65535 ) ) ;
}
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_UINT16 , STBIR_COLORSPACE_SRGB ) :
case STBIR__DECODE ( STBIR_TYPE_UINT16 , STBIR_COLORSPACE_SRGB ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned short * ) output_buffer ) [ output_pixel_index + n ] = ( unsigned short ) ( stbir__linear_to_srgb ( stbir__saturate ( encode_buffer [ encode_pixel_index + n ] ) ) * 65535 ) ;
{
int index = pixel_index + n ;
( ( unsigned short * ) output_buffer ) [ index ] = ( unsigned short ) ( round ( stbir__linear_to_srgb ( stbir__saturate ( encode_buffer [ index ] ) ) * 65535 ) ) ;
}
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
( ( unsigned short * ) output_buffer ) [ output_pixel_index + alpha_channel ] = ( unsigned char ) ( stbir__saturate ( encode_buffer [ encode_pixel_index + alpha_channel ] ) * 255 ) ;
( ( unsigned short * ) output_buffer ) [ pixel_index + alpha_channel ] = ( unsigned short ) ( round ( stbir__saturate ( encode_buffer [ pixel_index + alpha_channel ] ) * 65535 ) ) ;
}
}
break ;
break ;
@ -1258,50 +1360,58 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
case STBIR__DECODE ( STBIR_TYPE_UINT32 , STBIR_COLORSPACE_LINEAR ) :
case STBIR__DECODE ( STBIR_TYPE_UINT32 , STBIR_COLORSPACE_LINEAR ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned int * ) output_buffer ) [ output_pixel_index + n ] = ( unsigned int ) ( ( ( double ) stbir__saturate ( encode_buffer [ encode_pixel_index + n ] ) ) * 4294967295 ) ;
{
int index = pixel_index + n ;
( ( unsigned int * ) output_buffer ) [ index ] = ( unsigned int ) ( round ( ( ( double ) stbir__saturate ( encode_buffer [ index ] ) ) * 4294967295 ) ) ;
}
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_UINT32 , STBIR_COLORSPACE_SRGB ) :
case STBIR__DECODE ( STBIR_TYPE_UINT32 , STBIR_COLORSPACE_SRGB ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( unsigned int * ) output_buffer ) [ output_pixel_index + n ] = ( unsigned int ) ( ( ( double ) stbir__linear_to_srgb ( stbir__saturate ( encode_buffer [ encode_pixel_index + n ] ) ) ) * 4294967295 ) ;
{
int index = pixel_index + n ;
( ( unsigned int * ) output_buffer ) [ index ] = ( unsigned int ) ( round ( ( ( double ) stbir__linear_to_srgb ( stbir__saturate ( encode_buffer [ index ] ) ) ) * 4294967295 ) ) ;
}
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
( ( unsigned int * ) output_buffer ) [ output_pixel_index + alpha_channel ] = ( unsigned int ) ( ( ( double ) stbir__saturate ( encode_buffer [ encode_pixel_index + alpha_channel ] ) ) * 4294967295 ) ;
( ( unsigned int * ) output_buffer ) [ pixel_index + alpha_channel ] = ( unsigned int ) ( round ( ( ( double ) stbir__saturate ( encode_buffer [ pixel_index + alpha_channel ] ) ) * 4294967295 ) ) ;
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_FLOAT , STBIR_COLORSPACE_LINEAR ) :
case STBIR__DECODE ( STBIR_TYPE_FLOAT , STBIR_COLORSPACE_LINEAR ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( float * ) output_buffer ) [ output_pixel_index + n ] = encode_buffer [ encode_pixel_index + n ] ;
{
int index = pixel_index + n ;
( ( float * ) output_buffer ) [ index ] = encode_buffer [ index ] ;
}
}
}
break ;
break ;
case STBIR__DECODE ( STBIR_TYPE_FLOAT , STBIR_COLORSPACE_SRGB ) :
case STBIR__DECODE ( STBIR_TYPE_FLOAT , STBIR_COLORSPACE_SRGB ) :
for ( x = 0 ; x < num_pixels ; + + x )
for ( x = 0 ; x < num_pixels ; + + x )
{
{
int output_pixel_index = x * channels ;
int pixel_index = x * channels ;
int encode_pixel_index = x * channels ;
for ( n = 0 ; n < channels ; n + + )
for ( n = 0 ; n < channels ; n + + )
( ( float * ) output_buffer ) [ output_pixel_index + n ] = stbir__linear_to_srgb ( encode_buffer [ encode_pixel_index + n ] ) ;
{
int index = pixel_index + n ;
( ( float * ) output_buffer ) [ index ] = stbir__linear_to_srgb ( encode_buffer [ index ] ) ;
}
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
if ( ! ( stbir_info - > flags & STBIR_FLAG_ALPHA_USES_COLORSPACE ) )
( ( float * ) output_buffer ) [ output_pixel_index + alpha_channel ] = encode_buffer [ encode_pixel_index + alpha_channel ] ;
( ( float * ) output_buffer ) [ pixel_index + alpha_channel ] = encode_buffer [ pixel_index + alpha_channel ] ;
}
}
break ;
break ;
@ -1334,7 +1444,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
int n0 , n1 , output_row_start ;
int n0 , n1 , output_row_start ;
stbir__calculate_coefficients_upsample ( stbir_info , stbir_info - > vertical_filter , in_first_scanline , in_last_scanline , in_center_of_out , vertical_contributors , vertical_coefficients ) ;
stbir__calculate_coefficients_upsample ( stbir_info , stbir_info - > vertical_filter , stbir_info - > vertical_scale , in_first_scanline , in_last_scanline , in_center_of_out , vertical_contributors , vertical_coefficients ) ;
n0 = vertical_contributors - > n0 ;
n0 = vertical_contributors - > n0 ;
n1 = vertical_contributors - > n1 ;
n1 = vertical_contributors - > n1 ;
@ -1362,7 +1472,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
int c ;
int c ;
for ( c = 0 ; c < channels ; c + + )
for ( c = 0 ; c < channels ; c + + )
encode_buffer [ x * channels + c ] + = ring_buffer_entry [ in_pixel_index + c ] * coefficient ;
encode_buffer [ in_pixel_index + c ] + = ring_buffer_entry [ in_pixel_index + c ] * coefficient ;
}
}
}
}
stbir__encode_scanline ( stbir_info , output_w , ( char * ) output_data + output_row_start , encode_buffer , channels , alpha_channel , decode ) ;
stbir__encode_scanline ( stbir_info , output_w , ( char * ) output_data + output_row_start , encode_buffer , channels , alpha_channel , decode ) ;
@ -1421,7 +1531,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
{
{
int y ;
int y ;
float scale_ratio = stbir_info - > vertical_scale ;
float scale_ratio = stbir_info - > vertical_scale ;
float out_scanlines_radius = stbir__filter_info_table [ stbir_info - > vertical_filter ] . support * scale_ratio ;
float out_scanlines_radius = stbir__filter_info_table [ stbir_info - > vertical_filter ] . support ( 1 / scale_ratio ) * scale_ratio ;
STBIR__DEBUG_ASSERT ( stbir__use_height_upsampling ( stbir_info ) ) ;
STBIR__DEBUG_ASSERT ( stbir__use_height_upsampling ( stbir_info ) ) ;
@ -1451,7 +1561,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
else
else
{
{
stbir_info - > ring_buffer_first_scanline + + ;
stbir_info - > ring_buffer_first_scanline + + ;
stbir_info - > ring_buffer_begin_index = ( stbir_info - > ring_buffer_begin_index + 1 ) % stbir__get_filter_pixel_width_horizont al ( stbir_info ) ;
stbir_info - > ring_buffer_begin_index = ( stbir_info - > ring_buffer_begin_index + 1 ) % stbir__get_filter_pixel_width_vertic al ( stbir_info ) ;
}
}
}
}
}
}
@ -1494,6 +1604,7 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar
int output_row_start = stbir_info - > ring_buffer_first_scanline * output_stride_bytes ;
int output_row_start = stbir_info - > ring_buffer_first_scanline * output_stride_bytes ;
float * ring_buffer_entry = stbir__get_ring_buffer_entry ( ring_buffer , stbir_info - > ring_buffer_begin_index , ring_buffer_length ) ;
float * ring_buffer_entry = stbir__get_ring_buffer_entry ( ring_buffer , stbir_info - > ring_buffer_begin_index , ring_buffer_length ) ;
stbir__encode_scanline ( stbir_info , output_w , ( char * ) output_data + output_row_start , ring_buffer_entry , channels , alpha_channel , decode ) ;
stbir__encode_scanline ( stbir_info , output_w , ( char * ) output_data + output_row_start , ring_buffer_entry , channels , alpha_channel , decode ) ;
STBIR_PROGRESS_REPORT ( ( float ) stbir_info - > ring_buffer_first_scanline / stbir_info - > output_h ) ;
}
}
if ( stbir_info - > ring_buffer_first_scanline = = stbir_info - > ring_buffer_last_scanline )
if ( stbir_info - > ring_buffer_first_scanline = = stbir_info - > ring_buffer_last_scanline )
@ -1519,12 +1630,13 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
int y ;
int y ;
float scale_ratio = stbir_info - > vertical_scale ;
float scale_ratio = stbir_info - > vertical_scale ;
int output_h = stbir_info - > output_h ;
int output_h = stbir_info - > output_h ;
float in_pixels_radius = stbir__filter_info_table [ stbir_info - > vertical_filter ] . support / scale_ratio ;
float in_pixels_radius = stbir__filter_info_table [ stbir_info - > vertical_filter ] . support ( scale_ratio ) / scale_ratio ;
int max_y = stbir_info - > input_h + stbir__get_filter_pixel_margin_vertical ( stbir_info ) ;
int pixel_margin = stbir__get_filter_pixel_margin_vertical ( stbir_info ) ;
int max_y = stbir_info - > input_h + pixel_margin ;
STBIR__DEBUG_ASSERT ( ! stbir__use_height_upsampling ( stbir_info ) ) ;
STBIR__DEBUG_ASSERT ( ! stbir__use_height_upsampling ( stbir_info ) ) ;
for ( y = - stbir__get_filter_ pixel_margin_vertical ( stbir_info ) ; y < max_y ; y + + )
for ( y = - pixel_margin ; y < max_y ; y + + )
{
{
float out_center_of_in ; // Center of the current out scanline in the in scanline space
float out_center_of_in ; // Center of the current out scanline in the in scanline space
int out_first_scanline , out_last_scanline ;
int out_first_scanline , out_last_scanline ;
@ -1549,8 +1661,6 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
// Now the horizontal buffer is ready to write to all ring buffer rows.
// Now the horizontal buffer is ready to write to all ring buffer rows.
stbir__resample_vertical_downsample ( stbir_info , y , out_first_scanline , out_last_scanline , out_center_of_in ) ;
stbir__resample_vertical_downsample ( stbir_info , y , out_first_scanline , out_last_scanline , out_center_of_in ) ;
STBIR_PROGRESS_REPORT ( ( float ) ( y + stbir__get_filter_pixel_margin_vertical ( stbir_info ) ) / ( max_y + stbir__get_filter_pixel_margin_vertical ( stbir_info ) ) ) ;
}
}
stbir__empty_ring_buffer ( stbir_info , stbir_info - > output_h ) ;
stbir__empty_ring_buffer ( stbir_info , stbir_info - > output_h ) ;
@ -1601,33 +1711,36 @@ static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir
static stbir_uint32 stbir__calculate_memory ( stbir__info * info )
static stbir_uint32 stbir__calculate_memory ( stbir__info * info )
{
{
int pixel_margin = stbir__get_filter_pixel_margin ( info - > horizontal_filter , info - > input_w , info - > output_w , info - > horizontal_scale ) ;
int pixel_margin = stbir__get_filter_pixel_margin ( info - > horizontal_filter , info - > horizontal_scale ) ;
int filter_height = stbir__get_filter_pixel_width ( info - > vertical_filter , info - > input_h , info - > output_h , info - > vertical_scale ) ;
int filter_height = stbir__get_filter_pixel_width ( info - > vertical_filter , info - > vertical_scale ) ;
int contributors_size = stbir__get_horizontal_contributors ( info ) * sizeof ( stbir__contributors ) ;
info - > horizontal_ contributors_size = stbir__get_horizontal_contributors ( info ) * sizeof ( stbir__contributors ) ;
int horizontal_coefficients_size = stbir__get_total_coefficients ( info ) * sizeof ( float ) ;
info - > horizontal_coefficients_size = stbir__get_total_coefficients ( info ) * sizeof ( float ) ;
int vertical_coefficients_size = filter_height * sizeof ( float ) ;
info - > vertical_coefficients_size = filter_height * sizeof ( float ) ;
int decode_buffer_size = ( info - > input_w + pixel_margin * 2 ) * info - > channels * sizeof ( float ) ;
info - > decode_buffer_size = ( info - > input_w + pixel_margin * 2 ) * info - > channels * sizeof ( float ) ;
int horizontal_buffer_size = info - > output_w * info - > channels * sizeof ( float ) ;
info - > horizontal_buffer_size = info - > output_w * info - > channels * sizeof ( float ) ;
int ring_buffer_size = info - > output_w * info - > channels * filter_height * sizeof ( float ) ;
info - > ring_buffer_size = info - > output_w * info - > channels * filter_height * sizeof ( float ) ;
int encode_buffer_size = info - > output_w * info - > channels * sizeof ( float ) ;
info - > encode_buffer_size = info - > output_w * info - > channels * sizeof ( float ) ;
STBIR_ASSERT ( info - > horizontal_filter ! = 0 ) ;
STBIR_ASSERT ( info - > horizontal_filter ! = 0 ) ;
STBIR_ASSERT ( info - > horizontal_filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ; // this now happens too late
STBIR_ASSERT ( info - > horizontal_filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ; // this now happens too late
STBIR_ASSERT ( info - > vertical_filter ! = 0 ) ;
STBIR_ASSERT ( info - > vertical_filter ! = 0 ) ;
STBIR_ASSERT ( info - > vertical_filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ; // this now happens too late
STBIR_ASSERT ( info - > vertical_filter < STBIR__ARRAY_SIZE ( stbir__filter_info_table ) ) ; // this now happens too late
if ( stbir__use_upsampling ( info - > horizontal_scale ) )
if ( stbir__use_height_ upsampling ( info ) )
// The horizontal buffer is for when we're downsampling the height and we
// The horizontal buffer is for when we're downsampling the height and we
// can't output the result of sampling the decode buffer directly into the
// can't output the result of sampling the decode buffer directly into the
// ring buffers.
// ring buffers.
horizontal_buffer_size = 0 ;
info - > horizontal_buffer_size = 0 ;
else
else
// The encode buffer is to retain precision in the height upsampling method
// The encode buffer is to retain precision in the height upsampling method
// and isn't used when height downsampling.
// and isn't used when height downsampling.
encode_buffer_size = 0 ;
info - > encode_buffer_size = 0 ;
return contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size ;
return info - > horizontal_contributors_size + info - > horizontal_coefficients_size
+ info - > vertical_coefficients_size + info - > decode_buffer_size
+ info - > horizontal_buffer_size + info - > ring_buffer_size
+ info - > encode_buffer_size ;
}
}
static int stbir__resize_allocated ( stbir__info * info ,
static int stbir__resize_allocated ( stbir__info * info ,
@ -1707,28 +1820,28 @@ static int stbir__resize_allocated(stbir__info *info,
info - > ring_buffer_length_bytes = info - > output_w * info - > channels * sizeof ( float ) ;
info - > ring_buffer_length_bytes = info - > output_w * info - > channels * sizeof ( float ) ;
info - > decode_buffer_pixels = info - > input_w + stbir__get_filter_pixel_margin_horizontal ( info ) * 2 ;
info - > decode_buffer_pixels = info - > input_w + stbir__get_filter_pixel_margin_horizontal ( info ) * 2 ;
# define STBIR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old )
# define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size )
info - > horizontal_contributors = ( stbir__contributors * ) tempmem ;
info - > horizontal_contributors = ( stbir__contributors * ) tempmem ;
info - > horizontal_coefficients = STBIR__NEXT_MEMPTR ( info - > horizontal_contributors , stbir__get_horizontal_contributors ( info ) * sizeof ( stbir__contributors ) , float ) ;
info - > horizontal_coefficients = STBIR__NEXT_MEMPTR ( info - > horizontal_contributors , float ) ;
info - > vertical_coefficients = STBIR__NEXT_MEMPTR ( info - > horizontal_coefficients , stbir__get_total_coefficients ( info ) * sizeof ( float ) , float ) ;
info - > vertical_coefficients = STBIR__NEXT_MEMPTR ( info - > horizontal_coefficients , float ) ;
info - > decode_buffer = STBIR__NEXT_MEMPTR ( info - > vertical_coefficients , stbir__get_filter_pixel_width_vertical ( info ) * sizeof ( float ) , float ) ;
info - > decode_buffer = STBIR__NEXT_MEMPTR ( info - > vertical_coefficients , float ) ;
if ( stbir__use_height_upsampling ( info ) )
if ( stbir__use_height_upsampling ( info ) )
{
{
info - > horizontal_buffer = NULL ;
info - > horizontal_buffer = NULL ;
info - > ring_buffer = STBIR__NEXT_MEMPTR ( info - > decode_buffer , info - > decode_buffer_pixels * info - > channels * sizeof ( float ) , float ) ;
info - > ring_buffer = STBIR__NEXT_MEMPTR ( info - > decode_buffer , float ) ;
info - > encode_buffer = STBIR__NEXT_MEMPTR ( info - > ring_buffer , info - > ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal ( info ) , float ) ;
info - > encode_buffer = STBIR__NEXT_MEMPTR ( info - > ring_buffer , float ) ;
STBIR__DEBUG_ASSERT ( ( size_t ) STBIR__NEXT_MEMPTR ( info - > encode_buffer , info - > output_w * info - > channels * sizeof ( float ) , unsigned char ) = = ( size_t ) tempmem + tempmem_size_in_bytes ) ;
STBIR__DEBUG_ASSERT ( ( size_t ) STBIR__NEXT_MEMPTR ( info - > encode_buffer , unsigned char ) = = ( size_t ) tempmem + tempmem_size_in_bytes ) ;
}
}
else
else
{
{
info - > horizontal_buffer = STBIR__NEXT_MEMPTR ( info - > decode_buffer , info - > decode_buffer_pixels * info - > channels * sizeof ( float ) , float ) ;
info - > horizontal_buffer = STBIR__NEXT_MEMPTR ( info - > decode_buffer , float ) ;
info - > ring_buffer = STBIR__NEXT_MEMPTR ( info - > horizontal_buffer , info - > output_w * info - > channels * sizeof ( float ) , float ) ;
info - > ring_buffer = STBIR__NEXT_MEMPTR ( info - > horizontal_buffer , float ) ;
info - > encode_buffer = NULL ;
info - > encode_buffer = NULL ;
STBIR__DEBUG_ASSERT ( ( size_t ) STBIR__NEXT_MEMPTR ( info - > ring_buffer , info - > ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical ( info ) , unsigned char ) = = ( size_t ) tempmem + tempmem_size_in_bytes ) ;
STBIR__DEBUG_ASSERT ( ( size_t ) STBIR__NEXT_MEMPTR ( info - > ring_buffer , unsigned char ) = = ( size_t ) tempmem + tempmem_size_in_bytes ) ;
}
}
# undef STBIR__NEXT_MEMPTR
# undef STBIR__NEXT_MEMPTR
@ -1738,11 +1851,15 @@ static int stbir__resize_allocated(stbir__info *info,
stbir__calculate_horizontal_filters ( info ) ;
stbir__calculate_horizontal_filters ( info ) ;
STBIR_PROGRESS_REPORT ( 0 ) ;
if ( stbir__use_height_upsampling ( info ) )
if ( stbir__use_height_upsampling ( info ) )
stbir__buffer_loop_upsample ( info ) ;
stbir__buffer_loop_upsample ( info ) ;
else
else
stbir__buffer_loop_downsample ( info ) ;
stbir__buffer_loop_downsample ( info ) ;
STBIR_PROGRESS_REPORT ( 1 ) ;
# ifdef STBIR_DEBUG_OVERWRITE_TEST
# ifdef STBIR_DEBUG_OVERWRITE_TEST
STBIR__DEBUG_ASSERT ( memcmp ( overwrite_output_before_pre , & ( ( unsigned char * ) output_data ) [ - OVERWRITE_ARRAY_SIZE ] , OVERWRITE_ARRAY_SIZE ) = = 0 ) ;
STBIR__DEBUG_ASSERT ( memcmp ( overwrite_output_before_pre , & ( ( unsigned char * ) output_data ) [ - OVERWRITE_ARRAY_SIZE ] , OVERWRITE_ARRAY_SIZE ) = = 0 ) ;
STBIR__DEBUG_ASSERT ( memcmp ( overwrite_output_after_pre , & ( ( unsigned char * ) output_data ) [ begin_forbidden ] , OVERWRITE_ARRAY_SIZE ) = = 0 ) ;
STBIR__DEBUG_ASSERT ( memcmp ( overwrite_output_after_pre , & ( ( unsigned char * ) output_data ) [ begin_forbidden ] , OVERWRITE_ARRAY_SIZE ) = = 0 ) ;
@ -1772,7 +1889,7 @@ static int stbir__resize_arbitrary(
stbir__calculate_transform ( & info , s0 , t0 , s1 , t1 , transform ) ;
stbir__calculate_transform ( & info , s0 , t0 , s1 , t1 , transform ) ;
stbir__choose_filter ( & info , h_filter , v_filter ) ;
stbir__choose_filter ( & info , h_filter , v_filter ) ;
memory_required = stbir__calculate_memory ( & info ) ;
memory_required = stbir__calculate_memory ( & info ) ;
extra_memory = STBIR_MALLOC ( alloc_context , memory_required ) ;
extra_memory = STBIR_MALLOC ( memory_required , alloc_context ) ;
if ( ! extra_memory )
if ( ! extra_memory )
return 0 ;
return 0 ;
@ -1783,7 +1900,7 @@ static int stbir__resize_arbitrary(
edge_horizontal , edge_vertical ,
edge_horizontal , edge_vertical ,
colorspace , extra_memory , memory_required ) ;
colorspace , extra_memory , memory_required ) ;
STBIR_FREE ( alloc_context , extra_memory ) ;
STBIR_FREE ( extra_memory , alloc_context ) ;
return result ;
return result ;
}
}