Compare commits

...

13 Commits

Author SHA1 Message Date
Mikhail Morozov 2b25f2f8cd stb_image: fix warning in clang 2024-04-03 23:38:14 +03:00
Mikhail Morozov 4187a39b85 stb_image: fix endianness for 16-bit ppm 2024-04-03 23:38:14 +03:00
Mikhail Morozov d805b402ef stb_image: fix previously introduced warnings 2024-04-03 23:38:13 +03:00
Mikhail Morozov a8b4d31395 stb_image: add pbm support 2024-04-03 23:38:13 +03:00
Mikhail Morozov 9629715904 stb_image: add pbm support 2024-04-03 23:38:13 +03:00
Mikhail Morozov 53e8427e90 stb_image: add pbm tests derived from pngsuite 2024-04-03 23:38:13 +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
6 changed files with 140 additions and 45 deletions

View File

@ -1018,7 +1018,7 @@ static int stbi__mul2sizes_valid(int a, int b)
return a <= INT_MAX/b;
}
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
static int stbi__mad2sizes_valid(int a, int b, int add)
{
@ -1042,7 +1042,7 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
}
#endif
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
// mallocs with size overflow checking
static void *stbi__malloc_mad2(int a, int b, int add)
{
@ -7493,7 +7493,7 @@ static int stbi__pnm_test(stbi__context *s)
char p, t;
p = (char) stbi__get8(s);
t = (char) stbi__get8(s);
if (p != 'P' || (t != '5' && t != '6')) {
if (p != 'P' || (t != '4' && t != '5' && t != '6')) {
stbi__rewind( s );
return 0;
}
@ -7503,12 +7503,12 @@ static int stbi__pnm_test(stbi__context *s)
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
{
stbi_uc *out;
int bytes_per_channel;
STBI_NOTUSED(ri);
ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
if (ri->bits_per_channel == 0)
return 0;
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
@ -7519,11 +7519,65 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
return stbi__errpuc("too large", "PNM too large");
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (ri->bits_per_channel == 16) bytes_per_channel = 2; else bytes_per_channel = 1;
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, bytes_per_channel, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
if (ri->bits_per_channel == 1) {
stbi_uc *filebuf, *p_in, *p_out;
stbi__uint32 line, x, y, offset;
ri->bits_per_channel = 8;
line = s->img_x/8+((s->img_x%8>0)?1:0);
filebuf = (stbi_uc *) stbi__malloc_mad2(line, s->img_y, 0);
if (!filebuf) {
STBI_FREE(out);
return stbi__errpuc("outofmem", "Out of memory");
}
if (!stbi__getn(s, filebuf, line*s->img_y)) {
STBI_FREE(out);
STBI_FREE(filebuf);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
p_in = filebuf;
p_out = out;
offset = 8;
for(y = 0; y < s->img_y; y++) {
for(x = 0; x < s->img_x; x++) {
offset--;
*(p_out++) = ((*p_in>>offset)&0x1)?0:255;
if(offset == 0) {
offset = 8;
p_in++;
}
}
if(offset != 8) {
offset = 8;
p_in++;
}
}
STBI_FREE(filebuf);
} else {
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
if (ri->bits_per_channel == 16) {
stbi_uc *p8;
stbi__uint16 *p16;
size_t filesize;
p8 = out;
p16 = (stbi__uint16 *) out;
filesize = s->img_n * s->img_x * s->img_y;
while(filesize--) {
// Convert from BIG-ENDIAN
*(p16++) = p8[1] + p8[0]*256;
p8 += 2;
}
}
}
if (req_comp && req_comp != s->img_n) {
@ -7589,12 +7643,12 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
// Get identifier
p = (char) stbi__get8(s);
t = (char) stbi__get8(s);
if (p != 'P' || (t != '5' && t != '6')) {
if (p != 'P' || (t != '4' && t != '5' && t != '6')) {
stbi__rewind(s);
return 0;
}
*comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
*comp = (t == '6') ? 3 : 1; // '4' is monochrome .pbm, '5' is 1-component .pgm; '6' is 3-component .ppm
c = (char) stbi__get8(s);
stbi__pnm_skip_whitespace(s, &c);
@ -7607,6 +7661,7 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
*y = stbi__pnm_getinteger(s, &c); // read height
if (*y == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
if(t == '4') return 1; // Header ends here for PBM files
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value

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 ) );

View File

@ -22,7 +22,7 @@ unzip $SRC/stbi/tga.zip -d $SRC/stb/tests
find $SRC/stb/tests -name "*.png" -o -name "*.jpg" -o -name "*.gif" \
-o -name "*.bmp" -o -name "*.tga" -o -name "*.TGA" \
-o -name "*.ppm" -o -name "*.pgm" \
-o -name "*.ppm" -o -name "*.pgm" -o -name "*.pbm" \
| xargs zip $OUT/stbi_read_fuzzer_seed_corpus.zip
echo "" >> $SRC/stbi/gif.dict

BIN
tests/pbm/basi0g01.pbm Normal file

Binary file not shown.

BIN
tests/pbm/basi0g01_31.pbm Normal file

Binary file not shown.

BIN
tests/pbm/basi0g01_33.pbm Normal file

Binary file not shown.