1
- /* stb_image_resize2 - v2.10 - public domain image resizing
1
+ /* stb_image_resize2 - v2.11 - public domain image resizing
2
2
3
3
by Jeff Roberts (v2) and Jorge L Rodriguez
4
4
http://github.com/nothings/stb
11
11
#define STB_IMAGE_RESIZE_IMPLEMENTATION
12
12
before the #include. That will create the implementation in that file.
13
13
14
- PORTING FROM VERSION 1
15
-
16
- The API has changed. You can continue to use the old version of stb_image_resize.h,
17
- which is available in the "deprecated/" directory.
18
-
19
- If you're using the old simple-to-use API, porting is straightforward.
20
- (For more advanced APIs, read the documentation.)
21
-
22
- stbir_resize_uint8():
23
- - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout`
24
-
25
- stbir_resize_float():
26
- - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout`
27
-
28
- stbir_resize_uint8_srgb():
29
- - function name is unchanged
30
- - cast channel count to `stbir_pixel_layout`
31
- - above is sufficient unless your image has alpha and it's not RGBA/BGRA
32
- - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode
33
-
34
- stbir_resize_uint8_srgb_edgemode()
35
- - switch to the "medium complexity" API
36
- - stbir_resize(), very similar API but a few more parameters:
37
- - pixel_layout: cast channel count to `stbir_pixel_layout`
38
- - data_type: STBIR_TYPE_UINT8_SRGB
39
- - edge: unchanged (STBIR_EDGE_WRAP, etc.)
40
- - filter: STBIR_FILTER_DEFAULT
41
- - which channel is alpha is specified in stbir_pixel_layout, see enum for details
42
-
43
14
EASY API CALLS:
44
15
Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge.
45
16
296
267
ASSERT
297
268
Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
298
269
270
+ PORTING FROM VERSION 1
271
+ The API has changed. You can continue to use the old version of stb_image_resize.h,
272
+ which is available in the "deprecated/" directory.
273
+
274
+ If you're using the old simple-to-use API, porting is straightforward.
275
+ (For more advanced APIs, read the documentation.)
276
+
277
+ stbir_resize_uint8():
278
+ - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout`
279
+
280
+ stbir_resize_float():
281
+ - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout`
282
+
283
+ stbir_resize_uint8_srgb():
284
+ - function name is unchanged
285
+ - cast channel count to `stbir_pixel_layout`
286
+ - above is sufficient unless your image has alpha and it's not RGBA/BGRA
287
+ - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode
288
+
289
+ stbir_resize_uint8_srgb_edgemode()
290
+ - switch to the "medium complexity" API
291
+ - stbir_resize(), very similar API but a few more parameters:
292
+ - pixel_layout: cast channel count to `stbir_pixel_layout`
293
+ - data_type: STBIR_TYPE_UINT8_SRGB
294
+ - edge: unchanged (STBIR_EDGE_WRAP, etc.)
295
+ - filter: STBIR_FILTER_DEFAULT
296
+ - which channel is alpha is specified in stbir_pixel_layout, see enum for details
297
+
299
298
FUTURE TODOS
300
299
* For polyphase integral filters, we just memcpy the coeffs to dupe
301
300
them, but we should indirect and use the same coeff memory.
328
327
Nathan Reed: warning fixes for 1.0
329
328
330
329
REVISIONS
330
+ 2.11 (2024-09-08) fix harmless asan warnings in 2-channel and 3-channel mode
331
+ with AVX-2, fix some weird scaling edge conditions with
332
+ point sample mode.
331
333
2.10 (2024-07-27) fix the defines GCC and mingw for loop unroll control,
332
334
fix MSVC 32-bit arm half float routines.
333
335
2.09 (2024-06-19) fix the defines for 32-bit ARM GCC builds (was selecting
@@ -3247,6 +3249,7 @@ static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel
3247
3249
3248
3250
first = (int )(STBIR_FLOORF (in_pixel_influence_lowerbound + 0.5f ));
3249
3251
last = (int )(STBIR_FLOORF (in_pixel_influence_upperbound - 0.5f ));
3252
+ if ( last < first ) last = first ; // point sample mode can span a value *right* at 0.5, and cause these to cross
3250
3253
3251
3254
if ( edge == STBIR_EDGE_WRAP )
3252
3255
{
@@ -3282,6 +3285,11 @@ static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_
3282
3285
3283
3286
stbir__calculate_in_pixel_range ( & in_first_pixel , & in_last_pixel , out_pixel_center , out_filter_radius , inv_scale , out_shift , input_size , edge );
3284
3287
3288
+ // make sure we never generate a range larger than our precalculated coeff width
3289
+ // this only happens in point sample mode, but it's a good safe thing to do anyway
3290
+ if ( ( in_last_pixel - in_first_pixel + 1 ) > coefficient_width )
3291
+ in_last_pixel = in_first_pixel + coefficient_width - 1 ;
3292
+
3285
3293
last_non_zero = -1 ;
3286
3294
for (i = 0 ; i <= in_last_pixel - in_first_pixel ; i ++ )
3287
3295
{
@@ -3317,19 +3325,22 @@ static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_
3317
3325
}
3318
3326
}
3319
3327
3320
- static void stbir__insert_coeff ( stbir__contributors * contribs , float * coeffs , int new_pixel , float new_coeff )
3328
+ static void stbir__insert_coeff ( stbir__contributors * contribs , float * coeffs , int new_pixel , float new_coeff , int max_width )
3321
3329
{
3322
3330
if ( new_pixel <= contribs -> n1 ) // before the end
3323
3331
{
3324
3332
if ( new_pixel < contribs -> n0 ) // before the front?
3325
3333
{
3326
- int j , o = contribs -> n0 - new_pixel ;
3327
- for ( j = contribs -> n1 - contribs -> n0 ; j <= 0 ; j -- )
3328
- coeffs [ j + o ] = coeffs [ j ];
3329
- for ( j = 1 ; j < o ; j -- )
3330
- coeffs [ j ] = coeffs [ 0 ];
3331
- coeffs [ 0 ] = new_coeff ;
3332
- contribs -> n0 = new_pixel ;
3334
+ if ( ( contribs -> n1 - new_pixel + 1 ) <= max_width )
3335
+ {
3336
+ int j , o = contribs -> n0 - new_pixel ;
3337
+ for ( j = contribs -> n1 - contribs -> n0 ; j <= 0 ; j -- )
3338
+ coeffs [ j + o ] = coeffs [ j ];
3339
+ for ( j = 1 ; j < o ; j -- )
3340
+ coeffs [ j ] = coeffs [ 0 ];
3341
+ coeffs [ 0 ] = new_coeff ;
3342
+ contribs -> n0 = new_pixel ;
3343
+ }
3333
3344
}
3334
3345
else
3335
3346
{
@@ -3338,12 +3349,15 @@ static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs,
3338
3349
}
3339
3350
else
3340
3351
{
3341
- int j , e = new_pixel - contribs -> n0 ;
3342
- for ( j = ( contribs -> n1 - contribs -> n0 ) + 1 ; j < e ; j ++ ) // clear in-betweens coeffs if there are any
3343
- coeffs [j ] = 0 ;
3352
+ if ( ( new_pixel - contribs -> n0 + 1 ) <= max_width )
3353
+ {
3354
+ int j , e = new_pixel - contribs -> n0 ;
3355
+ for ( j = ( contribs -> n1 - contribs -> n0 ) + 1 ; j < e ; j ++ ) // clear in-betweens coeffs if there are any
3356
+ coeffs [j ] = 0 ;
3344
3357
3345
- coeffs [ e ] = new_coeff ;
3346
- contribs -> n1 = new_pixel ;
3358
+ coeffs [ e ] = new_coeff ;
3359
+ contribs -> n1 = new_pixel ;
3360
+ }
3347
3361
}
3348
3362
}
3349
3363
@@ -3522,6 +3536,7 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
3522
3536
3523
3537
coeffs = coefficient_group ;
3524
3538
contribs = contributors ;
3539
+
3525
3540
for (n = 0 ; n < num_contributors ; n ++ )
3526
3541
{
3527
3542
int i ;
@@ -3561,7 +3576,7 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
3561
3576
int endi = contribs -> n1 ;
3562
3577
contribs -> n1 = input_last_n1 ;
3563
3578
for ( i = input_size ; i <= endi ; i ++ )
3564
- stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( i , input_size ), coeffs [i - start ] );
3579
+ stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( i , input_size ), coeffs [i - start ], coefficient_width );
3565
3580
}
3566
3581
3567
3582
// now check left hand edge
@@ -3573,7 +3588,7 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
3573
3588
3574
3589
// reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist)
3575
3590
for ( i = -1 ; i > contribs -> n0 ; i -- )
3576
- stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( i , input_size ), * c -- );
3591
+ stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( i , input_size ), * c -- , coefficient_width );
3577
3592
save_n0 = contribs -> n0 ;
3578
3593
save_n0_coeff = c [0 ]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)!
3579
3594
@@ -3583,7 +3598,7 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
3583
3598
coeffs [i ] = coeffs [i - save_n0 ];
3584
3599
3585
3600
// now that we have shrunk down the contribs, we insert the first one safely
3586
- stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( save_n0 , input_size ), save_n0_coeff );
3601
+ stbir__insert_coeff ( contribs , coeffs , stbir__edge_wrap_slow [edge ]( save_n0 , input_size ), save_n0_coeff , coefficient_width );
3587
3602
}
3588
3603
}
3589
3604
@@ -3592,6 +3607,7 @@ static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter
3592
3607
int diff = contribs -> n1 - contribs -> n0 + 1 ;
3593
3608
while ( diff && ( coeffs [ diff - 1 ] == 0.0f ) )
3594
3609
-- diff ;
3610
+
3595
3611
contribs -> n1 = contribs -> n0 + diff - 1 ;
3596
3612
3597
3613
if ( contribs -> n0 <= contribs -> n1 )
@@ -3964,7 +3980,7 @@ static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * ot
3964
3980
}
3965
3981
else
3966
3982
{
3967
- stbir__insert_coeff ( scatter_contributors , scatter_coeffs , n , gc );
3983
+ stbir__insert_coeff ( scatter_contributors , scatter_coeffs , n , gc , scatter_coefficient_width );
3968
3984
}
3969
3985
STBIR_ASSERT ( ( scatter_contributors -> n1 - scatter_contributors -> n0 + 1 ) <= scatter_coefficient_width );
3970
3986
}
@@ -4810,12 +4826,13 @@ static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float
4810
4826
stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
4811
4827
4812
4828
#define stbir__1_coeff_remnant ( ofs ) \
4813
- { stbir__simdf t; \
4829
+ { stbir__simdf t,d; \
4814
4830
stbir__simdf_load1z( t, hc + (ofs) ); \
4831
+ stbir__simdf_load2( d, decode + (ofs) * 2 ); \
4815
4832
stbir__simdf_0123to0011( t, t ); \
4816
- stbir__simdf_mult_mem ( t, t, decode+(ofs)*2 ); \
4833
+ stbir__simdf_mult ( t, t, d ); \
4817
4834
stbir__simdf8_add4( tot0, tot0, t ); }
4818
-
4835
+
4819
4836
#define stbir__2_coeff_remnant ( ofs ) \
4820
4837
{ stbir__simdf t; \
4821
4838
stbir__simdf_load2( t, hc + (ofs) ); \
@@ -7112,6 +7129,11 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample
7112
7129
7113
7130
#ifdef STBIR__SEPARATE_ALLOCATIONS
7114
7131
temp_mem_amt = decode_buffer_size ;
7132
+
7133
+ #ifdef STBIR_SIMD8
7134
+ if ( effective_channels == 3 )
7135
+ -- temp_mem_amt ; // avx in 3 channel mode needs one float at the start of the buffer
7136
+ #endif
7115
7137
#else
7116
7138
temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits ;
7117
7139
#endif
@@ -7217,6 +7239,12 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample
7217
7239
int t , ofs , start ;
7218
7240
7219
7241
ofs = decode_buffer_size / 4 ;
7242
+
7243
+ #if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8 )
7244
+ if ( effective_channels == 3 )
7245
+ -- ofs ; // avx in 3 channel mode needs one float at the start of the buffer, so we snap back for clearing
7246
+ #endif
7247
+
7220
7248
start = ofs - 4 ;
7221
7249
if ( start < 0 ) start = 0 ;
7222
7250
0 commit comments