Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImGuiIntegration: use single-channel format for font texture atlas, if available #115

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions src/Magnum/ImGuiIntegration/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/Resource.h>
#include <Magnum/ImageView.h>
#include <Magnum/PixelFormat.h>
#include <Magnum/GL/Context.h>
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/GL/Extensions.h>
Expand Down Expand Up @@ -196,27 +197,45 @@ void Context::relayout(const Vector2& size, const Vector2i& windowSize, const Ve
/* Downscale back the upscaled font to achieve supersampling */
io.FontGlobalScale = 1.0f/nonZeroSupersamplingRatio;

PixelFormat format;
unsigned char *pixels;
int width, height;
int pixelSize;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &pixelSize);
CORRADE_INTERNAL_ASSERT(width > 0 && height > 0 && pixelSize == 4);
/* If the texture atlas only requires alpha we can use a single-channel
format and swizzle, if available, to conserve memory */
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if(!io.Fonts->TexPixelsUseColors) {
format = PixelFormat::R8Unorm;
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height, &pixelSize);
} else
#endif
{
format = PixelFormat::RGBA8Unorm;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &pixelSize);
}
CORRADE_INTERNAL_ASSERT(width > 0 && height > 0 && std::size_t(pixelSize) == pixelFormatSize(format));

ImageView2D image{GL::PixelFormat::RGBA,
GL::PixelType::UnsignedByte, {width, height},
/* Atlas width is guaranteed to be a power-of-two, so the default
PixelStorage works even for the single-channel format */
const ImageView2D image{format, {width, height},
{pixels, std::size_t(pixelSize*width*height)}};

_texture = GL::Texture2D{};
_texture.setMagnificationFilter(GL::SamplerFilter::Linear)
.setMinificationFilter(GL::SamplerFilter::Linear)
#ifndef MAGNUM_TARGET_GLES2
.setStorage(1, GL::TextureFormat::RGBA8, image.size())
.setStorage(1, GL::textureFormat(format), image.size())
.setSubImage(0, {}, image)
#else
.setImage(0, GL::TextureFormat::RGBA, image)
.setImage(0, GL::textureFormat(format), image)
#endif
;

#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if(!io.Fonts->TexPixelsUseColors)
_texture.setSwizzle<'1', '1', '1', 'r'>();
#endif

/* Clear texture to save RAM, we have it on the GPU now */
io.Fonts->ClearTexData();

Expand Down
4 changes: 3 additions & 1 deletion src/Magnum/ImGuiIntegration/Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# 2020, 2021, 2022, 2023, 2024
# Vladimír Vondruš <[email protected]>
# Copyright © 2018 Jonathan Hale <[email protected]>
# Copyright © 2022 Pablo Escobar <[email protected]>
# Copyright © 2022, 2024 Pablo Escobar <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
Expand Down Expand Up @@ -63,6 +63,8 @@ if(MAGNUM_BUILD_GL_TESTS)
ContextTestFiles/draw.png
ContextTestFiles/draw-scissor.png
ContextTestFiles/draw-texture.png
ContextTestFiles/draw-text.png
ContextTestFiles/draw-text-color-font.png
ContextTestFiles/texture.png)
target_include_directories(ImGuiContextGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
if(MAGNUM_IMGUIINTEGRATION_BUILD_STATIC)
Expand Down
125 changes: 125 additions & 0 deletions src/Magnum/ImGuiIntegration/Test/ContextGLTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ struct ContextGLTest: GL::OpenGLTester {
void draw();
void drawCallback();
void drawTexture();
void drawText();
void drawTextColorFont();
void drawScissor();
void drawVertexOffset();
void drawIndexOffset();
Expand Down Expand Up @@ -313,6 +315,8 @@ ContextGLTest::ContextGLTest() {
addTests({&ContextGLTest::draw,
&ContextGLTest::drawCallback,
&ContextGLTest::drawTexture,
&ContextGLTest::drawText,
&ContextGLTest::drawTextColorFont,
&ContextGLTest::drawScissor,
&ContextGLTest::drawVertexOffset,
&ContextGLTest::drawIndexOffset},
Expand Down Expand Up @@ -1397,6 +1401,127 @@ void ContextGLTest::drawTexture() {
(DebugTools::CompareImageToFile{_manager, 1.0f, 0.5f}));
}

void ContextGLTest::drawText() {
Context c{{200, 200}};

/* Scale up default font so large text output is not a complete blurry mess */
ImGui::GetIO().Fonts->Clear();
auto* font = ImGui::GetIO().Fonts->AddFontDefault();
font->Scale = 8.0f;

c.relayout({200, 200}, {70, 70}, _framebuffer.viewport().size());

MAGNUM_VERIFY_NO_GL_ERROR();

/* ImGui doesn't draw anything the first frame */
c.newFrame();
c.drawFrame();

MAGNUM_VERIFY_NO_GL_ERROR();

Utility::System::sleep(1);

c.newFrame();

/* Last drawlist that gets rendered, covers the entire display */
ImDrawList* drawList = ImGui::GetForegroundDrawList();
const ImVec2& size = ImGui::GetIO().DisplaySize;

drawList->AddRectFilled({size.x*0.1f, size.y*0.2f}, {size.x*0.9f, size.y*0.8f},
IM_COL32(255, 128, 128, 255));
drawList->AddText(nullptr, font->FontSize*font->Scale,
{size.x*0.3f, size.y*0.3f}, IM_COL32(255, 255, 0, 200), "ABC");

c.drawFrame();

MAGNUM_VERIFY_NO_GL_ERROR();

/* Catch also ABI and interface mismatch errors */
if(!(_manager.load("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.load("PngImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / PngImporter plugin can't be loaded.");

CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Path::join(IMGUIINTEGRATION_TEST_DIR, "ContextTestFiles/draw-text.png"),
(DebugTools::CompareImageToFile{_manager, 3.0f, 0.5f}));
}

void ContextGLTest::drawTextColorFont() {
Context c{{200, 200}};

/* Scale up default font so large text output is not a complete blurry mess */
ImGui::GetIO().Fonts->Clear();
auto* font = ImGui::GetIO().Fonts->AddFontDefault();
font->Scale = 8.0f;

/* Add a custom glyph rect for Unicode black square (U+25A0) and color it */

/* In ImGui < 1.90, font->ConfigData is not set until calling Build() */
const float glyphSize = ImGui::GetIO().Fonts->ConfigData[0].SizePixels;
const int rectIndex = ImGui::GetIO().Fonts->AddCustomRectFontGlyph(font, L'\u25A0',
glyphSize*0.5f, glyphSize*0.5f, glyphSize, {glyphSize*0.25f, glyphSize*0.25f});
ImFontAtlasCustomRect* rect = ImGui::GetIO().Fonts->GetCustomRectByIndex(rectIndex);
/* Build the atlas manually so we get the offsets to draw to. drawText()
already checked that Build() is correctly called by relayout(). */
ImGui::GetIO().Fonts->Build();

unsigned char* pixelData;
int width;
int height;
ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&pixelData, &width, &height);
Color4ub* pixels = reinterpret_cast<Color4ub*>(pixelData);
for(UnsignedShort y = rect->Y; y < rect->Y + rect->Height; ++y)
for(UnsignedShort x = rect->X; x < rect->X + rect->Width; ++x)
pixels[(y*width) + x] = Color4ub{128, 255, 128, 128};

/* Tell the backend that the font atlas contains colored glyphs so it
always creates an RGBA texture */
ImGui::GetIO().Fonts->TexPixelsUseColors = true;

c.relayout({200, 200}, {70, 70}, _framebuffer.viewport().size());

MAGNUM_VERIFY_NO_GL_ERROR();

/* ImGui doesn't draw anything the first frame */
c.newFrame();
c.drawFrame();

MAGNUM_VERIFY_NO_GL_ERROR();

Utility::System::sleep(1);

c.newFrame();

/* Last drawlist that gets rendered, covers the entire display */
ImDrawList* drawList = ImGui::GetForegroundDrawList();
const ImVec2& size = ImGui::GetIO().DisplaySize;

drawList->AddRectFilled({size.x*0.1f, size.y*0.2f}, {size.x*0.9f, size.y*0.8f},
IM_COL32(255, 128, 128, 255));
/* White, gets multiplied with the glyph color */
drawList->AddText(nullptr, font->FontSize*font->Scale,
{size.x*0.0f, size.y*0.0f}, IM_COL32(255, 255, 255, 255), u8"A\u25A0B");
drawList->AddText(nullptr, font->FontSize*font->Scale,
{size.x*0.0f, size.y*0.5f}, IM_COL32(255, 255, 255, 255), u8"\u25A0\u25A0");

c.drawFrame();

MAGNUM_VERIFY_NO_GL_ERROR();

/* Catch also ABI and interface mismatch errors */
if(!(_manager.load("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.load("PngImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / PngImporter plugin can't be loaded.");

CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Path::join(IMGUIINTEGRATION_TEST_DIR, "ContextTestFiles/draw-text-color-font.png"),
(DebugTools::CompareImageToFile{_manager, 3.0f, 0.5f}));
}

void ContextGLTest::drawScissor() {
Context c{{200, 200}, {70, 70}, _framebuffer.viewport().size()};

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Magnum/ImGuiIntegration/Widgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ inline ImTextureID textureId(GL::Texture2D& texture) {
#if IMGUI_VERSION_NUM >= 19131
return texture.id();
#else
return reinterpret_cast<ImTextureID>(texture.id());
return reinterpret_cast<ImTextureID>(std::uintptr_t(texture.id()));
#endif
}

Expand Down