Fixed multiple image scaling and rasterization issues.

This commit is contained in:
Leon Styhre 2021-10-23 15:45:44 +02:00
parent 39acfdfe00
commit 6cee6d2732
5 changed files with 21 additions and 28 deletions

View file

@ -63,11 +63,8 @@ void ImageComponent::resize()
else { else {
// SVG rasterization is determined by height and rasterization is done in terms of pixels. // SVG rasterization is determined by height and rasterization is done in terms of pixels.
// If rounding is off enough in the rasterization step (for images with extreme aspect // If rounding is off enough in the rasterization step (for images with extreme aspect
// ratios), it can cause cutoff when the aspect ratio breaks. // ratios), it can cause cutoff when the aspect ratio breaks. So we always make sure to
// So we always make sure the resultant height is an integer to make sure cutoff doesn't // round accordingly to avoid such issues.
// happen, and scale width from that (you'll see this scattered throughout the function).
// It's important to use floorf rather than round for this, as we never want to round up
// since that can lead to the cutoff just described.
if (mTargetIsMax) { if (mTargetIsMax) {
mSize = textureSize; mSize = textureSize;
@ -77,13 +74,11 @@ void ImageComponent::resize()
// This will be mTargetSize.x. We can't exceed it, nor be lower than it. // This will be mTargetSize.x. We can't exceed it, nor be lower than it.
mSize.x *= resizeScale.x; mSize.x *= resizeScale.x;
// We need to make sure we're not creating an image larger than max size. // We need to make sure we're not creating an image larger than max size.
mSize.y = std::min(floorf(mSize.y * resizeScale.x), mTargetSize.y); mSize.y = floorf(std::min(mSize.y * resizeScale.x, mTargetSize.y));
} }
else { else {
// This will be mTargetSize.y(). We can't exceed it. // This will be mTargetSize.y(). We can't exceed it.
mSize.y = floorf(mSize.y * resizeScale.y); mSize.y *= resizeScale.y;
// For SVG rasterization, always calculate width from rounded height (see comment
// above). We need to make sure we're not creating an image larger than max size.
mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x); mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
} }
} }
@ -106,9 +101,7 @@ void ImageComponent::resize()
float cropPercent = (mSize.x - mTargetSize.x) / (mSize.x * 2.0f); float cropPercent = (mSize.x - mTargetSize.x) / (mSize.x * 2.0f);
crop(cropPercent, 0.0f, cropPercent, 0.0f); crop(cropPercent, 0.0f, cropPercent, 0.0f);
} }
// For SVG rasterization, always calculate width from rounded height (see comment mSize.y = std::max(mSize.y, mTargetSize.y);
// above). We need to make sure we're not creating an image smaller than min size.
mSize.y = std::max(floorf(mSize.y), mTargetSize.y);
mSize.x = std::max((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x); mSize.x = std::max((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
} }
else { else {
@ -117,23 +110,24 @@ void ImageComponent::resize()
mSize = mTargetSize == glm::vec2{} ? textureSize : mTargetSize; mSize = mTargetSize == glm::vec2{} ? textureSize : mTargetSize;
// If only one component is set, we resize in a way that maintains aspect ratio. // If only one component is set, we resize in a way that maintains aspect ratio.
// For SVG rasterization, we always calculate width from rounded height (see
// comment above).
if (!mTargetSize.x && mTargetSize.y) { if (!mTargetSize.x && mTargetSize.y) {
mSize.y = floorf(mTargetSize.y); mSize.y = mTargetSize.y;
mSize.x = (mSize.y / textureSize.y) * textureSize.x; mSize.x = (mSize.y / textureSize.y) * textureSize.x;
} }
else if (mTargetSize.x && !mTargetSize.y) { else if (mTargetSize.x && !mTargetSize.y) {
mSize.y = floorf((mTargetSize.x / textureSize.x) * textureSize.y); mSize.y = (mTargetSize.x / textureSize.x) * textureSize.y;
mSize.x = (mSize.y / textureSize.y) * textureSize.x; mSize.x = (mSize.y / textureSize.y) * textureSize.x;
} }
} }
} }
// Make sure sub-pixel values are not rounded to zero.
if (mSize.x < 1.0f)
mSize.x = ceilf(mSize.x); mSize.x = ceilf(mSize.x);
if (mSize.y < 1.0f)
mSize.y = ceilf(mSize.y); mSize.y = ceilf(mSize.y);
mTexture->rasterizeAt(static_cast<size_t>(mSize.x), static_cast<size_t>(mSize.y)); mTexture->rasterizeAt(mSize.x, mSize.y);
onSizeChanged(); onSizeChanged();
} }

View file

@ -73,8 +73,8 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
mSourceHeight = svgImage->height; mSourceHeight = svgImage->height;
} }
mWidth = static_cast<size_t>(floorf(floorf(mSourceWidth) * mScaleDuringLoad)); mWidth = static_cast<int>(std::round(mSourceWidth * mScaleDuringLoad));
mHeight = static_cast<size_t>(floorf(floorf(mSourceHeight) * mScaleDuringLoad)); mHeight = static_cast<int>(std::round(mSourceHeight * mScaleDuringLoad));
if (mWidth == 0) { if (mWidth == 0) {
// Auto scale width to keep aspect ratio. // Auto scale width to keep aspect ratio.
@ -92,9 +92,8 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
NSVGrasterizer* rast = nsvgCreateRasterizer(); NSVGrasterizer* rast = nsvgCreateRasterizer();
nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth,
static_cast<int>(mWidth), static_cast<int>(mHeight), mHeight, mWidth * 4);
static_cast<int>(mWidth) * 4);
// This is important in order to avoid memory leaks. // This is important in order to avoid memory leaks.
nsvgDeleteRasterizer(rast); nsvgDeleteRasterizer(rast);

View file

@ -72,8 +72,8 @@ private:
std::string mPath; std::string mPath;
unsigned int mTextureID; unsigned int mTextureID;
std::vector<unsigned char> mDataRGBA; std::vector<unsigned char> mDataRGBA;
size_t mWidth; int mWidth;
size_t mHeight; int mHeight;
float mSourceWidth; float mSourceWidth;
float mSourceHeight; float mSourceHeight;
float mScaleDuringLoad; float mScaleDuringLoad;

View file

@ -194,7 +194,7 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
return tex; return tex;
} }
void TextureResource::rasterizeAt(size_t width, size_t height) void TextureResource::rasterizeAt(float width, float height)
{ {
if (mTextureData != nullptr) { if (mTextureData != nullptr) {
glm::vec2 textureSize = mTextureData.get()->getSize(); glm::vec2 textureSize = mTextureData.get()->getSize();

View file

@ -44,7 +44,7 @@ public:
// It does unload and re-rasterize the texture though which may cause flickering in some // It does unload and re-rasterize the texture though which may cause flickering in some
// situations. An alternative is to set a scaling factor directly when loading the texture // situations. An alternative is to set a scaling factor directly when loading the texture
// using get(), by using the scaleDuringLoad parameter (which also works for raster graphics). // using get(), by using the scaleDuringLoad parameter (which also works for raster graphics).
void rasterizeAt(size_t width, size_t height); void rasterizeAt(float width, float height);
glm::vec2 getSourceImageSize() const { return mSourceSize; } glm::vec2 getSourceImageSize() const { return mSourceSize; }
virtual ~TextureResource(); virtual ~TextureResource();