Added support for texture mipmapping with trilinear filtering.

This commit is contained in:
Leon Styhre 2022-09-05 23:36:49 +02:00
parent 1bbf32a6f5
commit 9d46625739
12 changed files with 52 additions and 20 deletions

View file

@ -48,6 +48,7 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic)
, mDynamic {dynamic} , mDynamic {dynamic}
, mRotateByTargetSize {false} , mRotateByTargetSize {false}
, mLinearInterpolation {false} , mLinearInterpolation {false}
, mMipmapping {false}
, mTopLeftCrop {0.0f, 0.0f} , mTopLeftCrop {0.0f, 0.0f}
, mBottomRightCrop {1.0f, 1.0f} , mBottomRightCrop {1.0f, 1.0f}
, mClipRegion {0.0f, 0.0f, 0.0f, 0.0f} , mClipRegion {0.0f, 0.0f, 0.0f, 0.0f}
@ -137,7 +138,7 @@ void ImageComponent::setImage(const std::string& path, bool tile)
mTexture.reset(); mTexture.reset();
else else
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic, mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic,
mLinearInterpolation); mLinearInterpolation, mMipmapping);
resize(true); resize(true);
} }
else { else {
@ -147,14 +148,14 @@ void ImageComponent::setImage(const std::string& path, bool tile)
// we perform the actual rasterization to have the cache entry updated with the proper // we perform the actual rasterization to have the cache entry updated with the proper
// texture. For SVG images this requires that every call to setImage is made only after // texture. For SVG images this requires that every call to setImage is made only after
// a call to setResize or setMaxSize (so the requested size is known upfront). // a call to setResize or setMaxSize (so the requested size is known upfront).
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation, 0, mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation,
0, 0.0f, 0.0f); mMipmapping, 0, 0, 0.0f, 0.0f);
if (isScalable) { if (isScalable) {
resize(false); resize(false);
mTexture.reset(); mTexture.reset();
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation, mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation,
static_cast<size_t>(mSize.x), mMipmapping, static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y), mTileWidth, mTileHeight); static_cast<size_t>(mSize.y), mTileWidth, mTileHeight);
mTexture->setScalableNonAspect(mScalableNonAspect); mTexture->setScalableNonAspect(mScalableNonAspect);
mTexture->rasterizeAt(mSize.x, mSize.y); mTexture->rasterizeAt(mSize.x, mSize.y);

View file

@ -92,6 +92,8 @@ public:
void setRotateByTargetSize(bool rotate) { mRotateByTargetSize = rotate; } void setRotateByTargetSize(bool rotate) { mRotateByTargetSize = rotate; }
// Whether to use smooth texture magnification by utilizing linear interpolation. // Whether to use smooth texture magnification by utilizing linear interpolation.
void setLinearInterpolation(bool state) { mLinearInterpolation = state; } void setLinearInterpolation(bool state) { mLinearInterpolation = state; }
// Whether to use mipmapping and trilinear filtering.
void setMipmapping(bool state) { mMipmapping = state; }
// Returns the size of the current texture, or (0, 0) if none is loaded. // Returns the size of the current texture, or (0, 0) if none is loaded.
// May be different than drawn size (use getSize() for that). // May be different than drawn size (use getSize() for that).
@ -152,6 +154,7 @@ private:
bool mDynamic; bool mDynamic;
bool mRotateByTargetSize; bool mRotateByTargetSize;
bool mLinearInterpolation; bool mLinearInterpolation;
bool mMipmapping;
glm::vec2 mTopLeftCrop; glm::vec2 mTopLeftCrop;
glm::vec2 mBottomRightCrop; glm::vec2 mBottomRightCrop;

View file

@ -66,7 +66,7 @@ void NinePatchComponent::buildVertices()
} }
glm::vec2 texSize {relCornerSize * 3.0f}; glm::vec2 texSize {relCornerSize * 3.0f};
mTexture = TextureResource::get(mPath, false, false, false, false, mTexture = TextureResource::get(mPath, false, false, false, false, false,
static_cast<size_t>(texSize.x), static_cast<size_t>(texSize.y)); static_cast<size_t>(texSize.x), static_cast<size_t>(texSize.y));
mTexture->rasterizeAt(texSize.x, texSize.y); mTexture->rasterizeAt(texSize.x, texSize.y);

View file

@ -179,7 +179,7 @@ void RatingComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
std::string path {std::string(elem->get<std::string>("filledPath"))}; std::string path {std::string(elem->get<std::string>("filledPath"))};
if (Utils::FileSystem::isRegularFile(path) || Utils::FileSystem::isSymlink(path)) { if (Utils::FileSystem::isRegularFile(path) || Utils::FileSystem::isSymlink(path)) {
auto tempImage = auto tempImage =
TextureResource::get(path, false, false, false, false, 0, 0, 0.0f, 0.0f); TextureResource::get(path, false, false, false, false, false, 0, 0, 0.0f, 0.0f);
mImageRatio = static_cast<float>(tempImage->getSize().x) / mImageRatio = static_cast<float>(tempImage->getSize().x) /
static_cast<float>(tempImage->getSize().y); static_cast<float>(tempImage->getSize().y);
} }

View file

@ -210,6 +210,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
(!defaultPath.empty() && ResourceManager::getInstance().fileExists(defaultPath))) { (!defaultPath.empty() && ResourceManager::getInstance().fileExists(defaultPath))) {
auto item = std::make_shared<ImageComponent>(false, dynamic); auto item = std::make_shared<ImageComponent>(false, dynamic);
item->setLinearInterpolation(mLinearInterpolation); item->setLinearInterpolation(mLinearInterpolation);
item->setMipmapping(true);
item->setMaxSize(mItemSize * mItemScale); item->setMaxSize(mItemSize * mItemScale);
item->applyTheme(theme, "system", "image_logo", item->applyTheme(theme, "system", "image_logo",
ThemeFlags::PATH | ThemeFlags::COLOR); ThemeFlags::PATH | ThemeFlags::COLOR);
@ -223,6 +224,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
ResourceManager::getInstance().fileExists(entry.data.itemPath)) { ResourceManager::getInstance().fileExists(entry.data.itemPath)) {
auto item = std::make_shared<ImageComponent>(false, dynamic); auto item = std::make_shared<ImageComponent>(false, dynamic);
item->setLinearInterpolation(mLinearInterpolation); item->setLinearInterpolation(mLinearInterpolation);
item->setMipmapping(true);
item->setMaxSize(mItemSize * mItemScale); item->setMaxSize(mItemSize * mItemScale);
item->setImage(entry.data.itemPath); item->setImage(entry.data.itemPath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
@ -233,6 +235,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
ResourceManager::getInstance().fileExists(entry.data.defaultItemPath)) { ResourceManager::getInstance().fileExists(entry.data.defaultItemPath)) {
auto defaultItem = std::make_shared<ImageComponent>(false, dynamic); auto defaultItem = std::make_shared<ImageComponent>(false, dynamic);
defaultItem->setLinearInterpolation(mLinearInterpolation); defaultItem->setLinearInterpolation(mLinearInterpolation);
defaultItem->setMipmapping(true);
defaultItem->setMaxSize(mItemSize * mItemScale); defaultItem->setMaxSize(mItemSize * mItemScale);
defaultItem->setImage(entry.data.defaultItemPath); defaultItem->setImage(entry.data.defaultItemPath);
defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL); defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL);
@ -306,6 +309,7 @@ void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<Theme
if (entry.data.itemPath != "") { if (entry.data.itemPath != "") {
auto item = std::make_shared<ImageComponent>(false, true); auto item = std::make_shared<ImageComponent>(false, true);
item->setLinearInterpolation(mLinearInterpolation); item->setLinearInterpolation(mLinearInterpolation);
item->setMipmapping(true);
item->setMaxSize(mItemSize * mItemScale); item->setMaxSize(mItemSize * mItemScale);
item->setImage(entry.data.itemPath); item->setImage(entry.data.itemPath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);

View file

@ -184,6 +184,7 @@ public:
virtual unsigned int createTexture(const TextureType type, virtual unsigned int createTexture(const TextureType type,
const bool linearMinify, const bool linearMinify,
const bool linearMagnify, const bool linearMagnify,
const bool mipmapping,
const bool repeat, const bool repeat,
const unsigned int width, const unsigned int width,
const unsigned int height, const unsigned int height,

View file

@ -240,13 +240,13 @@ bool RendererOpenGL::createContext()
GL_CHECK_ERROR(glBindVertexArray(mVertexBuffer2)); GL_CHECK_ERROR(glBindVertexArray(mVertexBuffer2));
uint8_t data[4] {255, 255, 255, 255}; uint8_t data[4] {255, 255, 255, 255};
mWhiteTexture = createTexture(TextureType::RGBA, false, false, true, 1, 1, data); mWhiteTexture = createTexture(TextureType::RGBA, false, false, false, true, 1, 1, data);
mPostProcTexture1 = createTexture(TextureType::RGBA, false, false, false, mPostProcTexture1 = createTexture(TextureType::RGBA, false, false, false, false,
static_cast<unsigned int>(getScreenWidth()), static_cast<unsigned int>(getScreenWidth()),
static_cast<unsigned int>(getScreenHeight()), nullptr); static_cast<unsigned int>(getScreenHeight()), nullptr);
mPostProcTexture2 = createTexture(TextureType::RGBA, false, false, false, mPostProcTexture2 = createTexture(TextureType::RGBA, false, false, false, false,
static_cast<unsigned int>(getScreenWidth()), static_cast<unsigned int>(getScreenWidth()),
static_cast<unsigned int>(getScreenHeight()), nullptr); static_cast<unsigned int>(getScreenHeight()), nullptr);
@ -277,8 +277,8 @@ void RendererOpenGL::destroyContext()
void RendererOpenGL::setMatrix(const glm::mat4& matrix) void RendererOpenGL::setMatrix(const glm::mat4& matrix)
{ {
mTrans = matrix; // Calculate the projection matrix.
mTrans = getProjectionMatrix() * mTrans; mTrans = getProjectionMatrix() * matrix;
} }
void RendererOpenGL::setScissor(const Rect& scissor) void RendererOpenGL::setScissor(const Rect& scissor)
@ -337,6 +337,7 @@ void RendererOpenGL::swapBuffers()
unsigned int RendererOpenGL::createTexture(const TextureType type, unsigned int RendererOpenGL::createTexture(const TextureType type,
const bool linearMinify, const bool linearMinify,
const bool linearMagnify, const bool linearMagnify,
const bool mipmapping,
const bool repeat, const bool repeat,
const unsigned int width, const unsigned int width,
const unsigned int height, const unsigned int height,
@ -354,15 +355,25 @@ unsigned int RendererOpenGL::createTexture(const TextureType type,
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, if (mipmapping) {
linearMinify ? static_cast<GLfloat>(GL_LINEAR) : GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
static_cast<GLfloat>(GL_NEAREST))); static_cast<GLfloat>(GL_LINEAR_MIPMAP_LINEAR)));
}
else {
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
linearMinify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST)));
}
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linearMagnify ? static_cast<GLfloat>(GL_LINEAR) : linearMagnify ? static_cast<GLfloat>(GL_LINEAR) :
static_cast<GLfloat>(GL_NEAREST))); static_cast<GLfloat>(GL_NEAREST)));
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType,
GL_UNSIGNED_BYTE, data)); GL_UNSIGNED_BYTE, data));
if (mipmapping)
GL_CHECK_ERROR(glGenerateMipmap(GL_TEXTURE_2D));
return texture; return texture;
} }

View file

@ -46,6 +46,7 @@ public:
unsigned int createTexture(const TextureType type, unsigned int createTexture(const TextureType type,
const bool linearMinify, const bool linearMinify,
const bool linearMagnify, const bool linearMagnify,
const bool mipmapping,
const bool repeat, const bool repeat,
const unsigned int width, const unsigned int width,
const unsigned int height, const unsigned int height,

View file

@ -39,6 +39,7 @@ TextureData::TextureData(bool tile)
, mHasRGBAData {false} , mHasRGBAData {false}
, mPendingRasterization {false} , mPendingRasterization {false}
, mLinearMagnify {false} , mLinearMagnify {false}
, mMipmapping {false}
{ {
} }
@ -240,8 +241,8 @@ bool TextureData::uploadAndBind()
// Upload texture. // Upload texture.
mTextureID = mTextureID =
mRenderer->createTexture(Renderer::TextureType::RGBA, true, mLinearMagnify, mTile, mRenderer->createTexture(Renderer::TextureType::RGBA, true, mLinearMagnify, mMipmapping,
static_cast<const unsigned int>(mWidth), 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

@ -70,6 +70,8 @@ public:
void setScalableNonAspect(bool state) { mScalableNonAspect = state; } void setScalableNonAspect(bool state) { mScalableNonAspect = state; }
// Whether to use linear filtering when magnifying the texture. // Whether to use linear filtering when magnifying the texture.
void setLinearMagnify(bool state) { mLinearMagnify = state; } void setLinearMagnify(bool state) { mLinearMagnify = state; }
// Whether to use mipmapping and trilinear filtering.
void setMipmapping(bool state) { mMipmapping = state; }
// Has the image been loaded but not yet been rasterized as the size was not known? // Has the image been loaded but not yet been rasterized as the size was not known?
const bool getPendingRasterization() { return mPendingRasterization; } const bool getPendingRasterization() { return mPendingRasterization; }
@ -98,6 +100,7 @@ private:
std::atomic<bool> mHasRGBAData; std::atomic<bool> mHasRGBAData;
std::atomic<bool> mPendingRasterization; std::atomic<bool> mPendingRasterization;
bool mLinearMagnify; bool mLinearMagnify;
bool mMipmapping;
bool mReloadable; bool mReloadable;
}; };

View file

@ -20,6 +20,7 @@ TextureResource::TextureResource(const std::string& path,
bool tile, bool tile,
bool dynamic, bool dynamic,
bool linearMagnify, bool linearMagnify,
bool mipmapping,
bool scalable) bool scalable)
: mTextureData {nullptr} : mTextureData {nullptr}
, mForceLoad {false} , mForceLoad {false}
@ -35,6 +36,7 @@ TextureResource::TextureResource(const std::string& path,
data->initFromPath(path); data->initFromPath(path);
data->setTileSize(tileWidth, tileHeight); data->setTileSize(tileWidth, tileHeight);
data->setLinearMagnify(linearMagnify); data->setLinearMagnify(linearMagnify);
data->setMipmapping(mipmapping);
// 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);
} }
@ -44,6 +46,7 @@ TextureResource::TextureResource(const std::string& path,
data->initFromPath(path); data->initFromPath(path);
data->setTileSize(tileWidth, tileHeight); data->setTileSize(tileWidth, tileHeight);
data->setLinearMagnify(linearMagnify); data->setLinearMagnify(linearMagnify);
data->setMipmapping(mipmapping);
// Load it so we can read the width/height. // Load it so we can read the width/height.
data->load(); data->load();
} }
@ -154,6 +157,7 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
bool forceLoad, bool forceLoad,
bool dynamic, bool dynamic,
bool linearMagnify, bool linearMagnify,
bool mipmapping,
size_t width, size_t width,
size_t height, size_t height,
float tileWidth, float tileWidth,
@ -161,8 +165,8 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
{ {
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( std::shared_ptr<TextureResource> tex(new TextureResource(
new TextureResource("", tileWidth, tileHeight, tile, false, linearMagnify, false)); "", tileWidth, tileHeight, tile, false, linearMagnify, mipmapping, false));
// 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.
ResourceManager::getInstance().addReloadable(tex); ResourceManager::getInstance().addReloadable(tex);
return tex; return tex;
@ -207,8 +211,9 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
} }
// Need to create it. // Need to create it.
std::shared_ptr<TextureResource> tex {std::shared_ptr<TextureResource>(new TextureResource( std::shared_ptr<TextureResource> tex {std::shared_ptr<TextureResource>(
std::get<0>(key), tileWidth, tileHeight, tile, dynamic, linearMagnify, isScalable))}; new TextureResource(std::get<0>(key), tileWidth, tileHeight, tile, dynamic, linearMagnify,
mipmapping, isScalable))};
std::shared_ptr<TextureData> data {sTextureDataManager.get(tex.get())}; std::shared_ptr<TextureData> data {sTextureDataManager.get(tex.get())};
if (!isScalable || (isScalable && width != 0.0f && height != 0.0f)) { if (!isScalable || (isScalable && width != 0.0f && height != 0.0f)) {

View file

@ -31,6 +31,7 @@ public:
bool forceLoad = false, bool forceLoad = false,
bool dynamic = true, bool dynamic = true,
bool linearMagnify = false, bool linearMagnify = false,
bool mipmapping = false,
size_t width = 0, size_t width = 0,
size_t height = 0, size_t height = 0,
float tileWidth = 0.0f, float tileWidth = 0.0f,
@ -86,6 +87,7 @@ protected:
bool tile, bool tile,
bool dynamic, bool dynamic,
bool linearMagnify, bool linearMagnify,
bool mipmapping,
bool scalable); bool scalable);
virtual void unload(ResourceManager& rm); virtual void unload(ResourceManager& rm);
virtual void reload(ResourceManager& rm); virtual void reload(ResourceManager& rm);