From aeb74055d0bf97e397c0f24c62e910b2eb72939f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 12 Jun 2021 21:08:35 +0200 Subject: [PATCH] Added a function to ImageComponent to crop fully transparent areas around an image. --- es-core/src/components/ImageComponent.cpp | 54 ++++++++++++++++++++++- es-core/src/components/ImageComponent.h | 4 ++ es-core/src/resources/TextureData.h | 1 + es-core/src/resources/TextureResource.cpp | 10 +++++ es-core/src/resources/TextureResource.h | 4 ++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index d69f22793..b69848d33 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -9,6 +9,7 @@ #include "components/ImageComponent.h" #include "resources/TextureResource.h" +#include "utils/CImgUtil.h" #include "Log.h" #include "Settings.h" #include "ThemeData.h" @@ -264,6 +265,55 @@ void ImageComponent::uncrop() crop(0, 0, 0, 0); } +void ImageComponent::cropTransparentPadding(float maxSizeX, float maxSizeY) +{ + if (mSize == 0) + return; + + std::vector imageRGBA = mTexture.get()->getRawRGBAData(); + + if (imageRGBA.size() == 0) + return; + + Vector2i imageSize = mTexture.get()->getSize(); + cimg_library::CImg imageCImg(imageSize.x(), imageSize.y(), 1, 4, 0); + + int paddingCoords[4] = {}; + + // We need to convert our RGBA data to the CImg internal format as CImg does not interleave + // the pixels (as in RGBARGBARGBA). + Utils::CImg::convertRGBAToCImg(imageRGBA, imageCImg); + + // This will give us the coordinates for the fully transparent areas. + Utils::CImg::getTransparentPaddingCoords(imageCImg, paddingCoords); + + Vector2f originalSize = mSize; + + float cropLeft = static_cast(paddingCoords[0]) / static_cast(imageSize.x()); + float cropTop = static_cast(paddingCoords[1]) / static_cast(imageSize.y()); + float cropRight = static_cast(paddingCoords[2]) / static_cast(imageSize.x()); + float cropBottom = static_cast(paddingCoords[3]) / static_cast(imageSize.y()); + + crop(cropLeft, cropTop, cropRight, cropBottom); + + // Cropping the image obviously leads to a reduction in size, so we need to determine + // how much to scale up after cropping to keep within the max size restrictions that + // were passed as arguments. + mSize.x() -= mSize.x() * (cropLeft + cropRight); + mSize.y() -= mSize.y() * (cropTop + cropBottom); + + float scaleFactor = originalSize.y() / mSize.y(); + + if (scaleFactor * mSize.x() > maxSizeX) + scaleFactor = maxSizeX / mSize.x(); + + if (scaleFactor * mSize.y() > maxSizeY) + scaleFactor = maxSizeY / mSize.y(); + + setResize(mSize.x() * scaleFactor, mSize.y() * scaleFactor); + updateVertices(); +} + void ImageComponent::setFlipX(bool flip) { mFlipX = flip; @@ -314,8 +364,8 @@ void ImageComponent::updateVertices() // We go through this mess to make sure everything is properly rounded. // If we just round vertices at the end, edge cases occur near sizes of 0.5. - const Vector2f topLeft = { mSize * mTopLeftCrop }; - const Vector2f bottomRight = { mSize * mBottomRightCrop }; + const Vector2f topLeft = { 0, 0 }; + const Vector2f bottomRight = mSize; const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f; const float py = mTexture->isTiled() ? mSize.y() / getTextureSize().y() : 1.0f; diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 9751020aa..eb58a3e3c 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -61,6 +61,10 @@ public: void crop(float left, float top, float right, float bot); void uncrop(); + // This crops any entirely transparent areas around the actual image. + // The arguments restrict how much the end result is allowed to be scaled. + void cropTransparentPadding(float maxSizeX, float maxSizeY); + // Multiply all pixels in the image by this color when rendering. void setColorShift(unsigned int color) override; void setColorShiftEnd(unsigned int color); diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index c1ffb404c..ca7e52973 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -57,6 +57,7 @@ public: // Define a factor for scaling the file when loading it (1.0f = no scaling). void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; } + std::vector getRawRGBAData() { return mDataRGBA; } bool tiled() { return mTile; } private: diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index e82a49310..887d6d408 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -105,6 +105,16 @@ void TextureResource::manualUnload(std::string path, bool tile) } } +std::vector TextureResource::getRawRGBAData() +{ + std::shared_ptr data = sTextureDataManager.get(this); + + if (data) + return data.get()->getRawRGBAData(); + else + return std::vector(0); +} + const Vector2i TextureResource::getSize() const { return mSize; diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index a9fa3ee0b..4db29adde 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -17,6 +17,7 @@ #include #include #include +#include class TextureData; @@ -35,6 +36,9 @@ public: virtual void initFromMemory(const char* data, size_t length); static void manualUnload(std::string path, bool tile); + // Returns the raw pixel values. + std::vector getRawRGBAData(); + // For SVG graphics this function effectively rescales the image to the defined size. // 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