diff --git a/examples/HelloAnimation/main.cpp b/examples/HelloAnimation/main.cpp index 4b63b15..3a05e41 100644 --- a/examples/HelloAnimation/main.cpp +++ b/examples/HelloAnimation/main.cpp @@ -6,7 +6,7 @@ using namespace Crafter; int main() { WindowWayland window(1280, 720, "Hello Input!"); - RenderingElement element( + RenderingElementScaling element( true, //opaque, wether the element is opague or semi-transparant 2, //bufferWidth: the width of this elements pixel buffer 1, //bufferHeight: the height of this elements pixel buffer @@ -21,6 +21,7 @@ int main() { ); window.elements.push_back(&element); + element.buffer = {{255, 0, 0 ,255}, {0, 255, 0 ,255}}; element.UpdatePosition(window); Animation> anim({ @@ -39,7 +40,6 @@ int main() { window.LogTiming(); }); - element.buffer = {{255, 0, 0 ,255}, {0, 255, 0 ,255}}; window.StartUpdate(); window.StartSync(); } diff --git a/implementations/Crafter.Graphics-RenderingElement.cpp b/implementations/Crafter.Graphics-RenderingElement.cpp index 7ecc90a..4dcf6e5 100644 --- a/implementations/Crafter.Graphics-RenderingElement.cpp +++ b/implementations/Crafter.Graphics-RenderingElement.cpp @@ -59,24 +59,36 @@ RenderingElementScaling::RenderingElementScaling(bool opaque, std::uint_fast32_t void RenderingElementPreScaled::UpdatePosition(Window& window) { - std::uint_fast32_t oldWidth = scaled.width; - std::uint_fast32_t oldHeight = scaled.height; + ScaleData oldScale = scaled; window.ScaleElement(*this); - if(oldWidth != scaled.width || oldHeight && scaled.height) { + + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { bufferScaled.resize(scaled.width * scaled.height); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); } + for(Transform* child : children) { child->UpdatePosition(window, *this); } } void RenderingElementPreScaled::UpdatePosition(Window& window, Transform& parent) { - std::uint_fast32_t oldWidth = scaled.width; - std::uint_fast32_t oldHeight = scaled.height; + ScaleData oldScale = scaled; window.ScaleElement(*this, parent); - if(oldWidth != scaled.width || oldHeight && scaled.height) { + + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { bufferScaled.resize(scaled.width * scaled.height); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); } + for(Transform* child : children) { child->UpdatePosition(window, *this); } @@ -103,26 +115,39 @@ void RenderingElementScaling::CopyNearestNeighbour(Pixel_BU8_GU8_RU8_AU8* dst, s } void RenderingElementScaling::UpdatePosition(Window& window) { - std::uint_fast32_t oldWidth = scaled.width; - std::uint_fast32_t oldHeight = scaled.height; + ScaleData oldScale = scaled; window.ScaleElement(*this); - if(oldWidth != scaled.width || oldHeight && scaled.height) { + + + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { bufferScaled.resize(scaled.width * scaled.height); CopyNearestNeighbour(bufferScaled.data(), scaled.width, scaled.height); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); } + for(Transform* child : children) { child->UpdatePosition(window, *this); } } void RenderingElementScaling::UpdatePosition(Window& window, Transform& parent) { - std::uint_fast32_t oldWidth = scaled.width; - std::uint_fast32_t oldHeight = scaled.height; + ScaleData oldScale = scaled; window.ScaleElement(*this, parent); - if(oldWidth != scaled.width || oldHeight && scaled.height) { + + if(oldScale.width != scaled.width || oldScale.height != scaled.height) { bufferScaled.resize(scaled.width * scaled.height); CopyNearestNeighbour(bufferScaled.data(), scaled.width, scaled.height); + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); + } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { + window.AddDirtyRect(oldScale); + window.AddDirtyRect(scaled); } + for(Transform* child : children) { child->UpdatePosition(window, *this); } diff --git a/implementations/Crafter.Graphics-TextElement.cpp b/implementations/Crafter.Graphics-TextElement.cpp index 556146d..f6b193c 100644 --- a/implementations/Crafter.Graphics-TextElement.cpp +++ b/implementations/Crafter.Graphics-TextElement.cpp @@ -34,7 +34,8 @@ TextElement::TextElement(std::int_fast32_t anchorX, std::int_fast32_t anchorY, s } -void TextElement::RenderText(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment, VerticalTextAlignment verticalAlignment, TextOverflowMode overflowMode) { +void TextElement::RenderText(Window& window, const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment, VerticalTextAlignment verticalAlignment, TextOverflowMode overflowMode) { + window.AddDirtyRect(scaled); // Calculate the actual size needed for the text float scale = stbtt_ScaleForPixelHeight(&font.font, size); int baseline = (int)(font.ascent * scale); diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index ef5efdf..f263859 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -17,7 +17,6 @@ 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 Crafter.Graphics:Window_impl; import :Window; import :Transform; @@ -25,7 +24,7 @@ import std; using namespace Crafter; -Window::Window(std::uint_fast32_t width, std::uint_fast32_t height) : width(width), height(height) { +Window::Window(std::int_fast32_t width, std::int_fast32_t height) : width(width), height(height) { } @@ -97,4 +96,32 @@ void Window::LogTiming() { duration_cast(max)) << std::endl; } } -#endif \ No newline at end of file +#endif + +bool Overlaps(const ScaleData& rect1, const ScaleData& rect2) { + return !(rect1.x + rect1.width <= rect2.x || rect2.x + rect2.width <= rect1.x || rect1.y + rect1.height <= rect2.y || rect2.y + rect2.height <= rect1.y); +} + +ScaleData MergeRects(const ScaleData& rect1, const ScaleData& rect2) { + ScaleData merged; + merged.x = std::min(rect1.x, rect2.x); + merged.y = std::min(rect1.y, rect2.y); + merged.width = std::max(rect1.x + rect1.width, rect2.x + rect2.width) - merged.x; + merged.height = std::max(rect1.y + rect1.height, rect2.y + rect2.height) - merged.y; + return merged; +} + +void Window::AddDirtyRect(ScaleData rect) { + bool merged = false; + for (auto& existingRect : dirtyRects) { + if (Overlaps(existingRect, rect)) { + existingRect = MergeRects(existingRect, rect); + merged = true; + break; + } + } + + if (!merged) { + dirtyRects.push_back(rect); + } +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index ce7998c..619b457 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -192,7 +192,6 @@ void WindowWayland::RenderElement(Transform* transform) { // Bounds check for source buffer if (src_x >= 0 && src_x < static_cast(src_width) && src_y >= 0 && src_y < static_cast(src_height)) { - // Direct copy for opaque elements (skip blending) framebuffer[y * width + x] = src_buffer[src_y * src_width + src_x]; } @@ -232,15 +231,27 @@ void WindowWayland::Render() { elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->z < b->z; }); - // Clear screen efficiently using memset - memset(framebuffer, 0, width * height * sizeof(Pixel_BU8_GU8_RU8_AU8)); - - for(Transform* child : elements) { - RenderElement(child); + if (!dirtyRects.empty()) { + for (const ScaleData& rect : dirtyRects) { + for (std::int_fast32_t y = rect.y; y < rect.y + rect.height && y < height; y++) { + for (std::int_fast32_t x = rect.x; x < rect.x + rect.width && x < width; x++) { + framebuffer[y * width + x] = {0, 0, 0, 0}; + } + } + } + + for(Transform* child : elements) { + RenderElement(child); + } + + for (const ScaleData& rect : dirtyRects) { + wl_surface_damage(surface, rect.x, rect.y, rect.width, rect.height); + } + dirtyRects.clear(); } + wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, width, height); wl_surface_commit(surface); } @@ -267,7 +278,7 @@ void WindowWayland::Write(Pixel_BU8_GU8_RU8_AU8* pixels) { } void WindowWayland::Write(std::uint_fast32_t x, std::uint_fast32_t y, Pixel_BU8_GU8_RU8_AU8 pixel) { -framebuffer[y * width + x] = pixel; + framebuffer[y * width + x] = pixel; } Pixel_BU8_GU8_RU8_AU8 WindowWayland::Read(std::uint_fast32_t x, std::uint_fast32_t y) const{ diff --git a/interfaces/Crafter.Graphics-TextElement.cppm b/interfaces/Crafter.Graphics-TextElement.cppm index 391dad4..89b35d4 100644 --- a/interfaces/Crafter.Graphics-TextElement.cppm +++ b/interfaces/Crafter.Graphics-TextElement.cppm @@ -46,6 +46,6 @@ export namespace Crafter { void RenderWrappedLine(const std::string_view line, float scale, int baseline, std::uint_fast32_t startY, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment); public: TextElement(std::int_fast32_t anchorX = FractionalToMapped(0.5), std::int_fast32_t anchorY = FractionalToMapped(0.5), std::uint_fast32_t relativeWidth = FractionalToMapped(1), std::uint_fast32_t relativeHeight = FractionalToMapped(1), 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 RenderText(const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font, TextAlignment alignment = TextAlignment::Left, VerticalTextAlignment verticalAlignment = VerticalTextAlignment::Top, TextOverflowMode overflowMode = TextOverflowMode::Clip); + void RenderText(Window& window, const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font, TextAlignment alignment = TextAlignment::Left, VerticalTextAlignment verticalAlignment = VerticalTextAlignment::Top, TextOverflowMode overflowMode = TextOverflowMode::Clip); }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 4cd77dc..f508d8e 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -48,16 +48,20 @@ export namespace Crafter { class RenderingElement; class Window { public: - std::uint_fast32_t width; - std::uint_fast32_t height; + std::int_fast32_t width; + std::int_fast32_t height; std::chrono::time_point lastFrameBegin; std::vector elements; Event onClose; Event onUpdate; bool open = true; bool updating = false; + + std::vector dirtyRects; + void AddDirtyRect(ScaleData rect); + Window() = default; - Window(std::uint_fast32_t width, std::uint_fast32_t height); + Window(std::int_fast32_t width, std::int_fast32_t height); Window(Window&) = delete; Window(Window&&) = delete; virtual ~Window() = default;