diff --git a/extra/FreeSans.ttf b/extra/FreeSans.ttf new file mode 100644 index 0000000..6de62eb Binary files /dev/null and b/extra/FreeSans.ttf differ diff --git a/extra/testTheme.txt b/extra/testTheme.txt index 28d7ae9..57dc24b 100644 --- a/extra/testTheme.txt +++ b/extra/testTheme.txt @@ -5,7 +5,7 @@ $accentColor = Color(rgba(255, 0, 255, 255)) const $color2 = Color(#0000FFFF) % ------------------- -window.backgroundColor = Color(rgba(0, 0, 0, 255)) +window.backgroundColor = Color(rgba(160, 160, 160, 255)) $accentColor = Color(rgb(255, 0, 0)) @@ -13,7 +13,7 @@ $accentColor = Color(rgb(255, 0, 0)) textColor = $accentColor backgroundColor = Color(#000000FF) borderColor = $accentColor - fontSize = 50 + fontSize = 20 borderRadius = 0 borderWidth = 2 showBackground = true @@ -65,7 +65,7 @@ $accentColor = Color(rgb(255, 0, 0)) (@testLabel label) { textColor = $color2 backgroundColor = Color(#000000FF) - fontSize = 20 + fontSize = 50 } (@testLabel2 label) { @@ -108,7 +108,6 @@ $accentColor = Color(rgb(255, 0, 0)) (@label3 label) { textColor = $color2 backgroundColor = Color(#000000FF) - fontSize = 20 borderColor = Color(#FF0000FF) borderWidth = 4 borderRadius = 15 diff --git a/other/TODO.txt b/other/TODO.txt index 91db5f1..4fbae84 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -14,6 +14,11 @@ Add theme caching Implement callback for state changes in checkbox FIX: WindowOutputHandler text rendering find a way to add fixed update to gui::Window +Implement global scale (probably query desktop scale and adjust accordingly) +Fix: Text rendering bug in labels +Implement show/hide functionality for widgets +Implement wrappers in Renderer2D to take a center and size for rects + Implement following widgets: diff --git a/other/build.nr b/other/build.nr index 1113ea2..8c6e25c 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -2060 +2061 diff --git a/src/ogfx/gui/widgets/CheckBox.cpp b/src/ogfx/gui/widgets/CheckBox.cpp index d5aedfa..5c52c3b 100644 --- a/src/ogfx/gui/widgets/CheckBox.cpp +++ b/src/ogfx/gui/widgets/CheckBox.cpp @@ -73,6 +73,8 @@ namespace ogfx void CheckBox::onMouseReleased(const Event& event) { + if (!isMouseInside()) + return; m_checked = !m_checked; setThemeQualifier("active", m_checked); } diff --git a/src/ogfx/render/BasicRenderer.cpp b/src/ogfx/render/BasicRenderer.cpp index c6925ec..7ab85f3 100644 --- a/src/ogfx/render/BasicRenderer.cpp +++ b/src/ogfx/render/BasicRenderer.cpp @@ -1,21 +1,21 @@ /* - OmniaFramework - A collection of useful functionality - Copyright (C) 2025 OmniaX-Dev + OmniaFramework - A collection of useful functionality + Copyright (C) 2025 OmniaX-Dev - This file is part of OmniaFramework. + This file is part of OmniaFramework. - OmniaFramework is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + OmniaFramework is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - OmniaFramework is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + OmniaFramework is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with OmniaFramework. If not, see . + You should have received a copy of the GNU General Public License + along with OmniaFramework. If not, see . */ #include "BasicRenderer.hpp" @@ -77,7 +77,7 @@ namespace ogfx void BasicRenderer2D::flushBatch(void) { if (!m_initialized || m_vertexCount == 0) - return; + return; SDL_RenderGeometry(m_window->getSDLRenderer(), m_texture, m_vertices.data(), m_vertexCount, m_indices.data(), m_indexCount); m_drawCallCount++; m_texture = nullptr; @@ -95,22 +95,22 @@ namespace ogfx { if (!m_initialized) return; - ostd::Rectangle finalRect = rect; + ostd::Rectangle finalRect = rect; - if (additive && !m_clipStack.empty()) - finalRect = m_clipStack.back().getIntersection(rect, false); + if (additive && !m_clipStack.empty()) + finalRect = m_clipStack.back().getIntersection(rect, false); if (!m_clipStack.empty() && m_clipStack.back() == finalRect) return; flushBatch(); - m_clipStack.push_back(finalRect); + m_clipStack.push_back(finalRect); - SDL_Rect r; - r.x = (int)std::round(finalRect.x); - r.y = (int)std::round(finalRect.y); - r.w = (int)std::round(finalRect.w); - r.h = (int)std::round(finalRect.h); + SDL_Rect r; + r.x = (int)std::round(finalRect.x); + r.y = (int)std::round(finalRect.y); + r.w = (int)std::round(finalRect.w); + r.h = (int)std::round(finalRect.h); SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r); } @@ -118,26 +118,26 @@ namespace ogfx void BasicRenderer2D::popClippingRect(void) { if (!m_initialized) return; - if (m_clipStack.empty()) return; + if (m_clipStack.empty()) return; - m_clipStack.pop_back(); + m_clipStack.pop_back(); flushBatch(); - if (m_clipStack.empty()) - { - SDL_SetRenderClipRect(m_window->getSDLRenderer(), nullptr); - return; - } + if (m_clipStack.empty()) + { + SDL_SetRenderClipRect(m_window->getSDLRenderer(), nullptr); + return; + } - const auto& rect = m_clipStack.back(); + const auto& rect = m_clipStack.back(); - SDL_Rect r; - r.x = (int)std::round(rect.x); - r.y = (int)std::round(rect.y); - r.w = (int)std::round(rect.w); - r.h = (int)std::round(rect.h); + SDL_Rect r; + r.x = (int)std::round(rect.x); + r.y = (int)std::round(rect.y); + r.w = (int)std::round(rect.w); + r.h = (int)std::round(rect.h); - SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r); + SDL_SetRenderClipRect(m_window->getSDLRenderer(), &r); } int32_t BasicRenderer2D::loadDefaultFont(int32_t fontSize) @@ -211,44 +211,30 @@ namespace ogfx return set_error_state(tErrors::NoError); } - ostd::IPoint BasicRenderer2D::getStringDimensions(const ostd::String& message, int32_t fontSize) + ostd::Vec2 BasicRenderer2D::getStringDimensions(const ostd::String& message, int32_t fontSize, TTF_Font* font) { - if (!m_initialized) - { - set_error_state(tErrors::Uninitialized); - return { 0, 0 }; - } - if (!m_fontOpen) - { - set_error_state(tErrors::NoFont); - return { 0, 0 }; - } - if (m_font == nullptr) - { - set_error_state(tErrors::NullFont); - return { 0, 0 }; - } - setFontSize(fontSize); - SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), message.len(), { 0, 0, 0, 255 }); - if (surf == nullptr) - { - set_error_state(tErrors::TTFRenderTextBlendedFail); - print_ttf_error("TTF_RenderText_Blended"); - return { 0, 0 }; - } - SDL_Texture* texture = SDL_CreateTextureFromSurface(m_window->getSDLRenderer(), surf); - if (texture == nullptr) - { - set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); - print_ttf_error("SDL_CreateTextureFromSurface"); - return { 0, 0 }; - } - SDL_DestroySurface(surf); + if (!isValid()) return { 0, 0 }; + if (fontSize <= 0) fontSize = m_fontSize; + if (!font) font = m_font; + int32_t oldFontSize = getFontSize(); + setFontSize(fontSize); - float w = 0, h = 0; - SDL_GetTextureSize(texture, &w, &h); - SDL_DestroyTexture(texture); - return { static_cast(w), static_cast(h) }; + auto glyphs = m_fontGlyphAtlas.processString(message, font, fontSize); + if (glyphs.empty()) return { 0, 0 }; + + float totalWidth = 0; + for (size_t i = 0; i < glyphs.size(); i++) + { + totalWidth += glyphs[i]->advance; + if (i > 0) + { + int kern = 0; + TTF_GetGlyphKerning(font, glyphs[i - 1]->codepoint, glyphs[i]->codepoint, &kern); + totalWidth += kern; + } + } + setFontSize(oldFontSize); + return { totalWidth, glyphs[0]->size.y }; } // ===================================================== UTILS ===================================================== @@ -257,83 +243,122 @@ namespace ogfx // ===================================================== SPECIALIZED ===================================================== - void BasicRenderer2D::drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Vec2& size, const ostd::Rectangle& srcRect) - { - if (!m_initialized || !image.isLoaded()) - return; - - SDL_Texture* tex = image.getSDLTexture(); - if (!tex) - return; - - ostd::Vec2 texSize = image.getSize(); - - // 1. Resolve source rectangle - float sx, sy, sw, sh; - if (srcRect.w == 0 && srcRect.h == 0) - { - // Use entire texture - sx = 0.0f; - sy = 0.0f; - sw = texSize.x; - sh = texSize.y; - } - else - { - sx = srcRect.x; - sy = srcRect.y; - sw = srcRect.w; - sh = srcRect.h; - } - - // 2. Resolve destination rectangle - float dx = position.x; - float dy = position.y; - float dw = (size.x == 0) ? sw : size.x; - float dh = (size.y == 0) ? sh : size.y; - float x1 = dx; - float y1 = dy; - float x2 = dx + dw; - float y2 = dy + dh; - - // 3. Build quad vertices - ostd::Vec2 verts[4] = { - { x1, y1 }, - { x2, y1 }, - { x2, y2 }, - { x1, y2 } - }; - - // 4. Build UVs (normalized) - ostd::Vec2 uvs[4] = { - { sx / texSize.x, sy / texSize.y }, - { (sx + sw) / texSize.x, sy / texSize.y }, - { (sx + sw) / texSize.x, (sy + sh) / texSize.y }, - { sx / texSize.x, (sy + sh) / texSize.y } - }; - - // 5. Push quad - uint32_t inds[6] = QUAD_INDICES_ARR; - push_polygon(verts, uvs, 4, inds, 6, ostd::Colors::White, tex); - } - - void BasicRenderer2D::drawAnimation(const Animation& anim, const ostd::Vec2& position, const ostd::Vec2& size) - { - if (!m_initialized) return; - if (!anim.hasImage()) return; - const Image& img = anim.getSpriteSheet(); - if (!img.isLoaded() || !img.isValid()) return; - drawImage(img, position, size, anim.getFrameRect()); - } - - void BasicRenderer2D::drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize) + void BasicRenderer2D::drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Vec2& size, const ostd::Rectangle& srcRect) { - if (!m_initialized) return; + if (!m_initialized || !image.isLoaded()) + return; + + SDL_Texture* tex = image.getSDLTexture(); + if (!tex) + return; + + ostd::Vec2 texSize = image.getSize(); + + // 1. Resolve source rectangle + float sx, sy, sw, sh; + if (srcRect.w == 0 && srcRect.h == 0) + { + // Use entire texture + sx = 0.0f; + sy = 0.0f; + sw = texSize.x; + sh = texSize.y; + } + else + { + sx = srcRect.x; + sy = srcRect.y; + sw = srcRect.w; + sh = srcRect.h; + } + + // 2. Resolve destination rectangle + float dx = position.x; + float dy = position.y; + float dw = (size.x == 0) ? sw : size.x; + float dh = (size.y == 0) ? sh : size.y; + float x1 = dx; + float y1 = dy; + float x2 = dx + dw; + float y2 = dy + dh; + + // 3. Build quad vertices + ostd::Vec2 verts[4] = { + { x1, y1 }, + { x2, y1 }, + { x2, y2 }, + { x1, y2 } + }; + + // 4. Build UVs (normalized) + ostd::Vec2 uvs[4] = { + { sx / texSize.x, sy / texSize.y }, + { (sx + sw) / texSize.x, sy / texSize.y }, + { (sx + sw) / texSize.x, (sy + sh) / texSize.y }, + { sx / texSize.x, (sy + sh) / texSize.y } + }; + + // 5. Push quad + uint32_t inds[6] = QUAD_INDICES_ARR; + push_polygon(verts, uvs, 4, inds, 6, ostd::Colors::White, tex); } - void BasicRenderer2D::drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize) + void BasicRenderer2D::drawAnimation(const Animation& anim, const ostd::Vec2& position, const ostd::Vec2& size) { if (!m_initialized) return; + if (!anim.hasImage()) return; + const Image& img = anim.getSpriteSheet(); + if (!img.isLoaded() || !img.isValid()) return; + drawImage(img, position, size, anim.getFrameRect()); + } + + void BasicRenderer2D::drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize, float scale) + { + if (!isValid()) return; + if (fontSize <= 0) + fontSize = m_fontSize; + setFontSize(fontSize); + + auto glyphs = m_fontGlyphAtlas.processString(str, m_font, fontSize); + if (glyphs.empty()) return; + + float x = position.x; + float y = position.y; + + + for (size_t i = 0; i < glyphs.size(); i++) + { + auto& g = glyphs[i]; + + if (i > 0) + { + int kern = 0; + TTF_GetGlyphKerning(m_font, glyphs[i - 1]->codepoint, g->codepoint, &kern); + x += (kern * scale); + } + + ostd::Vec2 verts[4] = { + { x, y }, + { x + g->size.x * scale, y }, + { x + g->size.x * scale, y + g->size.y * scale }, + { x, y + g->size.y * scale } + }; + uint32_t inds[6] = QUAD_INDICES_ARR; + push_polygon(verts, g->uvs, 4, inds, 6, color, g->atlas); + + x += (g->advance * scale); + } + } + + void BasicRenderer2D::drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize, float scale) + { + auto dims = getStringDimensions(str, fontSize); + drawString(str, { center.x - (dims.x * scale) * 0.5f, center.y - (dims.y * scale) * 0.5f }, color, fontSize, scale); + } + + void BasicRenderer2D::drawCenteredString(const ostd::String& str, const ostd::Rectangle& bounds, const ostd::Color& color, int32_t fontSize, float scale) + { + drawCenteredString(str, ostd::Vec2 { bounds.x + bounds.w * 0.5f, bounds.y + bounds.h * 0.5f }, color, fontSize, scale); } // ===================================================== SPECIALIZED ===================================================== @@ -357,7 +382,7 @@ namespace ogfx Vec2 off = perp * half; std::array verts = {{ - p1 - off, + p1 - off, p1 + off, p2 + off, p2 - off @@ -366,248 +391,248 @@ namespace ogfx push_polygon(verts.data(), nullptr, 4, inds.data(), 6, color, nullptr); - if (!rounded || thickness < 4) - return; + if (!rounded || thickness < 4) + return; int segments = std::max(6, int(thickness * 0.75f)); - generate_half_circle(p1, -dir, half, segments, color); - generate_half_circle(p2, dir, half, segments, color); + generate_half_circle(p1, -dir, half, segments, color); + generate_half_circle(p2, dir, half, segments, color); } void BasicRenderer2D::drawRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t thickness) { if (!m_initialized || thickness <= 0) - return; + return; - float half = thickness * 0.5f; + float half = thickness * 0.5f; - // Inset rectangle so the outline stays inside the original bounds - float x1 = rect.x + half; - float y1 = rect.y + half; - float x2 = rect.x + rect.w - half; - float y2 = rect.y + rect.h - half; + // Inset rectangle so the outline stays inside the original bounds + float x1 = rect.x + half; + float y1 = rect.y + half; + float x2 = rect.x + rect.w - half; + float y2 = rect.y + rect.h - half; - // Top - drawLine({ {x1, y1}, {x2, y1} }, color, thickness, false); + // Top + drawLine({ {x1, y1}, {x2, y1} }, color, thickness, false); - // Right - drawLine({ {x2, y1 - half}, {x2, y2 + half} }, color, thickness, false); + // Right + drawLine({ {x2, y1 - half}, {x2, y2 + half} }, color, thickness, false); - // Bottom - drawLine({ {x2, y2}, {x1, y2} }, color, thickness, false); + // Bottom + drawLine({ {x2, y2}, {x1, y2} }, color, thickness, false); - // Left - drawLine({ {x1, y2 + half}, {x1, y1 - half} }, color, thickness, false); + // Left + drawLine({ {x1, y2 + half}, {x1, y1 - half} }, color, thickness, false); } void BasicRenderer2D::drawRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, float radius, int32_t thickness) { if (!m_initialized || thickness <= 0) - return; + return; - // Clamp radius so it never exceeds half the smallest dimension - radius = std::max(0.0f, std::min(radius, std::min(rect.w, rect.h) * 0.5f)); + // Clamp radius so it never exceeds half the smallest dimension + radius = std::max(0.0f, std::min(radius, std::min(rect.w, rect.h) * 0.5f)); - float half = thickness * 0.5f; + float half = thickness * 0.5f; - // Inset rectangle so the border stays INSIDE the given bounds - float x1 = rect.x + half; - float y1 = rect.y + half; - float x2 = rect.x + rect.w - half; - float y2 = rect.y + rect.h - half; + // Inset rectangle so the border stays INSIDE the given bounds + float x1 = rect.x + half; + float y1 = rect.y + half; + float x2 = rect.x + rect.w - half; + float y2 = rect.y + rect.h - half; - // Corner centers - ostd::Vec2 TL { x1 + radius, y1 + radius }; - ostd::Vec2 TR { x2 - radius, y1 + radius }; - ostd::Vec2 BR { x2 - radius, y2 - radius }; - ostd::Vec2 BL { x1 + radius, y2 - radius }; + // Corner centers + ostd::Vec2 TL { x1 + radius, y1 + radius }; + ostd::Vec2 TR { x2 - radius, y1 + radius }; + ostd::Vec2 BR { x2 - radius, y2 - radius }; + ostd::Vec2 BL { x1 + radius, y2 - radius }; - // Straight edges (shortened to meet the arcs cleanly) - drawLine({ {x1 + radius, y1}, {x2 - radius, y1} }, color, thickness, false); // top - drawLine({ {x2, y1 + radius}, {x2, y2 - radius} }, color, thickness, false); // right - drawLine({ {x2 - radius, y2}, {x1 + radius, y2} }, color, thickness, false); // bottom - drawLine({ {x1, y2 - radius}, {x1, y1 + radius} }, color, thickness, false); // left + // Straight edges (shortened to meet the arcs cleanly) + drawLine({ {x1 + radius, y1}, {x2 - radius, y1} }, color, thickness, false); // top + drawLine({ {x2, y1 + radius}, {x2, y2 - radius} }, color, thickness, false); // right + drawLine({ {x2 - radius, y2}, {x1 + radius, y2} }, color, thickness, false); // bottom + drawLine({ {x1, y2 - radius}, {x1, y1 + radius} }, color, thickness, false); // left - // Number of segments for smooth arcs - int segments = std::max(6, int(radius * 0.75f)); + // Number of segments for smooth arcs + int segments = std::max(6, int(radius * 0.75f)); - // Quarter‑ellipse strokes (each is 90°) - generate_ellipse_stroke(TL, radius, radius, thickness, M_PI, M_PI + M_PI * 0.5f, color, segments); // TL - generate_ellipse_stroke(TR, radius, radius, thickness, 1.5f * M_PI, 2.0f * M_PI, color, segments); // TR - generate_ellipse_stroke(BR, radius, radius, thickness, 0.0f, M_PI * 0.5f, color, segments); // BR - generate_ellipse_stroke(BL, radius, radius, thickness, M_PI * 0.5f, M_PI, color, segments); // BL + // Quarter‑ellipse strokes (each is 90°) + generate_ellipse_stroke(TL, radius, radius, thickness, M_PI, M_PI + M_PI * 0.5f, color, segments); // TL + generate_ellipse_stroke(TR, radius, radius, thickness, 1.5f * M_PI, 2.0f * M_PI, color, segments); // TR + generate_ellipse_stroke(BR, radius, radius, thickness, 0.0f, M_PI * 0.5f, color, segments); // BR + generate_ellipse_stroke(BL, radius, radius, thickness, M_PI * 0.5f, M_PI, color, segments); // BL } void BasicRenderer2D::drawCircle(const ostd::Vec2& center, float radius, const ostd::Color& color, int32_t thickness) { if (!m_initialized || thickness <= 0) - return; - int segments = std::max(12, int(radius * 0.75f)); - generate_ellipse_stroke(center, radius, radius, thickness, 0.0f, 2.0f * M_PI, color, segments); + return; + int segments = std::max(12, int(radius * 0.75f)); + generate_ellipse_stroke(center, radius, radius, thickness, 0.0f, 2.0f * M_PI, color, segments); } void BasicRenderer2D::drawCircle(const ostd::Rectangle& rect, const ostd::Color& color, int32_t thickness) { - if (!m_initialized || thickness <= 0) - return; + if (!m_initialized || thickness <= 0) + return; - ostd::Vec2 center { - rect.x + rect.w * 0.5f, - rect.y + rect.h * 0.5f - }; + ostd::Vec2 center { + rect.x + rect.w * 0.5f, + rect.y + rect.h * 0.5f + }; - float radius = std::min(rect.w, rect.h) * 0.5f; - drawCircle(center, radius, color, thickness); + float radius = std::min(rect.w, rect.h) * 0.5f; + drawCircle(center, radius, color, thickness); } void BasicRenderer2D::drawEllipse(const ostd::Rectangle& rect, const ostd::Color& color, int32_t thickness) { if (!m_initialized || thickness <= 0) - return; + return; ostd::Vec2 center = { rect.x + rect.w*0.5f, rect.y + rect.h*0.5f }; - float rx = rect.w * 0.5f; - float ry = rect.h * 0.5f; + float rx = rect.w * 0.5f; + float ry = rect.h * 0.5f; - int segments = std::max(12, int(std::max(rx, ry) * 0.75f)); - generate_ellipse_stroke(center, rx, ry, thickness, 0.0f, 2.0f * M_PI, color, segments); + int segments = std::max(12, int(std::max(rx, ry) * 0.75f)); + generate_ellipse_stroke(center, rx, ry, thickness, 0.0f, 2.0f * M_PI, color, segments); } void BasicRenderer2D::fillRect(const ostd::Rectangle& rect, const ostd::Color& color) { if (!m_initialized) - return; + return; - float x1 = rect.x; - float y1 = rect.y; - float x2 = rect.x + rect.w; - float y2 = rect.y + rect.h; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; - ostd::Vec2 verts[4] = { - { x1, y1 }, - { x2, y1 }, - { x2, y2 }, - { x1, y2 } - }; - uint32_t inds[6] = QUAD_INDICES_ARR; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + ostd::Vec2 verts[4] = { + { x1, y1 }, + { x2, y1 }, + { x2, y2 }, + { x1, y2 } + }; + uint32_t inds[6] = QUAD_INDICES_ARR; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); } void BasicRenderer2D::fillRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, float radius) { if (!m_initialized) - return; + return; - // Clamp radius - radius = std::max(0.0f, std::min(radius, std::min(rect.w, rect.h) * 0.5f)); + // Clamp radius + radius = std::max(0.0f, std::min(radius, std::min(rect.w, rect.h) * 0.5f)); - float x1 = rect.x; - float y1 = rect.y; - float x2 = rect.x + rect.w; - float y2 = rect.y + rect.h; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; - float cx1 = x1 + radius; - float cy1 = y1 + radius; - float cx2 = x2 - radius; - float cy2 = y2 - radius; + float cx1 = x1 + radius; + float cy1 = y1 + radius; + float cx2 = x2 - radius; + float cy2 = y2 - radius; - // Number of segments for smooth corners - int segments = std::max(6, int(radius * 0.75f)); + // Number of segments for smooth corners + int segments = std::max(6, int(radius * 0.75f)); - // - // 1. Fill the center rectangle - // - { - ostd::Vec2 verts[4] = { - { cx1, cy1 }, - { cx2, cy1 }, - { cx2, cy2 }, - { cx1, cy2 } - }; - uint32_t inds[6] = { 0,1,2, 2,3,0 }; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); - } + // + // 1. Fill the center rectangle + // + { + ostd::Vec2 verts[4] = { + { cx1, cy1 }, + { cx2, cy1 }, + { cx2, cy2 }, + { cx1, cy2 } + }; + uint32_t inds[6] = { 0,1,2, 2,3,0 }; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + } - // - // 2. Fill the four side rectangles - // + // + // 2. Fill the four side rectangles + // - // Top - if (radius > 0) - { - ostd::Vec2 verts[4] = { - { x1 + radius, y1 }, - { x2 - radius, y1 }, - { x2 - radius, y1 + radius }, - { x1 + radius, y1 + radius } - }; - uint32_t inds[6] = { 0,1,2, 2,3,0 }; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); - } + // Top + if (radius > 0) + { + ostd::Vec2 verts[4] = { + { x1 + radius, y1 }, + { x2 - radius, y1 }, + { x2 - radius, y1 + radius }, + { x1 + radius, y1 + radius } + }; + uint32_t inds[6] = { 0,1,2, 2,3,0 }; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + } - // Bottom - { - ostd::Vec2 verts[4] = { - { x1 + radius, y2 - radius }, - { x2 - radius, y2 - radius }, - { x2 - radius, y2 }, - { x1 + radius, y2 } - }; - uint32_t inds[6] = { 0,1,2, 2,3,0 }; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); - } + // Bottom + { + ostd::Vec2 verts[4] = { + { x1 + radius, y2 - radius }, + { x2 - radius, y2 - radius }, + { x2 - radius, y2 }, + { x1 + radius, y2 } + }; + uint32_t inds[6] = { 0,1,2, 2,3,0 }; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + } - // Left - { - ostd::Vec2 verts[4] = { - { x1, y1 + radius }, - { x1 + radius, y1 + radius }, - { x1 + radius, y2 - radius }, - { x1, y2 - radius } - }; - uint32_t inds[6] = { 0,1,2, 2,3,0 }; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); - } + // Left + { + ostd::Vec2 verts[4] = { + { x1, y1 + radius }, + { x1 + radius, y1 + radius }, + { x1 + radius, y2 - radius }, + { x1, y2 - radius } + }; + uint32_t inds[6] = { 0,1,2, 2,3,0 }; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + } - // Right - { - ostd::Vec2 verts[4] = { - { x2 - radius, y1 + radius }, - { x2, y1 + radius }, - { x2, y2 - radius }, - { x2 - radius, y2 - radius } - }; - uint32_t inds[6] = { 0,1,2, 2,3,0 }; - push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); - } + // Right + { + ostd::Vec2 verts[4] = { + { x2 - radius, y1 + radius }, + { x2, y1 + radius }, + { x2, y2 - radius }, + { x2 - radius, y2 - radius } + }; + uint32_t inds[6] = { 0,1,2, 2,3,0 }; + push_polygon(verts, nullptr, 4, inds, 6, color, nullptr); + } - // - // 3. Fill the four rounded corners (quarter ellipses) - // + // + // 3. Fill the four rounded corners (quarter ellipses) + // - generate_filled_ellipse_stroke({cx1, cy1}, radius, radius, M_PI, color, segments); // TL - generate_filled_ellipse_stroke({cx2, cy1}, radius, radius, 1.5f * M_PI, color, segments); // TR - generate_filled_ellipse_stroke({cx2, cy2}, radius, radius, 0.0f, color, segments); // BR - generate_filled_ellipse_stroke({cx1, cy2}, radius, radius, 0.5f * M_PI, color, segments); // BL + generate_filled_ellipse_stroke({cx1, cy1}, radius, radius, M_PI, color, segments); // TL + generate_filled_ellipse_stroke({cx2, cy1}, radius, radius, 1.5f * M_PI, color, segments); // TR + generate_filled_ellipse_stroke({cx2, cy2}, radius, radius, 0.0f, color, segments); // BR + generate_filled_ellipse_stroke({cx1, cy2}, radius, radius, 0.5f * M_PI, color, segments); // BL } void BasicRenderer2D::fillCircle(const ostd::Vec2& center, float radius, const ostd::Color& color) { if (!m_initialized) - return; + return; - int segments = std::max(12, int(radius * 0.75f)); - generate_filled_ellipse(center, radius, radius, color, segments); + int segments = std::max(12, int(radius * 0.75f)); + generate_filled_ellipse(center, radius, radius, color, segments); } void BasicRenderer2D::fillCircle(const ostd::Rectangle& rect, const ostd::Color& color) { if (!m_initialized) - return; + return; ostd::Vec2 center { - rect.x + rect.w * 0.5f, - rect.y + rect.h * 0.5f + rect.x + rect.w * 0.5f, + rect.y + rect.h * 0.5f }; float radius = std::min(rect.w, rect.h) * 0.5f; @@ -617,18 +642,18 @@ namespace ogfx void BasicRenderer2D::fillEllipse(const ostd::Rectangle& rect, const ostd::Color& color) { if (!m_initialized) - return; + return; - ostd::Vec2 center { - rect.x + rect.w * 0.5f, - rect.y + rect.h * 0.5f - }; + ostd::Vec2 center { + rect.x + rect.w * 0.5f, + rect.y + rect.h * 0.5f + }; - float radiusX = rect.w * 0.5f; - float radiusY = rect.h * 0.5f; + float radiusX = rect.w * 0.5f; + float radiusY = rect.h * 0.5f; - int segments = std::max(12, int(std::max(radiusX, radiusY) * 0.75f)); - generate_filled_ellipse(center, radiusX, radiusY, color, segments); + int segments = std::max(12, int(std::max(radiusX, radiusY) * 0.75f)); + generate_filled_ellipse(center, radiusX, radiusY, color, segments); } void BasicRenderer2D::outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) @@ -657,11 +682,11 @@ namespace ogfx void BasicRenderer2D::outlinedCircle(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) { if (!m_initialized) - return; + return; ostd::Vec2 center { - rect.x + rect.w * 0.5f, - rect.y + rect.h * 0.5f + rect.x + rect.w * 0.5f, + rect.y + rect.h * 0.5f }; float radius = std::min(rect.w, rect.h) * 0.5f; @@ -696,52 +721,52 @@ namespace ogfx void BasicRenderer2D::generate_half_circle(const ostd::Vec2& center, const ostd::Vec2& dir, float radius, int segments, const ostd::Color& color) { - // Ensure we have room - if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) - flushBatch(); + // Ensure we have room + if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) + flushBatch(); - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - // Index of the center vertex - int centerIndex = m_vertexCount; + // Index of the center vertex + int centerIndex = m_vertexCount; - // Push center vertex - m_vertices[m_vertexCount++] = { - { center.x, center.y }, - col, - { 0.0f, 0.0f }, - }; + // Push center vertex + m_vertices[m_vertexCount++] = { + { center.x, center.y }, + col, + { 0.0f, 0.0f }, + }; - // Compute the base angle from the direction vector - float baseAngle = std::atan2(dir.y, dir.x); + // Compute the base angle from the direction vector + float baseAngle = std::atan2(dir.y, dir.x); - // Half circle spans 180 degrees - float startAngle = baseAngle - M_PI * 0.5f; - float endAngle = baseAngle + M_PI * 0.5f; + // Half circle spans 180 degrees + float startAngle = baseAngle - M_PI * 0.5f; + float endAngle = baseAngle + M_PI * 0.5f; - // Generate arc vertices - for (int i = 0; i <= segments; i++) - { - float t = float(i) / float(segments); - float angle = startAngle + t * (endAngle - startAngle); + // Generate arc vertices + for (int i = 0; i <= segments; i++) + { + float t = float(i) / float(segments); + float angle = startAngle + t * (endAngle - startAngle); - float x = center.x + std::cos(angle) * radius; - float y = center.y + std::sin(angle) * radius; + float x = center.x + std::cos(angle) * radius; + float y = center.y + std::sin(angle) * radius; - m_vertices[m_vertexCount++] = { - { x, y }, - col, - { 0.0f, 0.0f } - }; - } + m_vertices[m_vertexCount++] = { + { x, y }, + col, + { 0.0f, 0.0f } + }; + } - // Generate triangle fan indices - for (int i = 0; i < segments; i++) - { - m_indices[m_indexCount++] = centerIndex; - m_indices[m_indexCount++] = centerIndex + 1 + i; - m_indices[m_indexCount++] = centerIndex + 2 + i; - } + // Generate triangle fan indices + for (int i = 0; i < segments; i++) + { + m_indices[m_indexCount++] = centerIndex; + m_indices[m_indexCount++] = centerIndex + 1 + i; + m_indices[m_indexCount++] = centerIndex + 2 + i; + } } void BasicRenderer2D::generate_quarter_circle(const ostd::Vec2& center, float radius, float thickness, float startAngle, const ostd::Color& color, int segments) @@ -752,11 +777,11 @@ namespace ogfx float innerR = radius - half; if (innerR < 0.0f) - innerR = 0.0f; + innerR = 0.0f; // Ensure capacity if (m_vertexCount + (segments + 1) * 2 >= MaxVertices || m_indexCount + segments * 6 >= MaxIndices) - flushBatch(); + flushBatch(); SDL_FColor col = COLOR_CAST(color); @@ -767,263 +792,263 @@ namespace ogfx // Generate vertices: outer arc + inner arc for (int i = 0; i <= segments; i++) { - float t = float(i) / float(segments); - float angle = startAngle + t * (endAngle - startAngle); + float t = float(i) / float(segments); + float angle = startAngle + t * (endAngle - startAngle); - float cs = std::cos(angle); - float sn = std::sin(angle); + float cs = std::cos(angle); + float sn = std::sin(angle); - // Outer arc - m_vertices[m_vertexCount++] = { - { center.x + cs * outerR, center.y + sn * outerR }, - col, - { 0, 0 } - }; + // Outer arc + m_vertices[m_vertexCount++] = { + { center.x + cs * outerR, center.y + sn * outerR }, + col, + { 0, 0 } + }; - // Inner arc - m_vertices[m_vertexCount++] = { - { center.x + cs * innerR, center.y + sn * innerR }, - col, - { 0, 0 } - }; + // Inner arc + m_vertices[m_vertexCount++] = { + { center.x + cs * innerR, center.y + sn * innerR }, + col, + { 0, 0 } + }; } // Generate indices (triangle strip converted to triangles) for (int i = 0; i < segments; i++) { - int o0 = base + i * 2; - int i0 = o0 + 1; - int o1 = o0 + 2; - int i1 = o0 + 3; + int o0 = base + i * 2; + int i0 = o0 + 1; + int o1 = o0 + 2; + int i1 = o0 + 3; - // Triangle 1 - m_indices[m_indexCount++] = o0; - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i0; + // Triangle 1 + m_indices[m_indexCount++] = o0; + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i0; - // Triangle 2 - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i1; - m_indices[m_indexCount++] = i0; + // Triangle 2 + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i1; + m_indices[m_indexCount++] = i0; } } void BasicRenderer2D::generate_circle_stroke(const ostd::Vec2& center, float radius, float thickness, const ostd::Color& color, int segments) { - float half = thickness * 0.5f; + float half = thickness * 0.5f; - float outerR = radius + half; - float innerR = radius - half; - if (innerR < 0.0f) - innerR = 0.0f; + float outerR = radius + half; + float innerR = radius - half; + if (innerR < 0.0f) + innerR = 0.0f; - // Ensure capacity - if (m_vertexCount + (segments + 1) * 2 >= MaxVertices || m_indexCount + segments * 6 >= MaxIndices) - flushBatch(); + // Ensure capacity + if (m_vertexCount + (segments + 1) * 2 >= MaxVertices || m_indexCount + segments * 6 >= MaxIndices) + flushBatch(); - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - int base = m_vertexCount; + int base = m_vertexCount; - // Generate vertices: outer arc + inner arc - for (int i = 0; i <= segments; i++) - { - float t = float(i) / float(segments); - float angle = t * (2.0f * M_PI); + // Generate vertices: outer arc + inner arc + for (int i = 0; i <= segments; i++) + { + float t = float(i) / float(segments); + float angle = t * (2.0f * M_PI); - float cs = std::cos(angle); - float sn = std::sin(angle); + float cs = std::cos(angle); + float sn = std::sin(angle); - // Outer arc - m_vertices[m_vertexCount++] = { - { center.x + cs * outerR, center.y + sn * outerR }, - col, - { 0, 0 } - }; + // Outer arc + m_vertices[m_vertexCount++] = { + { center.x + cs * outerR, center.y + sn * outerR }, + col, + { 0, 0 } + }; - // Inner arc - m_vertices[m_vertexCount++] = { - { center.x + cs * innerR, center.y + sn * innerR }, - col, - { 0, 0 } - }; - } + // Inner arc + m_vertices[m_vertexCount++] = { + { center.x + cs * innerR, center.y + sn * innerR }, + col, + { 0, 0 } + }; + } - // Generate indices (triangle strip → triangles) - for (int i = 0; i < segments; i++) - { - int o0 = base + i * 2; - int i0 = o0 + 1; - int o1 = o0 + 2; - int i1 = o0 + 3; + // Generate indices (triangle strip → triangles) + for (int i = 0; i < segments; i++) + { + int o0 = base + i * 2; + int i0 = o0 + 1; + int o1 = o0 + 2; + int i1 = o0 + 3; - // Triangle 1 - m_indices[m_indexCount++] = o0; - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i0; + // Triangle 1 + m_indices[m_indexCount++] = o0; + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i0; - // Triangle 2 - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i1; - m_indices[m_indexCount++] = i0; - } + // Triangle 2 + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i1; + m_indices[m_indexCount++] = i0; + } } void BasicRenderer2D::generate_ellipse_stroke(const ostd::Vec2& center, float radiusX, float radiusY, float thickness, float startAngle, float endAngle, const ostd::Color& color, int segments) { - float half = thickness * 0.5f; + float half = thickness * 0.5f; - float outerRX = radiusX + half; - float outerRY = radiusY + half; - float innerRX = std::max(0.0f, radiusX - half); - float innerRY = std::max(0.0f, radiusY - half); + float outerRX = radiusX + half; + float outerRY = radiusY + half; + float innerRX = std::max(0.0f, radiusX - half); + float innerRY = std::max(0.0f, radiusY - half); - // Ensure capacity - if (m_vertexCount + (segments + 1) * 2 >= MaxVertices || - m_indexCount + segments * 6 >= MaxIndices) - { - flushBatch(); - } + // Ensure capacity + if (m_vertexCount + (segments + 1) * 2 >= MaxVertices || + m_indexCount + segments * 6 >= MaxIndices) + { + flushBatch(); + } - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - int base = m_vertexCount; + int base = m_vertexCount; - float angleRange = endAngle - startAngle; + float angleRange = endAngle - startAngle; - // Generate vertices: outer arc + inner arc - for (int i = 0; i <= segments; i++) - { - float t = float(i) / float(segments); - float angle = startAngle + t * angleRange; + // Generate vertices: outer arc + inner arc + for (int i = 0; i <= segments; i++) + { + float t = float(i) / float(segments); + float angle = startAngle + t * angleRange; - float cs = std::cos(angle); - float sn = std::sin(angle); + float cs = std::cos(angle); + float sn = std::sin(angle); - // Outer arc - m_vertices[m_vertexCount++] = { - { center.x + cs * outerRX, center.y + sn * outerRY }, - col, - { 0, 0 } - }; + // Outer arc + m_vertices[m_vertexCount++] = { + { center.x + cs * outerRX, center.y + sn * outerRY }, + col, + { 0, 0 } + }; - // Inner arc - m_vertices[m_vertexCount++] = { - { center.x + cs * innerRX, center.y + sn * innerRY }, - col, - { 0, 0 } - }; - } + // Inner arc + m_vertices[m_vertexCount++] = { + { center.x + cs * innerRX, center.y + sn * innerRY }, + col, + { 0, 0 } + }; + } - // Generate indices (triangle strip → triangles) - for (int i = 0; i < segments; i++) - { - int o0 = base + i * 2; - int i0 = o0 + 1; - int o1 = o0 + 2; - int i1 = o0 + 3; + // Generate indices (triangle strip → triangles) + for (int i = 0; i < segments; i++) + { + int o0 = base + i * 2; + int i0 = o0 + 1; + int o1 = o0 + 2; + int i1 = o0 + 3; - // Triangle 1 - m_indices[m_indexCount++] = o0; - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i0; + // Triangle 1 + m_indices[m_indexCount++] = o0; + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i0; - // Triangle 2 - m_indices[m_indexCount++] = o1; - m_indices[m_indexCount++] = i1; - m_indices[m_indexCount++] = i0; - } + // Triangle 2 + m_indices[m_indexCount++] = o1; + m_indices[m_indexCount++] = i1; + m_indices[m_indexCount++] = i0; + } } void BasicRenderer2D::generate_filled_ellipse_stroke(const ostd::Vec2& center, float radiusX, float radiusY, float startAngle, const ostd::Color& color, int segments) { - // Ensure capacity - if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) - flushBatch(); + // Ensure capacity + if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) + flushBatch(); - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - int base = m_vertexCount; + int base = m_vertexCount; - // Center vertex - m_vertices[m_vertexCount++] = { - { center.x, center.y }, - col, - { 0, 0 } - }; + // Center vertex + m_vertices[m_vertexCount++] = { + { center.x, center.y }, + col, + { 0, 0 } + }; - float endAngle = startAngle + M_PI * 0.5f; + float endAngle = startAngle + M_PI * 0.5f; - for (int i = 0; i <= segments; i++) - { - float t = float(i) / float(segments); - float angle = startAngle + t * (endAngle - startAngle); + for (int i = 0; i <= segments; i++) + { + float t = float(i) / float(segments); + float angle = startAngle + t * (endAngle - startAngle); - float x = center.x + std::cos(angle) * radiusX; - float y = center.y + std::sin(angle) * radiusY; + float x = center.x + std::cos(angle) * radiusX; + float y = center.y + std::sin(angle) * radiusY; - m_vertices[m_vertexCount++] = { - { x, y }, - col, - { 0, 0 } - }; - } + m_vertices[m_vertexCount++] = { + { x, y }, + col, + { 0, 0 } + }; + } - // Triangle fan indices - for (int i = 0; i < segments; i++) - { - m_indices[m_indexCount++] = base; - m_indices[m_indexCount++] = base + 1 + i; - m_indices[m_indexCount++] = base + 2 + i; - } + // Triangle fan indices + for (int i = 0; i < segments; i++) + { + m_indices[m_indexCount++] = base; + m_indices[m_indexCount++] = base + 1 + i; + m_indices[m_indexCount++] = base + 2 + i; + } } void BasicRenderer2D::generate_filled_ellipse(const ostd::Vec2& center, float radiusX, float radiusY, const ostd::Color& color, int segments) { - // Ensure capacity - if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) - flushBatch(); + // Ensure capacity + if (m_vertexCount + segments + 2 >= MaxVertices || m_indexCount + segments * 3 >= MaxIndices) + flushBatch(); - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - int base = m_vertexCount; + int base = m_vertexCount; - // Center vertex - m_vertices[m_vertexCount++] = { - { center.x, center.y }, - col, - { 0, 0 } - }; + // Center vertex + m_vertices[m_vertexCount++] = { + { center.x, center.y }, + col, + { 0, 0 } + }; - // Outer ring vertices - for (int i = 0; i <= segments; i++) - { - float t = float(i) / float(segments); - float angle = t * (2.0f * M_PI); + // Outer ring vertices + for (int i = 0; i <= segments; i++) + { + float t = float(i) / float(segments); + float angle = t * (2.0f * M_PI); - float x = center.x + std::cos(angle) * radiusX; - float y = center.y + std::sin(angle) * radiusY; + float x = center.x + std::cos(angle) * radiusX; + float y = center.y + std::sin(angle) * radiusY; - m_vertices[m_vertexCount++] = { - { x, y }, - col, - { 0, 0 } - }; - } + m_vertices[m_vertexCount++] = { + { x, y }, + col, + { 0, 0 } + }; + } - // Triangle fan indices - for (int i = 0; i < segments; i++) - { - m_indices[m_indexCount++] = base; - m_indices[m_indexCount++] = base + 1 + i; - m_indices[m_indexCount++] = base + 2 + i; - } + // Triangle fan indices + for (int i = 0; i < segments; i++) + { + m_indices[m_indexCount++] = base; + m_indices[m_indexCount++] = base + 1 + i; + m_indices[m_indexCount++] = base + 2 + i; + } } void BasicRenderer2D::push_polygon(const ostd::Vec2* verts, const ostd::Vec2* texCoords, uint32_t vertCount, const uint32_t* inds, uint32_t indexCount, const ostd::Color& color, SDL_Texture* texture) { - if (!m_initialized || vertCount <= 0 || indexCount <= 0) - return; + if (!m_initialized || vertCount <= 0 || indexCount <= 0) + return; if (vertCount > MaxVertices || indexCount > MaxIndices) { @@ -1031,127 +1056,33 @@ namespace ogfx return; } if (texture != m_texture) - flushBatch(); - m_texture = texture; - if (m_vertexCount + vertCount >= MaxVertices || m_indexCount + indexCount >= MaxIndices) - flushBatch(); + flushBatch(); + m_texture = texture; + if (m_vertexCount + vertCount >= MaxVertices || m_indexCount + indexCount >= MaxIndices) + flushBatch(); - SDL_FColor col = COLOR_CAST(color); + SDL_FColor col = COLOR_CAST(color); - int base = m_vertexCount; + int base = m_vertexCount; bool hasTexCoords = texCoords != nullptr; - for (int i = 0; i < vertCount; i++) - { + for (int i = 0; i < vertCount; i++) + { ostd::Vec2 tc { 0.0f, 0.0f }; if (hasTexCoords) tc = texCoords[i]; - m_vertices[m_vertexCount++] = { - { verts[i].x, verts[i].y }, - col, - { tc.x, tc.y }, - }; - } - for (int i = 0; i < indexCount; i++) - m_indices[m_indexCount++] = base + inds[i]; + m_vertices[m_vertexCount++] = { + { verts[i].x, verts[i].y }, + col, + { tc.x, tc.y }, + }; + } + for (int i = 0; i < indexCount; i++) + m_indices[m_indexCount++] = base + inds[i]; } void BasicRenderer2D::print_ttf_error(const ostd::String& funcName) { m_out.fg(ostd::ConsoleColors::Red).p(funcName).p("(...) failed: ").p(ostd::String(SDL_GetError())).reset().nl(); } - - void BasicRenderer2D::renderText(const ostd::String& message, int32_t x, int32_t y, const ostd::Color& color, int32_t fontSize) - { - if (!m_initialized) - { - set_error_state(tErrors::Uninitialized); - return; - } - if (!m_fontOpen) - { - set_error_state(tErrors::NoFont); - return; - } - if (m_font == nullptr) - { - set_error_state(tErrors::NullFont); - return; - } - setFontSize(fontSize); - SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), message.len(), { color.r, color.g, color.b, color.a }); - if (surf == nullptr) - { - set_error_state(tErrors::TTFRenderTextBlendedFail); - print_ttf_error("TTF_RenderText_Blended"); - return; - } - auto renderer = m_window->getSDLRenderer(); - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surf); - if (texture == nullptr) - { - set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); - print_ttf_error("SDL_CreateTextureFromSurface"); - return; - } - SDL_DestroySurface(surf); - - float w = 0, h = 0; - SDL_GetTextureSize(texture, &w, &h); - - SDL_FRect dst; - dst.x = x; - dst.y = y; - dst.w = w; - dst.h = h; - SDL_RenderTexture(renderer, texture, NULL, &dst); - SDL_DestroyTexture(texture); - } - - void BasicRenderer2D::renderCenteredText(const ostd::String& message, int32_t center_x, int32_t center_y, const ostd::Color& color, int32_t fontSize) - { - if (!m_initialized) - { - set_error_state(tErrors::Uninitialized); - return; - } - if (!m_fontOpen) - { - set_error_state(tErrors::NoFont); - return; - } - if (m_font == nullptr) - { - set_error_state(tErrors::NullFont); - return; - } - setFontSize(fontSize); - auto renderer = m_window->getSDLRenderer(); - SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), message.len(), { color.r, color.g, color.b, color.a }); - if (surf == nullptr) - { - set_error_state(tErrors::TTFRenderTextBlendedFail); - print_ttf_error("TTF_RenderText_Blended"); - return; - } - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surf); - if (texture == nullptr) - { - set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); - print_ttf_error("SDL_CreateTextureFromSurface"); - return; - } - SDL_DestroySurface(surf); - - float w = 0, h = 0; - SDL_GetTextureSize(texture, &w, &h); - - SDL_FRect dst; - dst.x = center_x - (w / 2); - dst.y = center_y - (h / 2); - dst.w = w; - dst.h = h; - SDL_RenderTexture(renderer, texture, NULL, &dst); - SDL_DestroyTexture(texture); - } // ===================================================== PRIVATE HELPERS ===================================================== } diff --git a/src/ogfx/render/BasicRenderer.hpp b/src/ogfx/render/BasicRenderer.hpp index 0f49c1b..dfd5b0a 100644 --- a/src/ogfx/render/BasicRenderer.hpp +++ b/src/ogfx/render/BasicRenderer.hpp @@ -1,21 +1,21 @@ /* - OmniaFramework - A collection of useful functionality - Copyright (C) 2025 OmniaX-Dev + OmniaFramework - A collection of useful functionality + Copyright (C) 2025 OmniaX-Dev - This file is part of OmniaFramework. + This file is part of OmniaFramework. - OmniaFramework is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + OmniaFramework is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - OmniaFramework is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + OmniaFramework is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with OmniaFramework. If not, see . + You should have received a copy of the GNU General Public License + along with OmniaFramework. If not, see . */ #pragma once @@ -68,7 +68,7 @@ namespace ogfx int32_t openFont(const ostd::String& fontPath, int32_t fontSize = 0); int32_t openFont(const ostd::UByte resource_data[], uint32_t data_size, int32_t fontSize = 0); int32_t setFontSize(int32_t fontSize); - ostd::IPoint getStringDimensions(const ostd::String& message, int32_t fontSize = 0); + ostd::Vec2 getStringDimensions(const ostd::String& message, int32_t fontSize = 0, TTF_Font* font = nullptr); inline uint32_t getDrawCallCount(void) { return m_drawCallCount; } inline bool hasOpenFont(void) { return m_fontOpen; } @@ -80,11 +80,13 @@ namespace ogfx inline bool isInitialized(void) { return m_initialized; } inline FontGlyphAtlas& getFontGlyphAtlas(void) { return m_fontGlyphAtlas; } - void drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Vec2& size = { 0, 0 }, const ostd::Rectangle& srcRect = { 0, 0, 0, 0 }); - void drawAnimation(const Animation& anim, const ostd::Vec2& position, const ostd::Vec2& size = { 0, 0 }); + void drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Vec2& size = { 0, 0 }, const ostd::Rectangle& srcRect = { 0, 0, 0, 0 }); + void drawAnimation(const Animation& anim, const ostd::Vec2& position, const ostd::Vec2& size = { 0, 0 }); + + void drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize = 0, float scale = 1.0f); + void drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize = 0, float scale = 1.0f); + void drawCenteredString(const ostd::String& str, const ostd::Rectangle& bounds, const ostd::Color& color, int32_t fontSize = 0, float scale = 1.0f); - void drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize = 0); - void drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize = 0); void drawLine(const ostd::FLine& line, const ostd::Color& color, int32_t thickness = 1, bool rounded = true); @@ -106,7 +108,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); - public: + private: 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); @@ -117,8 +119,6 @@ namespace ogfx void push_polygon(const ostd::Vec2* verts, const ostd::Vec2* texCoords, uint32_t vertCount, const uint32_t* inds, uint32_t indexCount, const ostd::Color& color, SDL_Texture* texture); void print_ttf_error(const ostd::String& funcName); inline int32_t set_error_state(int32_t err) { m_errorState = err; return m_errorState; } - void renderText(const ostd::String& message, int32_t x, int32_t y, const ostd::Color& color, int32_t fontSize = 0); - void renderCenteredText(const ostd::String& message, int32_t center_x, int32_t center_y, const ostd::Color& color, int32_t fontSize = 0); public: inline static constexpr int32_t MaxVertices { 8192 }; diff --git a/src/ogfx/render/FontGlyphAtlas.cpp b/src/ogfx/render/FontGlyphAtlas.cpp index d11446b..529486d 100644 --- a/src/ogfx/render/FontGlyphAtlas.cpp +++ b/src/ogfx/render/FontGlyphAtlas.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace ogfx { @@ -37,45 +38,67 @@ namespace ogfx return *this; } - bool FontGlyphAtlas::rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize) + const std::vector FontGlyphAtlas::processString(const ostd::String& str, TTF_Font* font, uint32_t fontSize) + { + if (m_currentAtlasCount <= 0) + return {}; + + // Pass 1: ensure all glyphs are rasterized (map may rehash here) + for (auto& c : str) + { + const GlyphInfo* dummy; + if (!rasterize_glyph(ostd::String("").addChar(c), font, fontSize, &dummy)) + return {}; + } + + // Pass 2: collect stable pointers (no more insertions, no rehash risk) + std::vector glyphs; + glyphs.reserve(str.len()); + for (auto& c : str) + { + auto cps = ostd::String("").addChar(c).getUTF8Codepoints(); + if (cps.size() != 1) return {}; + GlyphKey key { cps[0], uint64_t(font), fontSize }; + glyphs.push_back(&m_uvs[key]); + } + return glyphs; + } + + bool FontGlyphAtlas::rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize, const GlyphInfo** outGlyph) { if (!font || m_currentAtlasCount <= 0) return false; - // 1. Extract codepoint from the ostd::String auto cps = glyphStr.getUTF8Codepoints(); if (cps.size() != 1) - return false; // must be exactly one character + return false; uint32_t codepoint = cps[0]; GlyphKey key { codepoint, uint64_t(font), fontSize }; - if (auto it = m_uvs.find(key) != m_uvs.end()) + auto it = m_uvs.find(key); + if (it != m_uvs.end()) + { + *outGlyph = &(it->second); return true; + } - // 2. Convert the ostd::String to UTF-8 for SDL_ttf - std::string utf8 = glyphStr.cpp_str(); - - // 3. Rasterize glyph using SDL_ttf - SDL_Surface* surf = TTF_RenderText_Blended(font, utf8.c_str(), utf8.length(), SDL_Color{255, 255, 255, 255}); - + SDL_Surface* surf = TTF_RenderText_Blended(font, glyphStr.c_str(), glyphStr.len(), SDL_Color{255, 255, 255, 255}); if (!surf) return false; - // 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 + int minx, maxx, miny, maxy, advance; + TTF_GetGlyphMetrics(font, codepoint, &minx, &maxx, &miny, &maxy, &advance); + + GlyphInfo glyph; + glyph.advance = advance; + glyph.codepoint = codepoint; + 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)) { m_penX = 0; @@ -83,13 +106,12 @@ namespace ogfx m_rowHeight = 0; } - // 6. If still doesn't fit → create new atlas if (m_penY + gh > int(AtlasTextureDimension)) { SDL_DestroySurface(surf); if (!create_new_atlas()) - return false; // no more atlases available + return false; atlas = m_atlases[m_currentAtlasCount - 1]; m_penX = 0; @@ -97,74 +119,28 @@ namespace ogfx m_rowHeight = 0; } - 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_LockSurface(surf); SDL_UpdateTexture(atlas, &dstRect, surf->pixels, surf->pitch); SDL_UnlockSurface(surf); SDL_DestroySurface(surf); - // void* lockedPixels = nullptr; - // int lockedPitch = 0; - // if (SDL_LockTexture(atlas, &dstRect, &lockedPixels, &lockedPitch)) - // { - // SDL_LockSurface(surf); - // 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_UnlockSurface(surf); - // SDL_UnlockTexture(atlas); - // } - // // SDL_UpdateTexture(atlas, &dstRect, surf->pixels, surf->pitch); - // SDL_DestroySurface(surf); - - // 8. Compute UVs float u0 = float(m_penX) / float(AtlasTextureDimension); float v0 = float(m_penY) / float(AtlasTextureDimension); float u1 = float(m_penX + gw) / float(AtlasTextureDimension); float v1 = float(m_penY + gh) / float(AtlasTextureDimension); - GlyphUV uv; - uv.atlas = atlas; - uv.uvs[0] = { u0, v0 }; - uv.uvs[1] = { u1, v0 }; - uv.uvs[2] = { u1, v1 }; - uv.uvs[3] = { u0, v1 }; - uv.size = { static_cast(gw), static_cast(gh) }; + glyph.atlas = atlas; + glyph.uvs[0] = { u0, v0 }; + glyph.uvs[1] = { u1, v0 }; + glyph.uvs[2] = { u1, v1 }; + glyph.uvs[3] = { u0, v1 }; + glyph.size = { static_cast(gw), static_cast(gh) }; - // 9. Store in hashmap - m_uvs[key] = uv; + m_uvs[key] = glyph; + *outGlyph = &(m_uvs[key]); - // 10. Advance shelf packing cursor m_penX += gw; m_rowHeight = std::max(m_rowHeight, gh); return true; @@ -172,7 +148,6 @@ namespace ogfx bool FontGlyphAtlas::create_new_atlas(void) { - // 1. Check if we can create another atlas if (m_currentAtlasCount >= int(MaxAtlasCount)) return false; @@ -183,18 +158,15 @@ namespace ogfx if (!sdlRenderer) return false; - // 2. Create the texture - SDL_Texture* tex = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, AtlasTextureDimension, AtlasTextureDimension); - + SDL_Texture* tex = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, 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 - { + + { // Clear the texture to transparent void* pixels = nullptr; int pitch = 0; if (SDL_LockTexture(tex, nullptr, &pixels, &pitch) == 0) @@ -204,52 +176,13 @@ namespace ogfx } } - // 5. Store the atlas m_atlases[m_currentAtlasCount] = tex; m_currentAtlasCount++; - // 6. Reset packing cursor for this new atlas m_penX = 0; m_penY = 0; m_rowHeight = 0; return true; } - - void FontGlyphAtlas::save_atlas_to_png(SDL_Renderer* renderer, SDL_Texture* atlas, const char* filename) - { - // 1. Get texture dimensions and format - float w, h; - SDL_GetTextureSize(atlas, &w, &h); - int iw = (int)w, ih = (int)h; - - // 2. Create a target surface - SDL_Surface* surf = SDL_CreateSurface(iw, ih, SDL_PIXELFORMAT_ARGB8888); - if (!surf) return; - - // 3. Read pixels back from the texture - SDL_Rect rect { 0, 0, iw, ih }; - - // 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); - } - - // 4. Save - IMG_SavePNG(surf, filename); // needs SDL3_image - // or: SDL_SaveBMP(surf, "atlas_debug.bmp"); - - SDL_DestroySurface(surf); - } } diff --git a/src/ogfx/render/FontGlyphAtlas.hpp b/src/ogfx/render/FontGlyphAtlas.hpp index 748cdfa..a090e8d 100644 --- a/src/ogfx/render/FontGlyphAtlas.hpp +++ b/src/ogfx/render/FontGlyphAtlas.hpp @@ -29,11 +29,13 @@ namespace ogfx class BasicRenderer2D; class FontGlyphAtlas { - public: struct GlyphUV + public: struct GlyphInfo { ostd::Vec2 uvs[4]; SDL_Texture* atlas { nullptr }; ostd::Vec2 size; + uint32_t codepoint { 0 }; + int32_t advance { 0 }; }; struct GlyphKey { @@ -62,18 +64,18 @@ namespace ogfx inline FontGlyphAtlas(void) { } inline FontGlyphAtlas(BasicRenderer2D& renderer) { init(renderer); } FontGlyphAtlas init(BasicRenderer2D& renderer); + const std::vector processString(const ostd::String& str, TTF_Font* font, uint32_t fontSize); - public: - bool rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize); + private: + bool rasterize_glyph(const ostd::String& glyphStr, TTF_Font* font, uint32_t fontSize, const GlyphInfo** outGlyph); 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 }; public: - std::unordered_map m_uvs; + std::unordered_map m_uvs; SDL_Texture* m_atlases[FontGlyphAtlas::MaxAtlasCount]; int32_t m_currentAtlasCount { 0 }; BasicRenderer2D* m_renderer { nullptr }; diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index b736ea5..0787c06 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -1,21 +1,21 @@ /* - OmniaFramework - A collection of useful functionality - Copyright (C) 2025 OmniaX-Dev + OmniaFramework - A collection of useful functionality + Copyright (C) 2025 OmniaX-Dev - This file is part of OmniaFramework. + This file is part of OmniaFramework. - OmniaFramework is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + OmniaFramework is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - OmniaFramework is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + OmniaFramework is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with OmniaFramework. If not, see . + You should have received a copy of the GNU General Public License + along with OmniaFramework. If not, see . */ #include "gui/Events.hpp" @@ -25,42 +25,39 @@ ostd::ConsoleOutputHandler out; -void drawTexturedQuad(SDL_Renderer* renderer, - SDL_Texture* tex, - const ostd::Rectangle& rect, - const ostd::Vec2 uvs[4]) +void drawTexturedQuad(SDL_Renderer* renderer, SDL_Texture* tex, const ostd::Rectangle& rect, const ostd::Vec2 uvs[4]) { - SDL_Vertex verts[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; + // 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 } - }; + 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; + 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; + 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}; - } + 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 }; + // 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); + SDL_RenderGeometry(renderer, tex, verts, 4, indices, 6); } @@ -105,14 +102,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!"); @@ -156,63 +153,7 @@ class Window : public ogfx::gui::Window m_panel1.addChild(m_label3); setTheme(m_theme); - - m_gfx.setFontSize(fs); - m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar('H'), m_gfx.getSDLFont(), fs); - m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar('e'), m_gfx.getSDLFont(), fs); - m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar('l'), m_gfx.getSDLFont(), fs); - m_gfx.getFontGlyphAtlas().rasterize_glyph(ostd::String("").addChar('o'), m_gfx.getSDLFont(), fs); - - // 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); - // } - // } - - // 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 { @@ -226,39 +167,6 @@ class Window : public ogfx::gui::Window void onRedraw(ogfx::BasicRenderer2D& gfx) override { - // auto l_rndPoint = [&](void) -> ostd::FPoint { - // using rnd = ostd::Random; - // return rnd::getVec2({ 0, (float)getWindowWidth() / 2 }, { 0, (float)getWindowHeight() / 2 }); - // }; - // for (int32_t i = 0; i < 1000; i++) - // gfx.drawLine({ l_rndPoint(), l_rndPoint() }, ostd::Colors::Crimson, 10); - // gfx.drawLine({ { 10, 10 }, { 200, 100 } }, ostd::Colors::Crimson, 3); - // 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(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, fs); - } private: @@ -270,7 +178,6 @@ 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)