From 83b45a0dea4bd473008131b1b0e23d6ab6a69324 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Mon, 24 Nov 2025 06:08:35 +0100 Subject: [PATCH] fixes --- examples/HelloAnimation/README.md | 32 +++++++ .../Crafter.Graphics-UiElement.cpp | 8 +- ...ter.Graphics-UiElementBufferBufferBase.cpp | 6 +- .../Crafter.Graphics-UiElementImageBuffer.cpp | 63 +++++++++++++ .../Crafter.Graphics-UiElementTextBuffer.cpp | 94 +++++++++++++++++++ .../Crafter.Graphics-Window_wayland.cpp | 2 +- interfaces/Crafter.Graphics-Animation.cppm | 13 +++ interfaces/Crafter.Graphics-UiElement.cppm | 38 ++++++++ project.json | 2 +- 9 files changed, 251 insertions(+), 7 deletions(-) create mode 100644 examples/HelloAnimation/README.md create mode 100644 implementations/Crafter.Graphics-UiElementImageBuffer.cpp create mode 100644 implementations/Crafter.Graphics-UiElementTextBuffer.cpp diff --git a/examples/HelloAnimation/README.md b/examples/HelloAnimation/README.md new file mode 100644 index 0000000..6068a7e --- /dev/null +++ b/examples/HelloAnimation/README.md @@ -0,0 +1,32 @@ +# HelloWindow Example + +## Description + +This example demonstrates how to draw pixels to a window. + +## Expected Result + +A window with a green and blue colored square, when clicking on the square it logs the coordinates relative to the square. + +## Highlighted Code Snippet + +```cpp +UiElement& element = window.elements.emplace_back( + 0.5, + 0.5, + 2, + 1, + 0.5f, + 0.5f, + 0.5, + 0.5, + 0, + false +); +``` + +## How to Run + +```bash +crafter-build build executable -r +``` \ No newline at end of file diff --git a/implementations/Crafter.Graphics-UiElement.cpp b/implementations/Crafter.Graphics-UiElement.cpp index d6114f6..5b064da 100644 --- a/implementations/Crafter.Graphics-UiElement.cpp +++ b/implementations/Crafter.Graphics-UiElement.cpp @@ -45,13 +45,13 @@ UiElementBuffer::UiElementBuffer(std::uint_fast32_t width, std::uint_fast32_t he void UiElement::UpdatePosition(WindowFramebuffer& window) { window.ScaleElement(transform); for(UiElement* child : children) { - UpdatePosition(window, *child); - } + child->UpdatePosition(window, *this); + } } void UiElement::UpdatePosition(WindowFramebuffer& window, UiElement& parent) { window.ScaleElement(transform, parent.transform); for(UiElement* child : children) { - UpdatePosition(window, *child); - } + UpdatePosition(window, *child); + } } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-UiElementBufferBufferBase.cpp b/implementations/Crafter.Graphics-UiElementBufferBufferBase.cpp index 7cf2169..3f8e946 100644 --- a/implementations/Crafter.Graphics-UiElementBufferBufferBase.cpp +++ b/implementations/Crafter.Graphics-UiElementBufferBufferBase.cpp @@ -28,9 +28,13 @@ UiElementBufferBufferBase::UiElementBufferBufferBase(std::uint_fast32_t width, s } void UiElementBufferBufferBase::Create(std::uint_fast32_t width, std::uint_fast32_t height) { - buffer.resize(width*height); + this->width = width; + this->height = height; + buffer.resize(width * height); } void UiElementBufferBufferBase::Resize(std::uint_fast32_t width, std::uint_fast32_t height) { + this->width = width; + this->height = height; buffer.resize(width*height); } void UiElementBufferBufferBase::Resize(std::uint_fast32_t width, std::uint_fast32_t height, std::uint_fast32_t offsetX, std::uint_fast32_t offsetY) { diff --git a/implementations/Crafter.Graphics-UiElementImageBuffer.cpp b/implementations/Crafter.Graphics-UiElementImageBuffer.cpp new file mode 100644 index 0000000..2bcc1de --- /dev/null +++ b/implementations/Crafter.Graphics-UiElementImageBuffer.cpp @@ -0,0 +1,63 @@ +/* +Crafter®.Graphics +Copyright (C) 2025 Catcrafts® +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +module; +#define STB_IMAGE_IMPLEMENTATION +#include "../lib/stb_image.h" +module Crafter.Graphics:UiElementImage_impl; +import :UiElement; +import std; + +using namespace Crafter; + + +UiElementImageBuffer::UiElementImageBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementBufferBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { + +} + +UiElementImageBuffer::UiElementImageBuffer(const std::string_view path, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementBufferBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { + Load(path); +} + +void UiElementImageBuffer::Load(const std::string_view path) { + std::filesystem::path abs = std::filesystem::absolute(path); + int xSize; + int ySize; + unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); + + Create(xSize, ySize); + + for(std::uint_fast32_t x = 0; x < xSize; x++) { + for(std::uint_fast32_t y = 0; y < ySize; y++) { + std::uint_fast32_t idx = (x*ySize+y)*4; + buffer[x*ySize+y].r = bgData[idx]; + buffer[x*ySize+y].g = bgData[idx+1]; + buffer[x*ySize+y].b = bgData[idx+2]; + buffer[x*ySize+y].a = bgData[idx+3]; + } + } +} + +UiElementImageMouseBuffer::UiElementImageMouseBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementImageBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), UiElementMouse() { + +} + +UiElementImageMouseBuffer::UiElementImageMouseBuffer(const std::string_view path, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementImageBuffer(path, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), UiElementMouse() { + +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-UiElementTextBuffer.cpp b/implementations/Crafter.Graphics-UiElementTextBuffer.cpp new file mode 100644 index 0000000..2740106 --- /dev/null +++ b/implementations/Crafter.Graphics-UiElementTextBuffer.cpp @@ -0,0 +1,94 @@ +/* +Crafter®.Graphics +Copyright (C) 2025 Catcrafts® +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +module; +#include "../lib/stb_truetype.h" +module Crafter.Graphics:UiElementTextBuffer_impl; +import :UiElement; +import :Font; +import std; + +using namespace Crafter; + + +UiElementTextBuffer::UiElementTextBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementBufferBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { + +} + +UiElementTextBuffer::UiElementTextBuffer(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementBufferBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { + Render(text, size, color, font); +} + +void UiElementTextBuffer::Render(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font) { + buffer.clear(); + + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + + int baseline = (int)(font.ascent * scale); + + std::uint_fast32_t bufferWidth = 0; + for (const char c : text) { + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + bufferWidth += (int)(advance * scale); + } + + Create(bufferWidth, (font.ascent -font.descent) * scale); + + int x = 0; + for (std::uint_fast32_t i = 0; i < text.size(); i++) { + int codepoint = text[i]; + + int ax; + int lsb; + stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb); + + int c_x1, c_y1, c_x2, c_y2; + stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); + + int w = c_x2 - c_x1; + int h = c_y2 - c_y1; + + std::vector bitmap(w * h); + stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint); + + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufIndex = ((baseline + j + c_y1) * bufferWidth + (x + i + c_x1)); + unsigned char val = bitmap[j * w + i]; + buffer[bufIndex] = {color.r, color.g, color.b, val}; + } + } + + x += (int)(ax * scale); + + if (i + 1 < text.size()) { + x += (int)stbtt_GetCodepointKernAdvance(&font.font, codepoint, text[i+1] * scale); + } + } +} + + +UiElementTextMouseBuffer::UiElementTextMouseBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementTextBuffer(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), UiElementMouse() { + +} + +UiElementTextMouseBuffer::UiElementTextMouseBuffer(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : UiElementTextBuffer(text, size, color, font, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), UiElementMouse() { + +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index 8b4f42b..cac0a38 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -184,7 +184,7 @@ void RenderElement(UiElementBufferBuffer* element, WindowWayland* window) { } std::sort(element->children.begin(), element->children.end(), [](UiElement* a, UiElement* b){ return a->transform.z < b->transform.z; }); for(UiElement* child : element->children) { - RenderElement(element, window); + RenderElement(static_cast(child), window); } } diff --git a/interfaces/Crafter.Graphics-Animation.cppm b/interfaces/Crafter.Graphics-Animation.cppm index bccd2ab..31d24c5 100644 --- a/interfaces/Crafter.Graphics-Animation.cppm +++ b/interfaces/Crafter.Graphics-Animation.cppm @@ -27,6 +27,19 @@ namespace Crafter { return a + static_cast(elapsed * (b - a)); } + // Template specialization for std::string + template <> + std::string Lerp(std::string a, std::string b, double elapsed) { + // Clamp elapsed to [0, 1] + if (elapsed < 0.0) elapsed = 0.0; + if (elapsed > 1.0) elapsed = 1.0; + + // Number of characters from b to reveal + std::size_t len = static_cast(std::floor(b.size() * elapsed)); + + return a + b.substr(0, len); + } + template constexpr auto LerpTupleImpl(const Tuple& a, const Tuple& b, double elapsed, std::index_sequence) { return std::make_tuple(Lerp(std::get(a), std::get(b), elapsed)...); diff --git a/interfaces/Crafter.Graphics-UiElement.cppm b/interfaces/Crafter.Graphics-UiElement.cppm index e8fbaa8..bbe3eae 100644 --- a/interfaces/Crafter.Graphics-UiElement.cppm +++ b/interfaces/Crafter.Graphics-UiElement.cppm @@ -116,6 +116,44 @@ export namespace Crafter { UiElementBufferBuffer(std::uint_fast32_t width, std::uint_fast32_t height, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); }; + + class Font; + class UiElementText { + public: + virtual void Render(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font) = 0; + }; + + class UiElementTextBuffer: public UiElementText, public UiElementBufferBuffer { + public: + UiElementTextBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + UiElementTextBuffer(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + void Render(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font) override; + }; + + class UiElementTextMouseBuffer: public UiElementTextBuffer, public UiElementMouse { + public: + UiElementTextMouseBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + UiElementTextMouseBuffer(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + }; + + class UiElementImage { + public: + virtual void Load(const std::string_view path) = 0; + }; + + class UiElementImageBuffer : public UiElementImage, public UiElementBufferBuffer { + public: + UiElementImageBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + UiElementImageBuffer(const std::string_view path, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + void Load(const std::string_view path) override; + }; + + class UiElementImageMouseBuffer : public UiElementImageBuffer, public UiElementMouse { + public: + UiElementImageMouseBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + UiElementImageMouseBuffer(const std::string_view path, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + }; + class UiElementBufferMouseBuffer : public UiElementBufferBuffer, public UiElementMouse { public: UiElementBufferMouseBuffer(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); diff --git a/project.json b/project.json index 19a6e4f..b651d4a 100644 --- a/project.json +++ b/project.json @@ -3,7 +3,7 @@ "configurations": [ { "name": "base", - "implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-UiElement", "implementations/Crafter.Graphics-UiElementBufferBuffer", "implementations/Crafter.Graphics-UiElementBufferBufferBase", "implementations/Crafter.Graphics-UiElementBufferMouseBuffer"], + "implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-UiElement", "implementations/Crafter.Graphics-UiElementBufferBuffer", "implementations/Crafter.Graphics-UiElementBufferBufferBase", "implementations/Crafter.Graphics-UiElementImageBuffer", "implementations/Crafter.Graphics-UiElementBufferMouseBuffer", "implementations/Crafter.Graphics-UiElementTextBuffer"], "interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-UiElement", "interfaces/Crafter.Graphics-Animation"], "type": "library" },