Implemented Font GLyph Atlas overflow
This commit is contained in:
parent
20c40d87e4
commit
64cd4598f2
10 changed files with 221 additions and 84 deletions
3
.clangd
3
.clangd
|
|
@ -1,3 +1,2 @@
|
|||
CompileFlags:
|
||||
CompilationDatabase: bin
|
||||
Add: ["--target=x86_64-w64-mingw32"]
|
||||
CompilationDatabase: bin
|
||||
|
|
@ -5,7 +5,7 @@ $accentColor = Color(rgba(255, 0, 255, 255))
|
|||
const $color2 = Color(#0000FFFF)
|
||||
% -------------------
|
||||
|
||||
window.backgroundColor = Color(rgba(120, 120, 120, 255))
|
||||
window.backgroundColor = Color(rgba(0, 0, 0, 255))
|
||||
|
||||
$accentColor = Color(rgb(255, 0, 0))
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2057
|
||||
2058
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace ogfx
|
|||
{
|
||||
if (m_window != nullptr) return;
|
||||
m_window = &window;
|
||||
m_renderer.init(window);
|
||||
// m_renderer.init(window); //TODO: Fix
|
||||
}
|
||||
|
||||
void GraphicsWindowOutputHandler::setMonospaceFont(const String& filePath)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ namespace ogfx
|
|||
m_window = &window;
|
||||
if (m_initialized) return set_error_state(tErrors::NoError);
|
||||
init_arrays();
|
||||
m_initialized = true;
|
||||
loadDefaultFont(20);
|
||||
m_initialized = false;
|
||||
m_fontGlyphAtlas.init(*this);
|
||||
m_initialized = true;
|
||||
return set_error_state(tErrors::NoError);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace ogfx
|
|||
BasicRenderer2D& m_parent;
|
||||
};
|
||||
public:
|
||||
BasicRenderer2D(void) = default;
|
||||
inline BasicRenderer2D(void) { }
|
||||
~BasicRenderer2D(void);
|
||||
int32_t init(WindowCore& window);
|
||||
SDL_Renderer* getSDLRenderer(void) const;
|
||||
|
|
@ -106,7 +106,7 @@ namespace ogfx
|
|||
void outlinedCircle(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1);
|
||||
void outlinedEllipse(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1);
|
||||
|
||||
private:
|
||||
public:
|
||||
void init_arrays(void);
|
||||
void generate_half_circle(const ostd::Vec2& center, const ostd::Vec2& dir, float radius, int segments, const ostd::Color& color);
|
||||
void generate_quarter_circle(const ostd::Vec2& center, float radius, float thickness, float startAngle, const ostd::Color& color, int segments);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@
|
|||
|
||||
#include "FontGlyphAtlas.hpp"
|
||||
#include "BasicRenderer.hpp"
|
||||
|
||||
#define DEBUG(n) std::cout << (int)n << "\n";
|
||||
#include <SDL3/SDL_pixels.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <SDL3/SDL_surface.h>
|
||||
|
||||
namespace ogfx
|
||||
{
|
||||
|
|
@ -38,20 +39,20 @@ namespace ogfx
|
|||
|
||||
bool FontGlyphAtlas::rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize)
|
||||
{
|
||||
DEBUG(1);
|
||||
std::cout << (uint64_t)font << "\n";
|
||||
if (!font || m_currentAtlasCount <= 0)
|
||||
return false;
|
||||
|
||||
DEBUG(2);
|
||||
// 1. Extract codepoint from the ostd::String
|
||||
auto cps = glyphStr.getUTF8Codepoints();
|
||||
if (cps.size() != 1)
|
||||
return false; // must be exactly one character
|
||||
|
||||
DEBUG(3);
|
||||
uint32_t codepoint = cps[0];
|
||||
|
||||
GlyphKey key { codepoint, uint64_t(font), fontSize };
|
||||
if (auto it = m_uvs.find(key) != m_uvs.end())
|
||||
return true;
|
||||
|
||||
// 2. Convert the ostd::String to UTF-8 for SDL_ttf
|
||||
std::string utf8 = glyphStr.cpp_str();
|
||||
|
||||
|
|
@ -61,13 +62,19 @@ namespace ogfx
|
|||
if (!surf)
|
||||
return false;
|
||||
|
||||
DEBUG(4);
|
||||
// SDL_Surface* converted = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
|
||||
// SDL_DestroySurface(surf);
|
||||
// surf = converted;
|
||||
|
||||
int gw = surf->w;
|
||||
int gh = surf->h;
|
||||
|
||||
// 4. Get current atlas
|
||||
SDL_Texture* atlas = m_atlases[m_currentAtlasCount - 1];
|
||||
|
||||
// SDL_Log("atlas format = %s", SDL_GetPixelFormatName(atlas->format));
|
||||
// SDL_Log("surf format = %s", SDL_GetPixelFormatName(surf->format));
|
||||
|
||||
// 5. Shelf packing: move to next row if needed
|
||||
if (m_penX + gw > int(AtlasTextureDimension))
|
||||
{
|
||||
|
|
@ -90,10 +97,47 @@ namespace ogfx
|
|||
m_rowHeight = 0;
|
||||
}
|
||||
|
||||
DEBUG(5);
|
||||
uint8_t* pixels = (uint8_t*)surf->pixels;
|
||||
int pitch = surf->pitch;
|
||||
|
||||
// for (int y = 0; y < surf->h; y++)
|
||||
// {
|
||||
// uint32_t* row = (uint32_t*)(pixels + y * pitch);
|
||||
|
||||
// for (int x = 0; x < surf->w; x++)
|
||||
// {
|
||||
// uint32_t px = row[x];
|
||||
|
||||
// uint8_t a = (px >> 24) & 0xFF; // ABGR → A is highest byte
|
||||
// uint8_t r = (px >> 16) & 0xFF;
|
||||
// uint8_t g = (px >> 8) & 0xFF;
|
||||
// uint8_t b = (px) & 0xFF;
|
||||
|
||||
// std::cout << (int)r << "," << (int)g << "," << (int)b << "," << (int)a << " ";
|
||||
|
||||
// row[x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 7. Upload glyph bitmap into atlas
|
||||
SDL_Rect dstRect { m_penX, m_penY, gw, gh };
|
||||
SDL_UpdateTexture(atlas, &dstRect, surf->pixels, surf->pitch);
|
||||
|
||||
void* lockedPixels = nullptr;
|
||||
int lockedPitch = 0;
|
||||
if (SDL_LockTexture(atlas, &dstRect, &lockedPixels, &lockedPitch))
|
||||
{
|
||||
uint8_t* src = (uint8_t*)surf->pixels;
|
||||
uint8_t* dst = (uint8_t*)lockedPixels;
|
||||
for (int y = 0; y < gh; y++)
|
||||
{
|
||||
memcpy(dst, src, gw * 4); // 4 bytes per pixel (ARGB8888)
|
||||
src += surf->pitch;
|
||||
dst += lockedPitch;
|
||||
}
|
||||
SDL_UnlockTexture(atlas);
|
||||
}
|
||||
|
||||
// SDL_UpdateTexture(atlas, &dstRect, surf->pixels, surf->pitch);
|
||||
SDL_DestroySurface(surf);
|
||||
|
||||
// 8. Compute UVs
|
||||
|
|
@ -108,9 +152,9 @@ namespace ogfx
|
|||
uv.uvs[1] = { u1, v0 };
|
||||
uv.uvs[2] = { u1, v1 };
|
||||
uv.uvs[3] = { u0, v1 };
|
||||
uv.size = { static_cast<float>(gw), static_cast<float>(gh) };
|
||||
|
||||
// 9. Store in hashmap
|
||||
GlyphKey key { codepoint, uint64_t(font), fontSize };
|
||||
m_uvs[key] = uv;
|
||||
|
||||
// 10. Advance shelf packing cursor
|
||||
|
|
@ -133,19 +177,14 @@ namespace ogfx
|
|||
return false;
|
||||
|
||||
// 2. Create the texture
|
||||
SDL_Texture* tex = SDL_CreateTexture(
|
||||
sdlRenderer,
|
||||
SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
AtlasTextureDimension,
|
||||
AtlasTextureDimension
|
||||
);
|
||||
SDL_Texture* tex = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, AtlasTextureDimension, AtlasTextureDimension);
|
||||
|
||||
if (!tex)
|
||||
return false;
|
||||
|
||||
// 3. Set blend mode (important for alpha glyphs)
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_LINEAR);
|
||||
|
||||
// 4. Clear the texture to transparent
|
||||
{
|
||||
|
|
@ -153,7 +192,7 @@ namespace ogfx
|
|||
int pitch = 0;
|
||||
if (SDL_LockTexture(tex, nullptr, &pixels, &pitch) == 0)
|
||||
{
|
||||
memset(pixels, 0, pitch * AtlasTextureDimension);
|
||||
memset(pixels, 0x00, pitch * AtlasTextureDimension);
|
||||
SDL_UnlockTexture(tex);
|
||||
}
|
||||
}
|
||||
|
|
@ -170,55 +209,40 @@ namespace ogfx
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FontGlyphAtlas::save_atlas_png(int atlasIndex, const char* filename)
|
||||
void FontGlyphAtlas::save_atlas_to_png(SDL_Renderer* renderer, SDL_Texture* atlas, const char* filename)
|
||||
{
|
||||
if (atlasIndex < 0 || atlasIndex >= m_currentAtlasCount)
|
||||
return false;
|
||||
// 1. Get texture dimensions and format
|
||||
float w, h;
|
||||
SDL_GetTextureSize(atlas, &w, &h);
|
||||
int iw = (int)w, ih = (int)h;
|
||||
|
||||
SDL_Texture* tex = m_atlases[atlasIndex];
|
||||
if (!tex)
|
||||
return false;
|
||||
// 2. Create a target surface
|
||||
SDL_Surface* surf = SDL_CreateSurface(iw, ih, SDL_PIXELFORMAT_ARGB8888);
|
||||
if (!surf) return;
|
||||
|
||||
// --- 1. Query texture properties (SDL3 way) ---
|
||||
SDL_PropertiesID props = SDL_GetTextureProperties(tex);
|
||||
if (!props)
|
||||
return false;
|
||||
// 3. Read pixels back from the texture
|
||||
SDL_Rect rect { 0, 0, iw, ih };
|
||||
|
||||
int width = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_WIDTH_NUMBER, 0);
|
||||
int height = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0);
|
||||
Uint32 format = (Uint32)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, SDL_PIXELFORMAT_RGBA32);
|
||||
// For STREAMING textures, lock/read directly:
|
||||
void* pixels = nullptr;
|
||||
int pitch = 0;
|
||||
if (SDL_LockTexture(atlas, nullptr, &pixels, &pitch))
|
||||
{
|
||||
uint8_t* src = (uint8_t*)pixels;
|
||||
uint8_t* dst = (uint8_t*)surf->pixels;
|
||||
for (int y = 0; y < ih; y++)
|
||||
{
|
||||
memcpy(dst, src, iw * 4);
|
||||
src += pitch;
|
||||
dst += surf->pitch;
|
||||
}
|
||||
SDL_UnlockTexture(atlas);
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return false;
|
||||
// 4. Save
|
||||
IMG_SavePNG(surf, filename); // needs SDL3_image
|
||||
// or: SDL_SaveBMP(surf, "atlas_debug.bmp");
|
||||
|
||||
// --- 2. Create a surface to receive the pixels ---
|
||||
SDL_Surface* surf = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32);
|
||||
if (!surf)
|
||||
return false;
|
||||
|
||||
// --- 3. Lock texture and copy pixels ---
|
||||
void* pixels = nullptr;
|
||||
int pitch = 0;
|
||||
|
||||
if (!SDL_LockTexture(tex, nullptr, &pixels, &pitch))
|
||||
{
|
||||
SDL_DestroySurface(surf);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy row-by-row (pitch may differ)
|
||||
uint8_t* dst = static_cast<uint8_t*>(surf->pixels);
|
||||
uint8_t* src = static_cast<uint8_t*>(pixels);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy(dst + y * surf->pitch, src + y * pitch, width * 4);
|
||||
|
||||
SDL_UnlockTexture(tex);
|
||||
|
||||
// --- 4. Save PNG ---
|
||||
bool ok = (IMG_SavePNG(surf, filename) == 0);
|
||||
|
||||
SDL_DestroySurface(surf);
|
||||
return ok;
|
||||
SDL_DestroySurface(surf);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ namespace ogfx
|
|||
{
|
||||
ostd::Vec2 uvs[4];
|
||||
SDL_Texture* atlas { nullptr };
|
||||
ostd::Vec2 size;
|
||||
};
|
||||
struct GlyphKey
|
||||
{
|
||||
|
|
@ -49,30 +50,29 @@ namespace ogfx
|
|||
};
|
||||
struct GlyphKeyHasher
|
||||
{
|
||||
inline size_t operator()(const GlyphKey& k) const noexcept
|
||||
size_t operator()(const GlyphKey& k) const noexcept
|
||||
{
|
||||
uint64_t v = 0;
|
||||
v |= uint64_t(k.codepoint);
|
||||
v |= uint64_t(k.fontID) << 32;
|
||||
v |= uint64_t(k.pixelSize) << 48;
|
||||
return std::hash<uint64_t>()(v);
|
||||
}
|
||||
size_t h = std::hash<uint32_t>()(k.codepoint);
|
||||
h ^= std::hash<uint64_t>()(k.fontID) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
h ^= std::hash<uint32_t>()(k.pixelSize) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
public:
|
||||
inline FontGlyphAtlas(void) { }
|
||||
inline FontGlyphAtlas(BasicRenderer2D& renderer) { init(renderer); }
|
||||
FontGlyphAtlas init(BasicRenderer2D& renderer);
|
||||
bool save_atlas_png(int atlasIndex, const char* filename);
|
||||
|
||||
public:
|
||||
bool rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize);
|
||||
bool create_new_atlas(void);
|
||||
void save_atlas_to_png(SDL_Renderer* renderer, SDL_Texture* atlas, const char* filename);
|
||||
|
||||
public:
|
||||
inline static constexpr uint32_t AtlasTextureDimension { 8192 };
|
||||
inline static constexpr uint32_t MaxAtlasCount { 16 };
|
||||
|
||||
private:
|
||||
public:
|
||||
std::unordered_map<GlyphKey, GlyphUV, GlyphKeyHasher> m_uvs;
|
||||
SDL_Texture* m_atlases[FontGlyphAtlas::MaxAtlasCount];
|
||||
int32_t m_currentAtlasCount { 0 };
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@
|
|||
#endif
|
||||
|
||||
#ifndef OX_DEBUG_BUILD
|
||||
#define _DEBUG(n)
|
||||
#ifndef OX_RELEASE_BUILD
|
||||
#define OX_RELEASE_BUILD
|
||||
#endif
|
||||
#else
|
||||
#define _DEBUG(n) std::cout << (int)n << "\n";
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,11 +19,51 @@
|
|||
*/
|
||||
|
||||
#include "gui/Events.hpp"
|
||||
#include <SDL3/SDL_rect.h>
|
||||
#include <ogfx/utils/Keycodes.hpp>
|
||||
#include <ogfx/ogfx.hpp>
|
||||
|
||||
ostd::ConsoleOutputHandler out;
|
||||
|
||||
void drawTexturedQuad(SDL_Renderer* renderer,
|
||||
SDL_Texture* tex,
|
||||
const ostd::Rectangle& rect,
|
||||
const ostd::Vec2 uvs[4])
|
||||
{
|
||||
SDL_Vertex verts[4];
|
||||
|
||||
// Expand rectangle into quad corners
|
||||
const float x = rect.x;
|
||||
const float y = rect.y;
|
||||
const float w = rect.w;
|
||||
const float h = rect.h;
|
||||
|
||||
const ostd::Vec2 quad[4] = {
|
||||
{ x, y },
|
||||
{ x + w, y },
|
||||
{ x + w, y + h },
|
||||
{ x, y + h }
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
verts[i].position.x = quad[i].x;
|
||||
verts[i].position.y = quad[i].y;
|
||||
|
||||
verts[i].tex_coord.x = uvs[i].x;
|
||||
verts[i].tex_coord.y = uvs[i].y;
|
||||
|
||||
auto crim = ostd::Colors::Crimson.getNormalizedColor();
|
||||
verts[i].color = SDL_FColor{crim.r, crim.g, crim.b, crim.a};
|
||||
}
|
||||
|
||||
// Two triangles: (0,1,2) and (2,3,0)
|
||||
const int indices[6] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
SDL_RenderGeometry(renderer, tex, verts, 4, indices, 6);
|
||||
}
|
||||
|
||||
|
||||
class Window : public ogfx::gui::Window
|
||||
{
|
||||
public:
|
||||
|
|
@ -65,14 +105,14 @@ class Window : public ogfx::gui::Window
|
|||
m_label1.setMouseScrolledCallback([&](const ogfx::gui::Event& event) -> void {
|
||||
std::cout << "SCROLL!\n";
|
||||
});
|
||||
addWidget(m_label1);
|
||||
// addWidget(m_label1);
|
||||
m_panel2.addChild(m_panel1);
|
||||
addWidget(m_panel2);
|
||||
// addWidget(m_panel2);
|
||||
|
||||
|
||||
m_check1.setPosition(30, 30);
|
||||
m_check1.setText("Check this out!");
|
||||
addWidget(m_check1);
|
||||
// addWidget(m_check1);
|
||||
|
||||
m_label2.setPosition(0, 0);
|
||||
m_label2.setText("Ciccia Bella!");
|
||||
|
|
@ -117,10 +157,55 @@ class Window : public ogfx::gui::Window
|
|||
|
||||
setTheme(m_theme);
|
||||
|
||||
m_gfx.getFontGlyphAtlas().rasterize_glyph("A", m_gfx.getSDLFont(), 30);
|
||||
m_gfx.getFontGlyphAtlas().save_atlas_png(0, "./atlas.png");
|
||||
int32_t count = 0;
|
||||
for (char c = 'A'; c <= 'Z'; c++)
|
||||
{
|
||||
for (int32_t i = 0; i < 200; i++)
|
||||
{
|
||||
count++;
|
||||
uint32_t _fs = ostd::Random::getui32(8, 200);
|
||||
m_gfx.setFontSize(_fs);
|
||||
m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar(c), m_gfx.getSDLFont(), _fs);
|
||||
}
|
||||
}
|
||||
for (char c = '0'; c <= '9'; c++)
|
||||
{
|
||||
for (int32_t i = 0; i < 200; i++)
|
||||
{
|
||||
count++;
|
||||
uint32_t _fs = ostd::Random::getui32(8, 200);
|
||||
m_gfx.setFontSize(_fs);
|
||||
m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar(c), m_gfx.getSDLFont(), _fs);
|
||||
}
|
||||
}
|
||||
for (char c = 'a'; c <= 'z'; c++)
|
||||
{
|
||||
count++;
|
||||
for (int32_t i = 0; i < 200; i++)
|
||||
{
|
||||
uint32_t _fs = ostd::Random::getui32(8, 200);
|
||||
m_gfx.setFontSize(_fs);
|
||||
m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar(c), m_gfx.getSDLFont(), _fs);
|
||||
}
|
||||
}
|
||||
for (char c = '0'; c <= '9'; c++)
|
||||
{
|
||||
for (int32_t i = 0; i < 200; i++)
|
||||
{
|
||||
count++;
|
||||
uint32_t _fs = ostd::Random::getui32(8, 200);
|
||||
m_gfx.setFontSize(_fs);
|
||||
m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar(c), m_gfx.getSDLFont(), _fs);
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << m_theme.get<ostd::String>("panel.titlebarType", "", {}, {}) << " \n";
|
||||
for (int32_t i = 0; i < m_gfx.getFontGlyphAtlas().m_currentAtlasCount; i++)
|
||||
{
|
||||
ostd::String file("./atlas");
|
||||
file.add(i).add(".png");
|
||||
m_gfx.getFontGlyphAtlas().save_atlas_to_png(m_gfx.getSDLRenderer(), m_gfx.getFontGlyphAtlas().m_atlases[i], file);
|
||||
}
|
||||
std::cout << "Atlas cound: " << (int)count << "\n";
|
||||
}
|
||||
|
||||
inline void onSignal(ostd::Signal& signal) override
|
||||
|
|
@ -145,6 +230,29 @@ class Window : public ogfx::gui::Window
|
|||
// gfx.drawRect({ 50, 50, 200, 120 }, ostd::Colors::Aquamarine, 1);
|
||||
|
||||
// std::cout << (int)(gfx.getDrawCallCount() + 1) << "\n";
|
||||
|
||||
gfx.endFrame();
|
||||
|
||||
auto l_renderGlyph = [&](char c, float x, float y, float scale = 1) -> ostd::Vec2 {
|
||||
auto& tex = m_gfx.getFontGlyphAtlas().m_atlases[0];
|
||||
auto glyph = m_gfx.getFontGlyphAtlas().m_uvs[ { static_cast<uint32_t>(c), (uint64_t)gfx.getSDLFont(), fs }];
|
||||
drawTexturedQuad(getSDLRenderer(), tex, { x, y, glyph.size.x * scale, glyph.size.y * scale }, glyph.uvs);
|
||||
return glyph.size;
|
||||
};
|
||||
auto l_renderString = [&](const ostd::String& str, const ostd::Vec2& pos, float scale = 1.0f) -> void {
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
for (auto& c : str)
|
||||
{
|
||||
auto size = l_renderGlyph(c, x, y, scale);
|
||||
x += (size.x * scale);
|
||||
}
|
||||
};
|
||||
|
||||
l_renderString("Hello", { 10, 140 });
|
||||
|
||||
gfx.renderText("Hello", 10, 100, ostd::Colors::Crimson, 30);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -156,6 +264,7 @@ class Window : public ogfx::gui::Window
|
|||
ogfx::gui::widgets::CheckBox m_check1 { *this };
|
||||
ostd::Stylesheet m_theme;
|
||||
ostd::Vec2 pos { 0, 0 };
|
||||
uint32_t fs = 30;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
|
|
|||
Loading…
Reference in a new issue