mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Made a large optimization to the SVG rasterization logic.
This commit is contained in:
parent
270a2e3857
commit
7ed0267f5b
|
@ -600,8 +600,8 @@ void Window::renderLoadingScreen(std::string text)
|
|||
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
||||
|
||||
ImageComponent splash(this, true);
|
||||
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
||||
splash.setImage(":/graphics/splash.svg");
|
||||
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
||||
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x) / 2.0f,
|
||||
(Renderer::getScreenHeight() - splash.getSize().y) / 2.0f * 0.6f);
|
||||
splash.render(trans);
|
||||
|
|
|
@ -109,7 +109,8 @@ private:
|
|||
bool mTargetIsMin;
|
||||
|
||||
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
|
||||
// Used internally whenever the resizing parameters or texture change.
|
||||
// Used internally whenever the resizing parameters or texture change. This function also
|
||||
// initiates the SVG rasterization.
|
||||
void resize();
|
||||
|
||||
Renderer::Vertex mVertices[4];
|
||||
|
|
|
@ -59,7 +59,7 @@ void NinePatchComponent::buildVertices()
|
|||
else
|
||||
scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f);
|
||||
|
||||
mTexture = TextureResource::get(mPath, false, false, true, true, scaleFactor);
|
||||
mTexture = TextureResource::get(mPath, false, false, true, true, true, scaleFactor);
|
||||
|
||||
if (mTexture->getSize() == glm::ivec2{}) {
|
||||
mVertices = nullptr;
|
||||
|
|
|
@ -76,8 +76,12 @@ public:
|
|||
// Handles positioning/resizing of text and arrows.
|
||||
void onSizeChanged() override
|
||||
{
|
||||
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||
if (mText.getFont()->getLetterHeight() != mLeftArrow.getSize().y ||
|
||||
mLeftArrow.getTexture()->getPendingRasterization())
|
||||
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||
if (mText.getFont()->getLetterHeight() != mRightArrow.getSize().y ||
|
||||
mRightArrow.getTexture()->getPendingRasterization())
|
||||
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||
|
||||
if (mSize.x < (mLeftArrow.getSize().x + mRightArrow.getSize().x)) {
|
||||
LOG(LogWarning) << "OptionListComponent too narrow";
|
||||
|
|
|
@ -49,16 +49,6 @@ void SwitchComponent::render(const glm::mat4& parentTrans)
|
|||
renderChildren(trans);
|
||||
}
|
||||
|
||||
void SwitchComponent::setResize(float width, float height)
|
||||
{
|
||||
// Reposition the switch after resizing to make it centered.
|
||||
const glm::vec2 oldSize = mImage.getSize();
|
||||
mImage.setResize(width, height);
|
||||
const float xDiff = oldSize.x - mImage.getSize().x;
|
||||
const float yDiff = oldSize.y - mImage.getSize().y;
|
||||
mImage.setPosition(mImage.getPosition().x + xDiff, mImage.getPosition().y + yDiff / 2.0f);
|
||||
}
|
||||
|
||||
void SwitchComponent::setState(bool state)
|
||||
{
|
||||
mState = state;
|
||||
|
@ -81,6 +71,7 @@ void SwitchComponent::setValue(const std::string& statestring)
|
|||
void SwitchComponent::onStateChanged()
|
||||
{
|
||||
mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg");
|
||||
mImage.setResize(mSize);
|
||||
|
||||
// Change the color of the switch to reflect the changes.
|
||||
if (mState == mOriginalValue)
|
||||
|
|
|
@ -22,8 +22,7 @@ public:
|
|||
void render(const glm::mat4& parentTrans) override;
|
||||
void onSizeChanged() override { mImage.setSize(mSize); }
|
||||
|
||||
void setResize(float width, float height) override;
|
||||
|
||||
void setResize(float width, float height) override { setSize(width, height); }
|
||||
bool getState() const { return mState; }
|
||||
void setState(bool state);
|
||||
std::string getValue() const override;
|
||||
|
|
|
@ -24,16 +24,18 @@
|
|||
#define DPI 96
|
||||
|
||||
TextureData::TextureData(bool tile)
|
||||
: mTile(tile)
|
||||
, mTextureID(0)
|
||||
: mTile{tile}
|
||||
, mTextureID{0}
|
||||
, mDataRGBA({})
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mSourceWidth(0.0f)
|
||||
, mSourceHeight(0.0f)
|
||||
, mScaleDuringLoad(1.0f)
|
||||
, mScalable(false)
|
||||
, mLinearMagnify(false)
|
||||
, mWidth{0}
|
||||
, mHeight{0}
|
||||
, mSourceWidth{0.0f}
|
||||
, mSourceHeight{0.0f}
|
||||
, mScaleDuringLoad{1.0f}
|
||||
, mScalable{false}
|
||||
, mLinearMagnify{false}
|
||||
, mAlwaysRasterize{false}
|
||||
, mPendingRasterization{false}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -54,21 +56,26 @@ void TextureData::initFromPath(const std::string& path)
|
|||
bool TextureData::initSVGFromMemory(const std::string& fileData)
|
||||
{
|
||||
// If already initialized then don't process it again.
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
std::unique_lock<std::mutex> lock{mMutex};
|
||||
|
||||
if (!mDataRGBA.empty())
|
||||
return true;
|
||||
|
||||
NSVGimage* svgImage = nsvgParse(const_cast<char*>(fileData.c_str()), "px", DPI);
|
||||
NSVGimage* svgImage{nsvgParse(const_cast<char*>(fileData.c_str()), "px", DPI)};
|
||||
|
||||
if (!svgImage) {
|
||||
LOG(LogError) << "Couldn't parse SVG image";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to rasterize this texture at a specific resolution. If the source size
|
||||
// variables are set then use them, otherwise get them from the parsed file.
|
||||
if ((mSourceWidth == 0.0f) && (mSourceHeight == 0.0f)) {
|
||||
bool rasterize{true};
|
||||
|
||||
// If there is no image size defined yet, then don't rasterize unless mAlwaysRasterize has
|
||||
// been set (this is only used by NinePatchComponent to avoid flickering menus).
|
||||
if (mSourceWidth == 0.0f && mSourceHeight == 0.0f) {
|
||||
if (!mAlwaysRasterize)
|
||||
rasterize = false;
|
||||
|
||||
mSourceWidth = svgImage->width;
|
||||
mSourceHeight = svgImage->height;
|
||||
}
|
||||
|
@ -87,23 +94,31 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
|
|||
std::round((static_cast<float>(mWidth) / svgImage->width) * svgImage->height));
|
||||
}
|
||||
|
||||
std::vector<unsigned char> tempVector;
|
||||
tempVector.reserve(mWidth * mHeight * 4);
|
||||
if (rasterize) {
|
||||
std::vector<unsigned char> tempVector;
|
||||
tempVector.reserve(mWidth * mHeight * 4);
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
|
||||
nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth,
|
||||
mHeight, mWidth * 4);
|
||||
nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth,
|
||||
mHeight, mWidth * 4);
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
|
||||
mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(),
|
||||
tempVector.data() + (mWidth * mHeight * 4));
|
||||
|
||||
ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight);
|
||||
mPendingRasterization = false;
|
||||
}
|
||||
else {
|
||||
// TODO: Fix this properly instead of using the single byte texture workaround.
|
||||
mDataRGBA.push_back(0);
|
||||
mPendingRasterization = true;
|
||||
}
|
||||
|
||||
// This is important in order to avoid memory leaks.
|
||||
nsvgDeleteRasterizer(rast);
|
||||
nsvgDelete(svgImage);
|
||||
|
||||
mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(),
|
||||
tempVector.data() + (mWidth * mHeight * 4));
|
||||
|
||||
ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,11 @@ public:
|
|||
void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; }
|
||||
// Whether to use linear filtering when magnifying the texture.
|
||||
void setLinearMagnify(bool setting) { mLinearMagnify = setting; }
|
||||
// Whether to rasterize the image even if a size has not been set yet.
|
||||
void setAlwaysRasterize(bool setting) { mAlwaysRasterize = setting; }
|
||||
|
||||
// Has the image been loaded but not yet been rasterized as the size was not known?
|
||||
bool getPendingRasterization() { return mPendingRasterization; }
|
||||
|
||||
std::vector<unsigned char> getRawRGBAData() { return mDataRGBA; }
|
||||
std::string getTextureFilePath() { return mPath; }
|
||||
|
@ -80,6 +85,8 @@ private:
|
|||
bool mScalable;
|
||||
bool mLinearMagnify;
|
||||
bool mReloadable;
|
||||
bool mAlwaysRasterize;
|
||||
bool mPendingRasterization;
|
||||
};
|
||||
|
||||
#endif // ES_CORE_RESOURCES_TEXTURE_DATA_H
|
||||
|
|
|
@ -8,16 +8,20 @@
|
|||
|
||||
#include "resources/TextureResource.h"
|
||||
|
||||
#include "resources/TextureData.h"
|
||||
#include "utils/FileSystemUtil.h"
|
||||
#include "utils/StringUtil.h"
|
||||
|
||||
TextureDataManager TextureResource::sTextureDataManager;
|
||||
std::map<TextureResource::TextureKeyType, std::weak_ptr<TextureResource>>
|
||||
TextureResource::sTextureMap;
|
||||
std::set<TextureResource*> TextureResource::sAllTextures;
|
||||
|
||||
TextureResource::TextureResource(
|
||||
const std::string& path, bool tile, bool dynamic, bool linearMagnify, float scaleDuringLoad)
|
||||
TextureResource::TextureResource(const std::string& path,
|
||||
bool tile,
|
||||
bool dynamic,
|
||||
bool linearMagnify,
|
||||
bool alwaysRasterize,
|
||||
float scaleDuringLoad)
|
||||
: mTextureData(nullptr)
|
||||
, mForceLoad(false)
|
||||
{
|
||||
|
@ -32,6 +36,7 @@ TextureResource::TextureResource(
|
|||
if (scaleDuringLoad != 1.0f)
|
||||
data->setScaleDuringLoad(scaleDuringLoad);
|
||||
data->setLinearMagnify(linearMagnify);
|
||||
data->setAlwaysRasterize(alwaysRasterize);
|
||||
// Force the texture manager to load it using a blocking load.
|
||||
sTextureDataManager.load(data, true);
|
||||
}
|
||||
|
@ -42,6 +47,7 @@ TextureResource::TextureResource(
|
|||
if (scaleDuringLoad != 1.0f)
|
||||
data->setScaleDuringLoad(scaleDuringLoad);
|
||||
data->setLinearMagnify(linearMagnify);
|
||||
data->setAlwaysRasterize(alwaysRasterize);
|
||||
// Load it so we can read the width/height.
|
||||
data->load();
|
||||
}
|
||||
|
@ -148,6 +154,7 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
|
|||
bool forceLoad,
|
||||
bool dynamic,
|
||||
bool linearMagnify,
|
||||
bool alwaysRasterize,
|
||||
float scaleDuringLoad)
|
||||
{
|
||||
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
|
||||
|
@ -155,7 +162,7 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
|
|||
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path);
|
||||
if (canonicalPath.empty()) {
|
||||
std::shared_ptr<TextureResource> tex(
|
||||
new TextureResource("", tile, false, linearMagnify, scaleDuringLoad));
|
||||
new TextureResource("", tile, false, linearMagnify, alwaysRasterize, scaleDuringLoad));
|
||||
// Make sure we get properly deinitialized even though we do nothing on reinitialization.
|
||||
rm->addReloadable(tex);
|
||||
return tex;
|
||||
|
@ -171,12 +178,13 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
|
|||
|
||||
// Need to create it.
|
||||
std::shared_ptr<TextureResource> tex;
|
||||
tex = std::shared_ptr<TextureResource>(
|
||||
new TextureResource(key.first, tile, dynamic, linearMagnify, scaleDuringLoad));
|
||||
tex = std::shared_ptr<TextureResource>(new TextureResource(
|
||||
key.first, tile, dynamic, linearMagnify, alwaysRasterize, scaleDuringLoad));
|
||||
std::shared_ptr<TextureData> data = sTextureDataManager.get(tex.get());
|
||||
|
||||
// Is it an SVG?
|
||||
if (key.first.substr(key.first.size() - 4, std::string::npos) != ".svg") {
|
||||
if (Utils::String::toLower(key.first.substr(key.first.size() - 4, std::string::npos)) !=
|
||||
".svg") {
|
||||
// Probably not. Add it to our map. We don't add SVGs because 2 SVGs might be
|
||||
// rasterized at different sizes.
|
||||
sTextureMap[key] = std::weak_ptr<TextureResource>(tex);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define ES_CORE_RESOURCES_TEXTURE_RESOURCE_H
|
||||
|
||||
#include "resources/ResourceManager.h"
|
||||
#include "resources/TextureData.h"
|
||||
#include "resources/TextureDataManager.h"
|
||||
#include "utils/MathUtil.h"
|
||||
|
||||
|
@ -30,6 +31,7 @@ public:
|
|||
bool forceLoad = false,
|
||||
bool dynamic = true,
|
||||
bool linearMagnify = false,
|
||||
bool alwaysRasterize = false,
|
||||
float scaleDuringLoad = 1.0f);
|
||||
void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height);
|
||||
virtual void initFromMemory(const char* data, size_t length);
|
||||
|
@ -38,6 +40,9 @@ public:
|
|||
// Returns the raw pixel values.
|
||||
std::vector<unsigned char> getRawRGBAData();
|
||||
|
||||
// Has the image been loaded but not yet been rasterized as the size was not known?
|
||||
bool getPendingRasterization() { return mTextureData->getPendingRasterization(); }
|
||||
|
||||
std::string getTextureFilePath();
|
||||
|
||||
// For SVG graphics this function effectively rescales the image to the defined size.
|
||||
|
@ -65,6 +70,7 @@ protected:
|
|||
bool tile,
|
||||
bool dynamic,
|
||||
bool linearMagnify,
|
||||
bool alwaysRasterize,
|
||||
float scaleDuringLoad);
|
||||
virtual void unload(std::shared_ptr<ResourceManager>& rm);
|
||||
virtual void reload(std::shared_ptr<ResourceManager>& rm);
|
||||
|
|
Loading…
Reference in a new issue