Clarify Image::copy implementation

This commit is contained in:
kimci86 2022-06-22 23:51:50 +02:00 committed by Lukas Dürrenberger
parent 6cd07a043f
commit c4790bcb14
2 changed files with 29 additions and 32 deletions

View File

@ -199,7 +199,7 @@ public:
/// Note that this function can fail if either image is invalid /// Note that this function can fail if either image is invalid
/// (i.e. zero-sized width or height), or if \a sourceRect is /// (i.e. zero-sized width or height), or if \a sourceRect is
/// not within the boundaries of the \a source parameter, or /// not within the boundaries of the \a source parameter, or
/// if the destination area is invalid (i.e. negative coordinates). /// if the destination area is out of the boundaries of this image.
/// ///
/// On failure, the destination image is left unchanged. /// On failure, the destination image is left unchanged.
/// ///

View File

@ -174,56 +174,53 @@ void Image::createMaskFromColor(const Color& color, Uint8 alpha)
[[nodiscard]] bool Image::copy(const Image& source, const Vector2u& dest, const IntRect& sourceRect, bool applyAlpha) [[nodiscard]] bool Image::copy(const Image& source, const Vector2u& dest, const IntRect& sourceRect, bool applyAlpha)
{ {
// Make sure that both images are valid // Make sure that both images are valid
if ((source.m_size.x == 0) || (source.m_size.y == 0) || (m_size.x == 0) || (m_size.y == 0)) if (source.m_size.x == 0 || source.m_size.y == 0 || m_size.x == 0 || m_size.y == 0)
return false; return false;
// Make sure the sourceRect left & top and the {left, top} + {width, height} within bounds // Make sure the sourceRect components are non-negative before casting them to unsigned values
if (static_cast<unsigned int>(sourceRect.left) >= source.m_size.x || static_cast<unsigned int>(sourceRect.left + sourceRect.width) > source.m_size.x || if (sourceRect.left < 0 || sourceRect.top < 0 || sourceRect.width < 0 || sourceRect.height < 0)
static_cast<unsigned int>(sourceRect.top) >= source.m_size.y || static_cast<unsigned int>(sourceRect.top + sourceRect.height) > source.m_size.y)
return false; return false;
// Adjust the source rectangle Rect<unsigned int> srcRect(sourceRect);
IntRect srcRect = sourceRect;
if (srcRect.width == 0 || (srcRect.height == 0)) // Use the whole source image as srcRect if the provided source rectangle is empty
if (srcRect.width == 0 || srcRect.height == 0)
{ {
srcRect.left = 0; srcRect = Rect<unsigned int>({0, 0}, source.m_size);
srcRect.top = 0;
srcRect.width = static_cast<int>(source.m_size.x);
srcRect.height = static_cast<int>(source.m_size.y);
} }
// Otherwise make sure the provided source rectangle fits into the source image
else else
{ {
if (srcRect.left < 0) srcRect.left = 0; // Checking the bottom right corner is enough because
if (srcRect.top < 0) srcRect.top = 0; // left and top are non-negative and width and height are positive.
if (srcRect.width > static_cast<int>(source.m_size.x)) srcRect.width = static_cast<int>(source.m_size.x); if (source.m_size.x < srcRect.left + srcRect.width ||
if (srcRect.height > static_cast<int>(source.m_size.y)) srcRect.height = static_cast<int>(source.m_size.y); source.m_size.y < srcRect.top + srcRect.height)
return false;
} }
// Then find the valid bounds of the destination rectangle // Make sure the destination position is within this image bounds
auto width = static_cast<unsigned int>(srcRect.width); if (m_size.x <= dest.x || m_size.y <= dest.y)
auto height = static_cast<unsigned int>(srcRect.height);
if (dest.x + width > m_size.x) width = m_size.x - dest.x;
if (dest.y + height > m_size.y) height = m_size.y - dest.y;
// Make sure the destination area is valid
if ((width <= 0) || (height <= 0))
return false; return false;
// Then find the valid size of the destination rectangle
const Vector2u dstSize(std::min(m_size.x - dest.x, srcRect.width),
std::min(m_size.y - dest.y, srcRect.height));
// Precompute as much as possible // Precompute as much as possible
std::size_t pitch = static_cast<std::size_t>(width) * 4; const std::size_t pitch = static_cast<std::size_t>(dstSize.x) * 4;
unsigned int rows = height; const unsigned int srcStride = source.m_size.x * 4;
int srcStride = static_cast<int>(source.m_size.x) * 4; const unsigned int dstStride = m_size.x * 4;
int dstStride = static_cast<int>(m_size.x) * 4;
const Uint8* srcPixels = source.m_pixels.data() + (static_cast<unsigned int>(srcRect.left) + static_cast<unsigned int>(srcRect.top) * source.m_size.x) * 4; const Uint8* srcPixels = source.m_pixels.data() + (srcRect.left + srcRect.top * source.m_size.x) * 4;
Uint8* dstPixels = m_pixels.data() + (dest.x + dest.y * m_size.x) * 4; Uint8* dstPixels = m_pixels.data() + (dest.x + dest.y * m_size.x) * 4;
// Copy the pixels // Copy the pixels
if (applyAlpha) if (applyAlpha)
{ {
// Interpolation using alpha values, pixel by pixel (slower) // Interpolation using alpha values, pixel by pixel (slower)
for (unsigned int i = 0; i < rows; ++i) for (unsigned int i = 0; i < dstSize.y; ++i)
{ {
for (unsigned int j = 0; j < width; ++j) for (unsigned int j = 0; j < dstSize.x; ++j)
{ {
// Get a direct pointer to the components of the current pixel // Get a direct pointer to the components of the current pixel
const Uint8* src = srcPixels + j * 4; const Uint8* src = srcPixels + j * 4;
@ -251,7 +248,7 @@ void Image::createMaskFromColor(const Color& color, Uint8 alpha)
else else
{ {
// Optimized copy ignoring alpha values, row by row (faster) // Optimized copy ignoring alpha values, row by row (faster)
for (unsigned int i = 0; i < rows; ++i) for (unsigned int i = 0; i < dstSize.y; ++i)
{ {
std::memcpy(dstPixels, srcPixels, pitch); std::memcpy(dstPixels, srcPixels, pitch);
srcPixels += srcStride; srcPixels += srcStride;