Compare commits

...

11 Commits

Author SHA1 Message Date
Richard Gill 553d9134e7
Merge 4063864f1d into ae721c50ea 2024-03-19 01:10:47 -03:00
Sean Barrett ae721c50ea
Merge pull request #1609 from jeffrbig2/fix_coeffs
Fix bug in coeff gen on more than 3x downsamples with w and h equal
2024-02-12 23:49:35 -08:00
Jeff Roberts (Bellevue) 2fb057af65 remove test 2024-02-12 22:14:27 -08:00
Jeff Roberts (Bellevue) 1828f357dc Fix bug in coeff generation on more than 3x downsamples with width and height scale equal 2024-02-12 22:10:02 -08:00
Sean Barrett b7cf124628 stb_image: fix VC6 2024-02-08 13:24:06 -08:00
Jeff Roberts (Bellevue) c59da6729e Mark row0 as unused 2024-02-08 12:53:51 -08:00
Jeff Roberts (Bellevue) 7f7e3469cf clean up comments 2024-02-08 10:36:54 -08:00
Jeff Roberts (Bellevue) 7a075fe7c7 Fix 2 pixel to 1 pixel with wrap
Fix output buffer for output callback
2024-02-04 14:42:51 -08:00
Richard Gill 4063864f1d updated stbds_.h doc for stbds_arrctx 2023-04-18 22:29:50 +02:00
Richard Gill 38a9ead0f6 stbds_arrctx() context for custom realloc/free
Setting a user context with stbds_arrctx() permits custom allocator
functions (STBDS_REALLOC() and STBDS_FREE()) to forward that context.
The array might be allocated during the call if it was not.
Hence it's preferable to reassign the pointer on function return:

my_array = stbds_arrctx(my_array, my_context);
2023-04-18 22:24:24 +02:00
Richard Gill 200f6d0024 stbds_arrctx() context for custom realloc/free 2023-04-17 16:37:11 +02:00
2 changed files with 102 additions and 56 deletions

View File

@ -48,8 +48,8 @@ COMPILE-TIME OPTIONS
By default stb_ds uses stdlib realloc() and free() for memory management. You can
substitute your own functions instead by defining these symbols. You must either
define both, or neither. Note that at the moment, 'context' will always be NULL.
@TODO add an array/hash initialization function that takes a memory context pointer.
define both, or neither. To forward 'context' to those functions, call stbds_arrctx().
@TODO add an hash initialization function that takes a memory context pointer.
#define STBDS_UNIT_TESTS
@ -371,6 +371,7 @@ CREDITS
Per Vognsen -- idea for hash table API/implementation
Rafael Sachetto -- arrpop()
github:HeroicKatora -- arraddn() reworking
github:riri -- arrctx() context for custom realloc/free
Bugfixes:
Andy Durdin
@ -413,6 +414,7 @@ CREDITS
#define arrdelswap stbds_arrdelswap
#define arrcap stbds_arrcap
#define arrsetcap stbds_arrsetcap
#define arrctx stbds_arrctx
#define hmput stbds_hmput
#define hmputs stbds_hmputs
@ -491,7 +493,7 @@ extern void stbds_unit_tests(void);
// Everything below here is implementation details
//
extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap, void *ctx);
extern void stbds_arrfreef(void *a);
extern void stbds_hmfree_func(void *p, size_t elemsize);
extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
@ -534,31 +536,33 @@ extern void * stbds_shmode_func(size_t elemsize, int mode);
#define stbds_header(t) ((stbds_array_header *) (t) - 1)
#define stbds_temp(t) stbds_header(t)->temp
#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
#define stbds_context(t) ((t) ? stbds_header(t)->context : NULL)
#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n))
#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n,stbds_context(a)))
#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0)
#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0)
#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1,stbds_context(a)), (a)[stbds_header(a)->length++] = (v))
#define stbds_arrpush stbds_arrput // synonym
#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length])
#define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead:
#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n,stbds_context(a)), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n,stbds_context(a)), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
#define stbds_arraddnoff stbds_arraddnindex
#define stbds_arrlast(a) ((a)[stbds_header(a)->length-1])
#define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
#define stbds_arrfree(a) ((void) ((a) ? stbds_arrfreef(a) : (void)0), (a)=NULL)
#define stbds_arrdel(a,i) stbds_arrdeln(a,i,1)
#define stbds_arrdeln(a,i,n) (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
#define stbds_arrinsn(a,i,n) (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
#define stbds_arrins(a,i,v) (stbds_arrinsn((a),(i),1), (a)[i]=(v))
#define stbds_arrctx(a,c) (stbds_arrmaybegrow(a,0,c), stbds_header(a)->context = (c), (a))
#define stbds_arrmaybegrow(a,n) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
? (stbds_arrgrow(a,n,0),0) : 0)
#define stbds_arrmaybegrow(a,n,x) ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
? (stbds_arrgrow(a,n,0,x),0) : 0)
#define stbds_arrgrow(a,b,c) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
#define stbds_arrgrow(a,b,c,x) ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c), (x)))
#define stbds_hmput(t, k, v) \
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0), \
@ -660,6 +664,7 @@ typedef struct
size_t capacity;
void * hash_table;
ptrdiff_t temp;
void * context;
} stbds_array_header;
typedef struct stbds_string_block
@ -690,8 +695,8 @@ enum
#ifdef __cplusplus
// in C we use implicit assignment from these void*-returning functions to T*.
// in C++ these templates make the same code work
template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap, void *ctx) {
return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap, ctx);
}
template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
@ -759,7 +764,7 @@ size_t stbds_rehash_items;
//int *prev_allocs[65536];
//int num_prev;
void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap, void *ctx)
{
stbds_array_header temp={0}; // force debugging
void *b;
@ -770,7 +775,7 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
if (min_len > min_cap)
min_cap = min_len;
if (min_cap <= stbds_arrcap(a))
if (a && min_cap <= stbds_arrcap(a))
return a;
// increase needed capacity to guarantee O(1) amortized
@ -782,13 +787,14 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
//if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
//if (num_prev == 2201)
// num_prev = num_prev;
b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
b = STBDS_REALLOC(ctx, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
//if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
b = (char *) b + sizeof(stbds_array_header);
if (a == NULL) {
stbds_header(b)->length = 0;
stbds_header(b)->hash_table = 0;
stbds_header(b)->temp = 0;
stbds_header(b)->context = ctx;
} else {
STBDS_STATS(++stbds_array_grow);
}
@ -799,7 +805,7 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
void stbds_arrfreef(void *a)
{
STBDS_FREE(NULL, stbds_header(a));
STBDS_FREE(stbds_context(a), stbds_header(a));
}
//
@ -1283,7 +1289,7 @@ void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, p
size_t keyoffset = 0;
if (a == NULL) {
// make it non-empty so we can return a temp
a = stbds_arrgrowf(0, elemsize, 0, 1);
a = stbds_arrgrowf(0, elemsize, 0, 1, NULL);
stbds_header(a)->length += 1;
memset(a, 0, elemsize);
*temp = STBDS_INDEX_EMPTY;
@ -1324,7 +1330,7 @@ void * stbds_hmput_default(void *a, size_t elemsize)
// a has a hash table but no entries, because of shmode <- grow
// a has entries <- do nothing
if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1, NULL);
stbds_header(a)->length += 1;
memset(a, 0, elemsize);
a=STBDS_ARR_TO_HASH(a,elemsize);
@ -1341,7 +1347,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m
stbds_hash_index *table;
if (a == NULL) {
a = stbds_arrgrowf(0, elemsize, 0, 1);
a = stbds_arrgrowf(0, elemsize, 0, 1, NULL);
memset(a, 0, elemsize);
stbds_header(a)->length += 1;
// adjust a to point AFTER the default element
@ -1437,7 +1443,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m
ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
// we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
if ((size_t) i+1 > stbds_arrcap(a))
*(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
*(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0, NULL);
raw_a = STBDS_ARR_TO_HASH(a,elemsize);
STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
@ -1460,7 +1466,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m
void * stbds_shmode_func(size_t elemsize, int mode)
{
void *a = stbds_arrgrowf(0, elemsize, 0, 1);
void *a = stbds_arrgrowf(0, elemsize, 0, 1, NULL);
stbds_hash_index *h;
memset(a, 0, elemsize);
stbds_header(a)->length = 1;

View File

@ -1,4 +1,4 @@
/* stb_image_resize2 - v2.04 - public domain image resizing
/* stb_image_resize2 - v2.06 - public domain image resizing
by Jeff Roberts (v2) and Jorge L Rodriguez
http://github.com/nothings/stb
@ -324,11 +324,15 @@
Fabian Giesen: half float and srgb converters
Sean Barrett: API design, optimizations
Jorge L Rodriguez: Original 1.0 implementation
Aras Pranckevicius: bugfixes for 1.0
Aras Pranckevicius: bugfixes
Nathan Reed: warning fixes for 1.0
REVISIONS
2.04 (2023-11-17) Fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic).
2.06 (2024-02-10) fix for indentical width/height 3x or more down-scaling
undersampling a single row on rare resize ratios (about 1%)
2.05 (2024-02-07) fix for 2 pixel to 1 pixel resizes with wrap (thanks Aras)
fix for output callback (thanks Julien Koenen)
2.04 (2023-11-17) fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic).
2.03 (2023-11-01) ASAN and TSAN warnings fixed, minor tweaks.
2.00 (2023-10-10) mostly new source: new api, optimizations, simd, vertical-first, etc
(2x-5x faster without simd, 4x-12x faster with simd)
@ -3200,8 +3204,8 @@ static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel
if ( edge == STBIR_EDGE_WRAP )
{
if ( first <= -input_size )
first = -(input_size-1);
if ( first < -input_size )
first = -input_size;
if ( last >= (input_size*2))
last = (input_size*2) - 1;
}
@ -3392,6 +3396,12 @@ static void stbir__calculate_coefficients_for_gather_downsample( int start, int
}
}
#ifdef STBIR_RENORMALIZE_IN_FLOAT
#define STBIR_RENORM_TYPE float
#else
#define STBIR_RENORM_TYPE double
#endif
static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficient_group, int coefficient_width )
{
int input_size = scale_info->input_full_size;
@ -3413,14 +3423,14 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
for (n = 0; n < end; n++)
{
int i;
float filter_scale, total_filter = 0;
STBIR_RENORM_TYPE filter_scale, total_filter = 0;
int e;
// add all contribs
e = contribs->n1 - contribs->n0;
for( i = 0 ; i <= e ; i++ )
{
total_filter += coeffs[i];
total_filter += (STBIR_RENORM_TYPE) coeffs[i];
STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights
}
@ -3436,10 +3446,11 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
// if the total isn't 1.0, rescale everything
if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) )
{
filter_scale = 1.0f / total_filter;
filter_scale = ((STBIR_RENORM_TYPE)1.0) / total_filter;
// scale them all
for (i = 0; i <= e; i++)
coeffs[i] *= filter_scale;
coeffs[i] = (float) ( coeffs[i] * filter_scale );
}
}
++contribs;
@ -3560,7 +3571,9 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
filter_info->widest = widest;
}
static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row_width )
#undef STBIR_RENORM_TYPE
static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row0, int row1 )
{
#define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; }
#define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; }
@ -3569,6 +3582,10 @@ static int stbir__pack_coefficients( int num_contributors, stbir__contributors*
#else
#define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; }
#endif
int row_end = row1 + 1;
STBIR__UNUSED( row0 ); // only used in an assert
if ( coefficient_width != widest )
{
float * pc = coefficents;
@ -3710,10 +3727,10 @@ static int stbir__pack_coefficients( int num_contributors, stbir__contributors*
float * coeffs = coefficents + widest * ( num_contributors - 1 );
// go until no chance of clipping (this is usually less than 8 lops)
while ( ( contribs >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_width ) )
while ( ( contribs >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_end ) )
{
// might we clip??
if ( ( contribs->n0 + widest ) > row_width )
if ( ( contribs->n0 + widest ) > row_end )
{
int stop_range = widest;
@ -3732,15 +3749,15 @@ static int stbir__pack_coefficients( int num_contributors, stbir__contributors*
}
// now see if we still clip with the refined range
if ( ( contribs->n0 + stop_range ) > row_width )
if ( ( contribs->n0 + stop_range ) > row_end )
{
int new_n0 = row_width - stop_range;
int new_n0 = row_end - stop_range;
int num = contribs->n1 - contribs->n0 + 1;
int backup = contribs->n0 - new_n0;
float * from_co = coeffs + num - 1;
float * to_co = from_co + backup;
STBIR_ASSERT( ( new_n0 >= 0 ) && ( new_n0 < contribs->n0 ) );
STBIR_ASSERT( ( new_n0 >= row0 ) && ( new_n0 < contribs->n0 ) );
// move the coeffs over
while( num )
@ -3863,26 +3880,33 @@ static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * ot
for (k = gn0 ; k <= gn1 ; k++ )
{
float gc = *g_coeffs++;
if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) )
// skip zero and denormals - must skip zeros to avoid adding coeffs beyond scatter_coefficient_width
// (which happens when pivoting from horizontal, which might have dummy zeros)
if ( ( ( gc >= stbir__small_float ) || ( gc <= -stbir__small_float ) ) )
{
if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) )
{
// if we are skipping over several contributors, we need to clear the skipped ones
stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1);
while ( clear_contributors < scatter_contributors )
{
clear_contributors->n0 = 0;
clear_contributors->n1 = -1;
++clear_contributors;
// if we are skipping over several contributors, we need to clear the skipped ones
stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1);
while ( clear_contributors < scatter_contributors )
{
clear_contributors->n0 = 0;
clear_contributors->n1 = -1;
++clear_contributors;
}
}
scatter_contributors->n0 = n;
scatter_contributors->n1 = n;
scatter_coeffs[0] = gc;
highest_set = k;
}
scatter_contributors->n0 = n;
scatter_contributors->n1 = n;
scatter_coeffs[0] = gc;
highest_set = k;
}
else
{
stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc );
else
{
stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc );
}
STBIR_ASSERT( ( scatter_contributors->n1 - scatter_contributors->n0 + 1 ) <= scatter_coefficient_width );
}
++scatter_contributors;
scatter_coeffs += scatter_coefficient_width;
@ -5958,7 +5982,7 @@ static void stbir__encode_scanline( stbir__info const * stbir_info, void *output
// if we have an output callback, call it to send the data
if ( stbir_info->out_pixels_cb )
stbir_info->out_pixels_cb( output_buffer_data, num_pixels, row, stbir_info->user_data );
stbir_info->out_pixels_cb( output_buffer, num_pixels, row, stbir_info->user_data );
}
@ -6352,15 +6376,31 @@ static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir
// pre calculate stuff based on the above
samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data);
// filter_pixel_width is the conservative size in pixels of input that affect an output pixel.
// In rare cases (only with 2 pix to 1 pix with the default filters), it's possible that the
// filter will extend before or after the scanline beyond just one extra entire copy of the
// scanline (we would hit the edge twice). We don't let you do that, so we clamp the total
// width to 3x the total of input pixel (once for the scanline, once for the left side
// overhang, and once for the right side). We only do this for edge mode, since the other
// modes can just re-edge clamp back in again.
if ( edge == STBIR_EDGE_WRAP )
if ( samp->filter_pixel_width > ( scale_info->input_full_size * 2 ) ) // this can only happen when shrinking to a single pixel
samp->filter_pixel_width = scale_info->input_full_size * 2;
if ( samp->filter_pixel_width > ( scale_info->input_full_size * 3 ) )
samp->filter_pixel_width = scale_info->input_full_size * 3;
// This is how much to expand buffers to account for filters seeking outside
// the image boundaries.
samp->filter_pixel_margin = samp->filter_pixel_width / 2;
// filter_pixel_margin is the amount that this filter can overhang on just one side of either
// end of the scanline (left or the right). Since we only allow you to overhang 1 scanline's
// worth of pixels, we clamp this one side of overhang to the input scanline size. Again,
// this clamping only happens in rare cases with the default filters (2 pix to 1 pix).
if ( edge == STBIR_EDGE_WRAP )
if ( samp->filter_pixel_margin > scale_info->input_full_size )
samp->filter_pixel_margin = scale_info->input_full_size;
samp->num_contributors = stbir__get_contributors(samp, samp->is_gather);
samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors);
samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding
@ -6996,7 +7036,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample
stbir__get_extents( horizontal, &info->scanline_extents );
// pack the horizontal coeffs
horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n1 + 1 );
horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n0, info->scanline_extents.conservative.n1 );
STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) );