From 98a632f0bc249949ddd4fa4b0441f7da14b3fc73 Mon Sep 17 00:00:00 2001 From: Samuel Ugochukwu Date: Sat, 21 Dec 2024 14:30:32 +0100 Subject: [PATCH] Add `Document::updateLayout` function --- include/lunasvg.h | 9 ++++++-- source/lunasvg.cpp | 51 ++++++++++++++++++++++++------------------- source/svgelement.cpp | 14 ++++++++++++ source/svgelement.h | 11 ++++++++-- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/include/lunasvg.h b/include/lunasvg.h index 6248e5e..a76eba5 100644 --- a/include/lunasvg.h +++ b/include/lunasvg.h @@ -644,7 +644,7 @@ class LUNASVG_API Element : public Node { private: Element(SVGElement* element); - SVGElement* element() const; + SVGElement* element(bool layout = false) const; friend class Node; friend class Document; }; @@ -707,10 +707,15 @@ class LUNASVG_API Document { Box boundingBox() const; /** - * @brief Updates the layout of the document. + * @brief Updates the layout of the document if needed. */ void updateLayout(); + /** + * @brief Forces an immediate layout update. + */ + void forceLayout(); + /** * @brief Renders the document onto a bitmap using a transformation matrix. * @param bitmap The bitmap to render onto. diff --git a/source/lunasvg.cpp b/source/lunasvg.cpp index 2cb72d1..d824949 100644 --- a/source/lunasvg.cpp +++ b/source/lunasvg.cpp @@ -1,6 +1,5 @@ #include "lunasvg.h" #include "svgelement.h" -#include "svglayoutstate.h" #include "svgrenderstate.h" #include @@ -336,14 +335,14 @@ void Element::render(Bitmap& bitmap, const Matrix& matrix) const return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); - element()->render(state); + element(true)->render(state); } Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) const { if(m_node == nullptr) return Bitmap(); - auto elementBounds = element()->localTransform().mapRect(element()->paintBoundingBox()); + auto elementBounds = element(true)->localTransform().mapRect(element()->paintBoundingBox()); if(elementBounds.isEmpty()) return Bitmap(); if(width <= 0 && height <= 0) { @@ -368,7 +367,7 @@ Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) Matrix Element::getLocalMatrix() const { if(m_node) - return element()->localTransform(); + return element(true)->localTransform(); return Matrix(); } @@ -376,7 +375,7 @@ Matrix Element::getGlobalMatrix() const { if(m_node == nullptr) return Matrix(); - auto transform = element()->localTransform(); + auto transform = element(true)->localTransform(); for(auto parent = element()->parent(); parent; parent = parent->parent()) transform.postMultiply(parent->localTransform()); return transform; @@ -395,7 +394,7 @@ Box Element::getGlobalBoundingBox() const Box Element::getBoundingBox() const { if(m_node) - return element()->paintBoundingBox(); + return element(true)->paintBoundingBox(); return Box(); } @@ -409,9 +408,12 @@ NodeList Element::children() const return children; } -SVGElement* Element::element() const +SVGElement* Element::element(bool layout) const { - return static_cast(m_node); + auto element = static_cast(m_node); + if(element && layout) + element->rootElement()->updateLayout(); + return element; } std::unique_ptr Document::loadFromFile(const std::string& filename) @@ -441,29 +443,32 @@ std::unique_ptr Document::loadFromData(const char* data, size_t length std::unique_ptr document(new Document); if(!document->parse(data, length)) return nullptr; - document->updateLayout(); return document; } float Document::width() const { - return m_rootElement->intrinsicWidth(); + return m_rootElement->updateLayout()->intrinsicWidth(); } float Document::height() const { - return m_rootElement->intrinsicHeight(); + return m_rootElement->updateLayout()->intrinsicHeight(); } Box Document::boundingBox() const { - return m_rootElement->localTransform().mapRect(m_rootElement->paintBoundingBox()); + return m_rootElement->updateLayout()->localTransform().mapRect(m_rootElement->paintBoundingBox()); } void Document::updateLayout() { - SVGLayoutState state; - m_rootElement->layout(state); + m_rootElement->updateLayout(); +} + +void Document::forceLayout() +{ + m_rootElement->forceLayout(); } void Document::render(Bitmap& bitmap, const Matrix& matrix) const @@ -472,24 +477,26 @@ void Document::render(Bitmap& bitmap, const Matrix& matrix) const return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); - m_rootElement->render(state); + m_rootElement->updateLayout()->render(state); } Bitmap Document::renderToBitmap(int width, int height, uint32_t backgroundColor) const { - if(!m_rootElement->intrinsicWidth() || !m_rootElement->intrinsicHeight()) + auto intrinsicWidth = m_rootElement->updateLayout()->intrinsicWidth(); + auto intrinsicHeight = m_rootElement->intrinsicHeight(); + if(intrinsicWidth == 0.f || intrinsicHeight == 0.f) return Bitmap(); if(width <= 0 && height <= 0) { - width = static_cast(std::ceil(m_rootElement->intrinsicWidth())); - height = static_cast(std::ceil(m_rootElement->intrinsicHeight())); + width = static_cast(std::ceil(intrinsicWidth)); + height = static_cast(std::ceil(intrinsicHeight)); } else if(width > 0 && height <= 0) { - height = static_cast(std::ceil(width * m_rootElement->intrinsicHeight() / m_rootElement->intrinsicWidth())); + height = static_cast(std::ceil(width * intrinsicHeight / intrinsicWidth)); } else if(height > 0 && width <= 0) { - width = static_cast(std::ceil(height * m_rootElement->intrinsicWidth() / m_rootElement->intrinsicHeight())); + width = static_cast(std::ceil(height * intrinsicWidth / intrinsicHeight)); } - auto xScale = width / m_rootElement->intrinsicWidth(); - auto yScale = height / m_rootElement->intrinsicHeight(); + auto xScale = width / intrinsicWidth; + auto yScale = height / intrinsicHeight; Matrix matrix(xScale, 0, 0, yScale, 0, 0); Bitmap bitmap(width, height); diff --git a/source/svgelement.cpp b/source/svgelement.cpp index 4bcdf00..efcf5a2 100644 --- a/source/svgelement.cpp +++ b/source/svgelement.cpp @@ -214,6 +214,7 @@ bool SVGElement::setAttribute(const Attribute& attribute) void SVGElement::parseAttribute(PropertyID id, const std::string& value) { + rootElement()->setNeedsLayout(); if(auto property = getProperty(id)) { property->parse(value); } @@ -618,6 +619,13 @@ SVGRootElement::SVGRootElement(Document* document) { } +SVGRootElement* SVGRootElement::updateLayout() +{ + if(needsLayout()) + forceLayout(); + return this; +} + SVGElement* SVGRootElement::getElementById(const std::string_view& id) const { auto it = m_idCache.find(id); @@ -673,6 +681,12 @@ void SVGRootElement::layout(SVGLayoutState& state) } } +void SVGRootElement::forceLayout() +{ + SVGLayoutState state; + layout(state); +} + SVGUseElement::SVGUseElement(Document* document) : SVGGraphicsElement(document, ElementID::Use) , SVGURIReference(this) diff --git a/source/svgelement.h b/source/svgelement.h index 9ac7d7a..6aa7a1b 100644 --- a/source/svgelement.h +++ b/source/svgelement.h @@ -334,14 +334,21 @@ class SVGRootElement final : public SVGSVGElement { float intrinsicWidth() const { return m_intrinsicWidth; } float intrinsicHeight() const { return m_intrinsicHeight; } + void setNeedsLayout() { m_intrinsicWidth = -1.f; } + bool needsLayout() const { return m_intrinsicWidth == -1.f; } + + SVGRootElement* updateLayout(); + SVGElement* getElementById(const std::string_view& id) const; void addElementById(const std::string& id, SVGElement* element); void layout(SVGLayoutState& state) final; + void forceLayout(); + private: std::map> m_idCache; - float m_intrinsicWidth{0}; - float m_intrinsicHeight{0}; + float m_intrinsicWidth{-1.f}; + float m_intrinsicHeight{-1.f}; }; class SVGUseElement final : public SVGGraphicsElement, public SVGURIReference {