Added support for upscaling bitmap images using linear filtering.

This commit is contained in:
Leon Styhre 2021-08-19 20:16:42 +02:00
parent 8e25609d4f
commit 6e882dd564
11 changed files with 80 additions and 57 deletions

View file

@ -138,7 +138,7 @@ void ImageComponent::resize()
onSizeChanged(); onSizeChanged();
} }
void ImageComponent::setImage(std::string path, bool tile) void ImageComponent::setImage(std::string path, bool tile, bool linearMagnify)
{ {
// Always load bundled graphic resources statically, unless mForceLoad has been set. // Always load bundled graphic resources statically, unless mForceLoad has been set.
// This eliminates annoying texture pop-in problems that would otherwise occur. // This eliminates annoying texture pop-in problems that would otherwise occur.
@ -150,10 +150,11 @@ void ImageComponent::setImage(std::string path, bool tile)
if (mDefaultPath.empty() || !ResourceManager::getInstance()->fileExists(mDefaultPath)) if (mDefaultPath.empty() || !ResourceManager::getInstance()->fileExists(mDefaultPath))
mTexture.reset(); mTexture.reset();
else else
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic); mTexture =
TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic, linearMagnify);
} }
else { else {
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic); mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, linearMagnify);
} }
resize(); resize();

View file

@ -24,7 +24,7 @@ public:
// Loads the image at the given filepath. Will tile if tile is true (retrieves texture // Loads the image at the given filepath. Will tile if tile is true (retrieves texture
// as tiling, creates vertices accordingly). // as tiling, creates vertices accordingly).
void setImage(std::string path, bool tile = false); void setImage(std::string path, bool tile = false, bool linearMagnify = false);
// Loads an image from memory. // Loads an image from memory.
void setImage(const char* data, size_t length, bool tile = false); void setImage(const char* data, size_t length, bool tile = false);
// Use an already existing texture. // Use an already existing texture.

View file

@ -59,7 +59,7 @@ void NinePatchComponent::buildVertices()
else else
scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f);
mTexture = TextureResource::get(mPath, false, false, true, scaleFactor); mTexture = TextureResource::get(mPath, false, false, true, true, scaleFactor);
if (mTexture->getSize() == glm::ivec2{}) { if (mTexture->getSize() == glm::ivec2{}) {
mVertices = nullptr; mVertices = nullptr;

View file

@ -167,12 +167,13 @@ namespace Renderer
void setupWindow(); void setupWindow();
bool createContext(); bool createContext();
void destroyContext(); void destroyContext();
unsigned int createTexture(const Texture::Type _type, unsigned int createTexture(const Texture::Type type,
const bool _linear, const bool linearMinify,
const bool _repeat, const bool linearMagnify,
const unsigned int _width, const bool repeat,
const unsigned int _height, const unsigned int width,
void* _data); const unsigned int height,
void* data);
void destroyTexture(const unsigned int _texture); void destroyTexture(const unsigned int _texture);
void updateTexture(const unsigned int _texture, void updateTexture(const unsigned int _texture,
const Texture::Type _type, const Texture::Type _type,

View file

@ -138,7 +138,7 @@ namespace Renderer
} }
uint8_t data[4] = {255, 255, 255, 255}; uint8_t data[4] = {255, 255, 255, 255};
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); whiteTexture = createTexture(Texture::RGBA, false, false, true, 1, 1, data);
GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D)); GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D));
@ -162,32 +162,35 @@ namespace Renderer
sdlContext = nullptr; sdlContext = nullptr;
} }
unsigned int createTexture(const Texture::Type _type, unsigned int createTexture(const Texture::Type type,
const bool _linear, const bool linearMinify,
const bool _repeat, const bool linearMagnify,
const unsigned int _width, const bool repeat,
const unsigned int _height, const unsigned int width,
void* _data) const unsigned int height,
void* data)
{ {
const GLenum type = convertTextureType(_type); const GLenum textureType = convertTextureType(type);
unsigned int texture; unsigned int texture;
GL_CHECK_ERROR(glGenTextures(1, &texture)); GL_CHECK_ERROR(glGenTextures(1, &texture));
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
_repeat ? static_cast<GLfloat>(GL_REPEAT) : repeat ? static_cast<GLfloat>(GL_REPEAT) :
static_cast<GLfloat>(GL_CLAMP_TO_EDGE))); static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
_repeat ? static_cast<GLfloat>(GL_REPEAT) : repeat ? static_cast<GLfloat>(GL_REPEAT) :
static_cast<GLfloat>(GL_CLAMP_TO_EDGE))); static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
_linear ? static_cast<GLfloat>(GL_LINEAR) : linearMinify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST)));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linearMagnify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST))); static_cast<GLfloat>(GL_NEAREST)));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type, GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType,
GL_UNSIGNED_BYTE, _data)); GL_UNSIGNED_BYTE, data));
return texture; return texture;
} }
@ -448,7 +451,8 @@ namespace Renderer
vertices[0].saturation = parameters.fragmentSaturation; vertices[0].saturation = parameters.fragmentSaturation;
setMatrix(getIdentity()); setMatrix(getIdentity());
GLuint screenTexture = createTexture(Texture::RGBA, false, false, width, height, nullptr); GLuint screenTexture =
createTexture(Texture::RGBA, false, false, false, width, height, nullptr);
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));

View file

@ -91,7 +91,7 @@ namespace Renderer
"MISSING"); "MISSING");
uint8_t data[4] = {255, 255, 255, 255}; uint8_t data[4] = {255, 255, 255, 255};
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); whiteTexture = createTexture(Texture::RGBA, false, false, true, 1, 1, data);
GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D)); GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D));
@ -111,29 +111,31 @@ namespace Renderer
sdlContext = nullptr; sdlContext = nullptr;
} }
unsigned int createTexture(const Texture::Type _type, unsigned int createTexture(const Texture::Type type,
const bool _linear, const bool linearMinify,
const bool _repeat, const bool linearMagnify,
const unsigned int _width, const bool repeat,
const unsigned int _height, const unsigned int width,
void* _data) const unsigned int height,
void* data)
{ {
const GLenum type = convertTextureType(_type); const GLenum textureType = convertTextureType(type);
unsigned int texture; unsigned int texture;
GL_CHECK_ERROR(glGenTextures(1, &texture)); GL_CHECK_ERROR(glGenTextures(1, &texture));
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
_repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
_repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
_linear ? GL_LINEAR : GL_NEAREST)); linearMinify ? GL_LINEAR : GL_NEAREST));
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linearMagnify ? GL_LINEAR : GL_NEAREST));
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type, GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType,
GL_UNSIGNED_BYTE, _data)); GL_UNSIGNED_BYTE, data));
return texture; return texture;
} }

View file

@ -179,8 +179,8 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out
void Font::FontTexture::initTexture() void Font::FontTexture::initTexture()
{ {
assert(textureId == 0); assert(textureId == 0);
textureId = Renderer::createTexture(Renderer::Texture::ALPHA, false, false, textureSize.x, textureId = Renderer::createTexture(Renderer::Texture::ALPHA, false, false, false,
textureSize.y, nullptr); textureSize.x, textureSize.y, nullptr);
} }
void Font::FontTexture::deinitTexture() void Font::FontTexture::deinitTexture()

View file

@ -28,6 +28,7 @@ TextureData::TextureData(bool tile)
, mDataRGBA({}) , mDataRGBA({})
, mScaleDuringLoad(1.0f) , mScaleDuringLoad(1.0f)
, mScalable(false) , mScalable(false)
, mLinearMagnify(false)
, mWidth(0) , mWidth(0)
, mHeight(0) , mHeight(0)
, mSourceWidth(0.0f) , mSourceWidth(0.0f)
@ -196,8 +197,9 @@ bool TextureData::uploadAndBind()
return false; return false;
// Upload texture. // Upload texture.
mTextureID = Renderer::createTexture( mTextureID =
Renderer::Texture::RGBA, true, mTile, static_cast<const unsigned int>(mWidth), Renderer::createTexture(Renderer::Texture::RGBA, true, mLinearMagnify, mTile,
static_cast<const unsigned int>(mWidth),
static_cast<const unsigned int>(mHeight), mDataRGBA.data()); static_cast<const unsigned int>(mHeight), mDataRGBA.data());
} }
return true; return true;

View file

@ -56,6 +56,8 @@ public:
// Define a factor for scaling the file when loading it (1.0f = no scaling). // Define a factor for scaling the file when loading it (1.0f = no scaling).
void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; } void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; }
// Whether to use linear filtering when magnifying the texture.
void setLinearMagnify(bool setting) { mLinearMagnify = setting; }
std::vector<unsigned char> getRawRGBAData() { return mDataRGBA; } std::vector<unsigned char> getRawRGBAData() { return mDataRGBA; }
std::string getTextureFilePath() { return mPath; } std::string getTextureFilePath() { return mPath; }
@ -73,6 +75,7 @@ private:
float mSourceHeight; float mSourceHeight;
float mScaleDuringLoad; float mScaleDuringLoad;
bool mScalable; bool mScalable;
bool mLinearMagnify;
bool mReloadable; bool mReloadable;
}; };

View file

@ -16,10 +16,8 @@ std::map<TextureResource::TextureKeyType, std::weak_ptr<TextureResource>>
TextureResource::sTextureMap; TextureResource::sTextureMap;
std::set<TextureResource*> TextureResource::sAllTextures; std::set<TextureResource*> TextureResource::sAllTextures;
TextureResource::TextureResource(const std::string& path, TextureResource::TextureResource(
bool tile, const std::string& path, bool tile, bool dynamic, bool linearMagnify, float scaleDuringLoad)
bool dynamic,
float scaleDuringLoad)
: mTextureData(nullptr) : mTextureData(nullptr)
, mForceLoad(false) , mForceLoad(false)
{ {
@ -33,6 +31,7 @@ TextureResource::TextureResource(const std::string& path,
data->initFromPath(path); data->initFromPath(path);
if (scaleDuringLoad != 1.0f) if (scaleDuringLoad != 1.0f)
data->setScaleDuringLoad(scaleDuringLoad); data->setScaleDuringLoad(scaleDuringLoad);
data->setLinearMagnify(linearMagnify);
// Force the texture manager to load it using a blocking load. // Force the texture manager to load it using a blocking load.
sTextureDataManager.load(data, true); sTextureDataManager.load(data, true);
} }
@ -42,6 +41,7 @@ TextureResource::TextureResource(const std::string& path,
data->initFromPath(path); data->initFromPath(path);
if (scaleDuringLoad != 1.0f) if (scaleDuringLoad != 1.0f)
data->setScaleDuringLoad(scaleDuringLoad); data->setScaleDuringLoad(scaleDuringLoad);
data->setLinearMagnify(linearMagnify);
// Load it so we can read the width/height. // Load it so we can read the width/height.
data->load(); data->load();
} }
@ -143,14 +143,19 @@ bool TextureResource::bind()
} }
} }
std::shared_ptr<TextureResource> TextureResource::get( std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
const std::string& path, bool tile, bool forceLoad, bool dynamic, float scaleDuringLoad) bool tile,
bool forceLoad,
bool dynamic,
bool linearMagnify,
float scaleDuringLoad)
{ {
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance(); std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path);
if (canonicalPath.empty()) { if (canonicalPath.empty()) {
std::shared_ptr<TextureResource> tex(new TextureResource("", tile, false, scaleDuringLoad)); std::shared_ptr<TextureResource> tex(
new TextureResource("", tile, false, linearMagnify, scaleDuringLoad));
// Make sure we get properly deinitialized even though we do nothing on reinitialization. // Make sure we get properly deinitialized even though we do nothing on reinitialization.
rm->addReloadable(tex); rm->addReloadable(tex);
return tex; return tex;
@ -167,7 +172,7 @@ std::shared_ptr<TextureResource> TextureResource::get(
// Need to create it. // Need to create it.
std::shared_ptr<TextureResource> tex; std::shared_ptr<TextureResource> tex;
tex = std::shared_ptr<TextureResource>( tex = std::shared_ptr<TextureResource>(
new TextureResource(key.first, tile, dynamic, scaleDuringLoad)); new TextureResource(key.first, tile, dynamic, linearMagnify, scaleDuringLoad));
std::shared_ptr<TextureData> data = sTextureDataManager.get(tex.get()); std::shared_ptr<TextureData> data = sTextureDataManager.get(tex.get());
// Is it an SVG? // Is it an SVG?

View file

@ -29,6 +29,7 @@ public:
bool tile = false, bool tile = false,
bool forceLoad = false, bool forceLoad = false,
bool dynamic = true, bool dynamic = true,
bool linearMagnify = false,
float scaleDuringLoad = 1.0f); float scaleDuringLoad = 1.0f);
void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height);
virtual void initFromMemory(const char* data, size_t length); virtual void initFromMemory(const char* data, size_t length);
@ -60,7 +61,11 @@ public:
static size_t getTotalTextureSize(); static size_t getTotalTextureSize();
protected: protected:
TextureResource(const std::string& path, bool tile, bool dynamic, float scaleDuringLoad); TextureResource(const std::string& path,
bool tile,
bool dynamic,
bool linearMagnify,
float scaleDuringLoad);
virtual void unload(std::shared_ptr<ResourceManager>& rm); virtual void unload(std::shared_ptr<ResourceManager>& rm);
virtual void reload(std::shared_ptr<ResourceManager>& rm); virtual void reload(std::shared_ptr<ResourceManager>& rm);