Allow rescaling larger images in Win64 builds

Use wxUIntPtr rather than (unsigned) long in wxImage::ResampleNearest()
as long is still 32 bits under Win64 and so doesn't allow the code there
to work with images larger than 2^16 in either direction, when it could
be allowed in this case.

Document the current limits on the size of the image and add a unit test
checking that resizing images of size greater than 2^16 works in 64 bits.

See #18550.
This commit is contained in:
Vadim Zeitlin
2021-09-01 20:00:48 +01:00
parent 39e1cae617
commit 44a5cf78d1
3 changed files with 34 additions and 8 deletions

View File

@@ -972,6 +972,11 @@ public:
}
@endcode
@note The algorithm used for the default (normal) quality value doesn't
work with images larger than 65536 (2^16) pixels in either dimension
for 32-bit programs. For 64-bit programs the limit is 2^48 and so not
relevant in practice.
@see Rescale()
*/
wxImage Scale(int width, int height,

View File

@@ -503,10 +503,19 @@ wxImage wxImage::ResampleNearest(int width, int height) const
{
wxImage image;
const unsigned long old_width = M_IMGDATA->m_width;
const unsigned long old_height = M_IMGDATA->m_height;
wxCHECK_MSG(old_width <= (ULONG_MAX >> 16) &&
old_height <= (ULONG_MAX >> 16), image, "image dimension too large");
// We use wxUIntPtr to rescale images of larger size in 64-bit builds:
// using long wouldn't allow using images larger than 2^16 in either
// direction because of the check below, as sizeof(long) == 4 even in 64
// bit builds under MSW, but sizeof(wxUIntPtr) == 8 in this case.
const wxUIntPtr old_width = M_IMGDATA->m_width;
const wxUIntPtr old_height = M_IMGDATA->m_height;
// We use "x << 16" in the code below, so check that this doesn't wrap
// around, as the code wouldn't work correctly if it did.
static const wxUIntPtr SIZE_LIMIT = static_cast<wxUIntPtr>(-1) >> 16;
wxCHECK_MSG(old_width <= SIZE_LIMIT &&
old_height <= SIZE_LIMIT, image, "image dimension too large");
image.Create( width, height, false );
@@ -529,18 +538,18 @@ wxImage wxImage::ResampleNearest(int width, int height) const
}
}
const unsigned long x_delta = (old_width << 16) / width;
const unsigned long y_delta = (old_height << 16) / height;
const wxUIntPtr x_delta = (old_width << 16) / width;
const wxUIntPtr y_delta = (old_height << 16) / height;
unsigned char* dest_pixel = target_data;
unsigned long y = 0;
wxUIntPtr y = 0;
for (int j = 0; j < height; j++)
{
const unsigned char* src_line = &source_data[(y>>16)*old_width*3];
const unsigned char* src_alpha_line = source_alpha ? &source_alpha[(y>>16)*old_width] : 0 ;
unsigned long x = 0;
wxUIntPtr x = 0;
for (int i = 0; i < width; i++)
{
const unsigned char* src_pixel = &src_line[(x>>16)*3];

View File

@@ -2249,6 +2249,18 @@ TEST_CASE("wxImage::ChangeColours", "[image]")
CHECK_THAT(test, RGBSameAs(expected));
}
TEST_CASE("wxImage::SizeLimits", "[image]")
{
#if SIZEOF_VOID_P == 8
// Check that we can resample an image of size greater than 2^16, which is
// the limit used in 32-bit code to avoid integer overflows.
wxImage image(100000, 2);
REQUIRE_NOTHROW( image = image.ResampleNearest(100000, 1) );
CHECK( image.GetWidth() == 100000 );
CHECK( image.GetHeight() == 1 );
#endif // SIZEOF_VOID_P == 8
}
/*
TODO: add lots of more tests to wxImage functions
*/