1- /* stb_image_resize2 - v2.15 - public domain image resizing
1+ /* stb_image_resize2 - v2.17 - public domain image resizing
22
33 by Jeff Roberts (v2) and Jorge L Rodriguez
44 http://github.com/nothings/stb
329329 Nathan Reed: warning fixes for 1.0
330330
331331 REVISIONS
332+ 2.17 (2025-10-25) silly format bug in easy-to-use APIs.
333+ 2.16 (2025-10-21) fixed the easy-to-use APIs to allow inverted bitmaps (negative
334+ strides), fix vertical filter kernel callback, fix threaded
335+ gather buffer priming (and assert).
336+ (thanks adipose, TainZerL, and Harrison Green)
332337 2.15 (2025-07-17) fixed an assert in debug mode when using floats with input
333338 callbacks, work around GCC warning when adding to null ptr
334339 (thanks Johannes Spohr and Pyry Kovanen).
@@ -6711,7 +6716,7 @@ static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contr
67116716 }
67126717}
67136718
6714- static void stbir__get_split_info ( stbir__per_split_info * split_info , int splits , int output_height , int vertical_pixel_margin , int input_full_height )
6719+ static void stbir__get_split_info ( stbir__per_split_info * split_info , int splits , int output_height , int vertical_pixel_margin , int input_full_height , int is_gather , stbir__contributors * contribs )
67156720{
67166721 int i , cur ;
67176722 int left = output_height ;
@@ -6720,9 +6725,58 @@ static void stbir__get_split_info( stbir__per_split_info* split_info, int splits
67206725 for ( i = 0 ; i < splits ; i ++ )
67216726 {
67226727 int each ;
6728+
67236729 split_info [i ].start_output_y = cur ;
67246730 each = left / ( splits - i );
67256731 split_info [i ].end_output_y = cur + each ;
6732+
6733+ // ok, when we are gathering, we need to make sure we are starting on a y offset that doesn't have
6734+ // a "special" set of coefficients. Basically, with exactly the right filter at exactly the right
6735+ // resize at exactly the right phase, some of the coefficents can be zero. When they are zero, we
6736+ // don't process them at all. But this leads to a tricky thing with the thread splits, where we
6737+ // might have a set of two coeffs like this for example: (4,4) and (3,6). The 4,4 means there was
6738+ // just one single coeff because things worked out perfectly (normally, they all have 4 coeffs
6739+ // like the range 3,6. The problem is that if we start right on the (4,4) on a brand new thread,
6740+ // then when we get to (3,6), we don't have the "3" sample in memory (because we didn't load
6741+ // it on the initial (4,4) range because it didn't have a 3 (we only add new samples that are
6742+ // larger than our existing samples - it's just how the eviction works). So, our solution here
6743+ // is pretty simple, if we start right on a range that has samples that start earlier, then we
6744+ // simply bump up our previous thread split range to include it, and then start this threads
6745+ // range with the smaller sample. It just moves one scanline from one thread split to another,
6746+ // so that we end with the unusual one, instead of start with it. To do this, we check 2-4
6747+ // sample at each thread split start and then occassionally move them.
6748+
6749+ if ( ( is_gather ) && ( i ) )
6750+ {
6751+ stbir__contributors * small_contribs ;
6752+ int j , smallest , stop , start_n0 ;
6753+ stbir__contributors * split_contribs = contribs + cur ;
6754+
6755+ // scan for a max of 3x the filter width or until the next thread split
6756+ stop = vertical_pixel_margin * 3 ;
6757+ if ( each < stop )
6758+ stop = each ;
6759+
6760+ // loops a few times before early out
6761+ smallest = 0 ;
6762+ small_contribs = split_contribs ;
6763+ start_n0 = small_contribs -> n0 ;
6764+ for ( j = 1 ; j <= stop ; j ++ )
6765+ {
6766+ ++ split_contribs ;
6767+ if ( split_contribs -> n0 > start_n0 )
6768+ break ;
6769+ if ( split_contribs -> n0 < small_contribs -> n0 )
6770+ {
6771+ small_contribs = split_contribs ;
6772+ smallest = j ;
6773+ }
6774+ }
6775+
6776+ split_info [i - 1 ].end_output_y += smallest ;
6777+ split_info [i ].start_output_y += smallest ;
6778+ }
6779+
67266780 cur += each ;
67276781 left -= each ;
67286782
@@ -7269,7 +7323,7 @@ static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sample
72697323 }
72707324
72717325 // setup the vertical split ranges
7272- stbir__get_split_info ( info -> split_info , info -> splits , info -> vertical .scale_info .output_sub_size , info -> vertical .filter_pixel_margin , info -> vertical .scale_info .input_full_size );
7326+ stbir__get_split_info ( info -> split_info , info -> splits , info -> vertical .scale_info .output_sub_size , info -> vertical .filter_pixel_margin , info -> vertical .scale_info .input_full_size , info -> vertical . is_gather , info -> vertical . contributors );
72737327
72747328 // now we know precisely how many entries we need
72757329 info -> ring_buffer_num_entries = info -> vertical .extent_info .widest ;
@@ -7833,7 +7887,7 @@ static int stbir__perform_build( STBIR_RESIZE * resize, int splits )
78337887
78347888 stbir__set_sampler (& horizontal , resize -> horizontal_filter , resize -> horizontal_filter_kernel , resize -> horizontal_filter_support , resize -> horizontal_edge , & horizontal .scale_info , 1 , resize -> user_data );
78357889 stbir__get_conservative_extents ( & horizontal , & conservative , resize -> user_data );
7836- stbir__set_sampler (& vertical , resize -> vertical_filter , resize -> horizontal_filter_kernel , resize -> vertical_filter_support , resize -> vertical_edge , & vertical .scale_info , 0 , resize -> user_data );
7890+ stbir__set_sampler (& vertical , resize -> vertical_filter , resize -> vertical_filter_kernel , resize -> vertical_filter_support , resize -> vertical_edge , & vertical .scale_info , 0 , resize -> user_data );
78377891
78387892 if ( ( vertical .scale_info .output_sub_size / splits ) < STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS ) // each split should be a minimum of 4 scanlines (handwavey choice)
78397893 {
@@ -7958,153 +8012,120 @@ STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start
79588012 return stbir__perform_resize ( resize -> samplers , split_start , split_count );
79598013}
79608014
7961- static int stbir__check_output_stuff ( void * * ret_ptr , int * ret_pitch , void * output_pixels , int type_size , int output_w , int output_h , int output_stride_in_bytes , stbir_internal_pixel_layout pixel_layout )
8015+
8016+ static void * stbir_quick_resize_helper ( const void * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
8017+ void * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
8018+ stbir_pixel_layout pixel_layout , stbir_datatype data_type , stbir_edge edge , stbir_filter filter )
79628019{
7963- size_t size ;
7964- int pitch ;
7965- void * ptr ;
8020+ STBIR_RESIZE resize ;
8021+ int scanline_output_in_bytes ;
8022+ int positive_output_stride_in_bytes ;
8023+ void * start_ptr ;
8024+ void * free_ptr ;
79668025
7967- pitch = output_w * type_size * stbir__pixel_channels [ pixel_layout ];
7968- if ( pitch == 0 )
8026+ scanline_output_in_bytes = output_w * stbir__type_size [ data_type ] * stbir__pixel_channels [ stbir__pixel_layout_convert_public_to_internal [ pixel_layout ] ];
8027+ if ( scanline_output_in_bytes == 0 )
79698028 return 0 ;
79708029
8030+ // if zero stride, use scanline output
79718031 if ( output_stride_in_bytes == 0 )
7972- output_stride_in_bytes = pitch ;
8032+ output_stride_in_bytes = scanline_output_in_bytes ;
79738033
7974- if ( output_stride_in_bytes < pitch )
7975- return 0 ;
8034+ // abs value for inverted images (negative pitches)
8035+ positive_output_stride_in_bytes = output_stride_in_bytes ;
8036+ if ( positive_output_stride_in_bytes < 0 )
8037+ positive_output_stride_in_bytes = - positive_output_stride_in_bytes ;
79768038
7977- size = ( size_t ) output_stride_in_bytes * ( size_t ) output_h ;
7978- if ( size == 0 )
8039+ // is the requested stride smaller than the scanline output? if so, just fail
8040+ if ( positive_output_stride_in_bytes < scanline_output_in_bytes )
79798041 return 0 ;
79808042
7981- * ret_ptr = 0 ;
7982- * ret_pitch = output_stride_in_bytes ;
8043+ start_ptr = output_pixels ;
8044+ free_ptr = 0 ; // no free pointer, since they passed buffer to use
79838045
8046+ // did they pass a zero for the dest? if so, allocate the buffer
79848047 if ( output_pixels == 0 )
79858048 {
7986- ptr = STBIR_MALLOC ( size , 0 );
7987- if ( ptr == 0 )
7988- return 0 ;
8049+ size_t size ;
8050+ char * ptr ;
79898051
7990- * ret_ptr = ptr ;
7991- * ret_pitch = pitch ;
7992- }
7993-
7994- return 1 ;
7995- }
8052+ size = (size_t )positive_output_stride_in_bytes * (size_t )output_h ;
8053+ if ( size == 0 )
8054+ return 0 ;
79968055
8056+ ptr = (char * ) STBIR_MALLOC ( size , 0 );
8057+ if ( ptr == 0 )
8058+ return 0 ;
79978059
7998- STBIRDEF unsigned char * stbir_resize_uint8_linear ( const unsigned char * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
7999- unsigned char * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
8000- stbir_pixel_layout pixel_layout )
8001- {
8002- STBIR_RESIZE resize ;
8003- unsigned char * optr ;
8004- int opitch ;
8060+ free_ptr = ptr ;
80058061
8006- if ( !stbir__check_output_stuff ( (void * * )& optr , & opitch , output_pixels , sizeof ( unsigned char ), output_w , output_h , output_stride_in_bytes , stbir__pixel_layout_convert_public_to_internal [ pixel_layout ] ) )
8007- return 0 ;
8062+ // point at the last scanline, if they requested a flipped image
8063+ if ( output_stride_in_bytes < 0 )
8064+ start_ptr = ptr + ( (size_t )positive_output_stride_in_bytes * (size_t )( output_h - 1 ) );
8065+ else
8066+ start_ptr = ptr ;
8067+ }
80088068
8069+ // ok, now do the resize
80098070 stbir_resize_init ( & resize ,
80108071 input_pixels , input_w , input_h , input_stride_in_bytes ,
8011- (optr ) ? optr : output_pixels , output_w , output_h , opitch ,
8012- pixel_layout , STBIR_TYPE_UINT8 );
8072+ start_ptr , output_w , output_h , output_stride_in_bytes ,
8073+ pixel_layout , data_type );
8074+
8075+ resize .horizontal_edge = edge ;
8076+ resize .vertical_edge = edge ;
8077+ resize .horizontal_filter = filter ;
8078+ resize .vertical_filter = filter ;
80138079
80148080 if ( !stbir_resize_extended ( & resize ) )
80158081 {
8016- if ( optr )
8017- STBIR_FREE ( optr , 0 );
8082+ if ( free_ptr )
8083+ STBIR_FREE ( free_ptr , 0 );
80188084 return 0 ;
80198085 }
80208086
8021- return (optr ) ? optr : output_pixels ;
8087+ return (free_ptr ) ? free_ptr : start_ptr ;
8088+ }
8089+
8090+
8091+
8092+ STBIRDEF unsigned char * stbir_resize_uint8_linear ( const unsigned char * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
8093+ unsigned char * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
8094+ stbir_pixel_layout pixel_layout )
8095+ {
8096+ return (unsigned char * ) stbir_quick_resize_helper ( input_pixels , input_w , input_h , input_stride_in_bytes ,
8097+ output_pixels , output_w , output_h , output_stride_in_bytes ,
8098+ pixel_layout , STBIR_TYPE_UINT8 , STBIR_EDGE_CLAMP , STBIR_FILTER_DEFAULT );
80228099}
80238100
80248101STBIRDEF unsigned char * stbir_resize_uint8_srgb ( const unsigned char * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
80258102 unsigned char * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
80268103 stbir_pixel_layout pixel_layout )
80278104{
8028- STBIR_RESIZE resize ;
8029- unsigned char * optr ;
8030- int opitch ;
8031-
8032- if ( !stbir__check_output_stuff ( (void * * )& optr , & opitch , output_pixels , sizeof ( unsigned char ), output_w , output_h , output_stride_in_bytes , stbir__pixel_layout_convert_public_to_internal [ pixel_layout ] ) )
8033- return 0 ;
8034-
8035- stbir_resize_init ( & resize ,
8036- input_pixels , input_w , input_h , input_stride_in_bytes ,
8037- (optr ) ? optr : output_pixels , output_w , output_h , opitch ,
8038- pixel_layout , STBIR_TYPE_UINT8_SRGB );
8039-
8040- if ( !stbir_resize_extended ( & resize ) )
8041- {
8042- if ( optr )
8043- STBIR_FREE ( optr , 0 );
8044- return 0 ;
8045- }
8046-
8047- return (optr ) ? optr : output_pixels ;
8105+ return (unsigned char * ) stbir_quick_resize_helper ( input_pixels , input_w , input_h , input_stride_in_bytes ,
8106+ output_pixels , output_w , output_h , output_stride_in_bytes ,
8107+ pixel_layout , STBIR_TYPE_UINT8_SRGB , STBIR_EDGE_CLAMP , STBIR_FILTER_DEFAULT );
80488108}
80498109
80508110
80518111STBIRDEF float * stbir_resize_float_linear ( const float * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
80528112 float * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
80538113 stbir_pixel_layout pixel_layout )
80548114{
8055- STBIR_RESIZE resize ;
8056- float * optr ;
8057- int opitch ;
8058-
8059- if ( !stbir__check_output_stuff ( (void * * )& optr , & opitch , output_pixels , sizeof ( float ), output_w , output_h , output_stride_in_bytes , stbir__pixel_layout_convert_public_to_internal [ pixel_layout ] ) )
8060- return 0 ;
8061-
8062- stbir_resize_init ( & resize ,
8063- input_pixels , input_w , input_h , input_stride_in_bytes ,
8064- (optr ) ? optr : output_pixels , output_w , output_h , opitch ,
8065- pixel_layout , STBIR_TYPE_FLOAT );
8066-
8067- if ( !stbir_resize_extended ( & resize ) )
8068- {
8069- if ( optr )
8070- STBIR_FREE ( optr , 0 );
8071- return 0 ;
8072- }
8073-
8074- return (optr ) ? optr : output_pixels ;
8115+ return (float * ) stbir_quick_resize_helper ( input_pixels , input_w , input_h , input_stride_in_bytes ,
8116+ output_pixels , output_w , output_h , output_stride_in_bytes ,
8117+ pixel_layout , STBIR_TYPE_FLOAT , STBIR_EDGE_CLAMP , STBIR_FILTER_DEFAULT );
80758118}
80768119
80778120
80788121STBIRDEF void * stbir_resize ( const void * input_pixels , int input_w , int input_h , int input_stride_in_bytes ,
80798122 void * output_pixels , int output_w , int output_h , int output_stride_in_bytes ,
8080- stbir_pixel_layout pixel_layout , stbir_datatype data_type ,
8081- stbir_edge edge , stbir_filter filter )
8123+ stbir_pixel_layout pixel_layout , stbir_datatype data_type ,
8124+ stbir_edge edge , stbir_filter filter )
80828125{
8083- STBIR_RESIZE resize ;
8084- float * optr ;
8085- int opitch ;
8086-
8087- if ( !stbir__check_output_stuff ( (void * * )& optr , & opitch , output_pixels , stbir__type_size [data_type ], output_w , output_h , output_stride_in_bytes , stbir__pixel_layout_convert_public_to_internal [ pixel_layout ] ) )
8088- return 0 ;
8089-
8090- stbir_resize_init ( & resize ,
8091- input_pixels , input_w , input_h , input_stride_in_bytes ,
8092- (optr ) ? optr : output_pixels , output_w , output_h , output_stride_in_bytes ,
8093- pixel_layout , data_type );
8094-
8095- resize .horizontal_edge = edge ;
8096- resize .vertical_edge = edge ;
8097- resize .horizontal_filter = filter ;
8098- resize .vertical_filter = filter ;
8099-
8100- if ( !stbir_resize_extended ( & resize ) )
8101- {
8102- if ( optr )
8103- STBIR_FREE ( optr , 0 );
8104- return 0 ;
8105- }
8106-
8107- return (optr ) ? optr : output_pixels ;
8126+ return (void * ) stbir_quick_resize_helper ( input_pixels , input_w , input_h , input_stride_in_bytes ,
8127+ output_pixels , output_w , output_h , output_stride_in_bytes ,
8128+ pixel_layout , data_type , edge , filter );
81088129}
81098130
81108131#ifdef STBIR_PROFILE
0 commit comments