Skip to content

Commit

Permalink
Add Document::updateLayout function
Browse files Browse the repository at this point in the history
  • Loading branch information
sammycage committed Dec 21, 2024
1 parent 18acc06 commit 98a632f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 26 deletions.
9 changes: 7 additions & 2 deletions include/lunasvg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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.
Expand Down
51 changes: 29 additions & 22 deletions source/lunasvg.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "lunasvg.h"
#include "svgelement.h"
#include "svglayoutstate.h"
#include "svgrenderstate.h"

#include <cstring>
Expand Down Expand Up @@ -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) {
Expand All @@ -368,15 +367,15 @@ 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();
}

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;
Expand All @@ -395,7 +394,7 @@ Box Element::getGlobalBoundingBox() const
Box Element::getBoundingBox() const
{
if(m_node)
return element()->paintBoundingBox();
return element(true)->paintBoundingBox();
return Box();
}

Expand All @@ -409,9 +408,12 @@ NodeList Element::children() const
return children;
}

SVGElement* Element::element() const
SVGElement* Element::element(bool layout) const
{
return static_cast<SVGElement*>(m_node);
auto element = static_cast<SVGElement*>(m_node);
if(element && layout)
element->rootElement()->updateLayout();
return element;
}

std::unique_ptr<Document> Document::loadFromFile(const std::string& filename)
Expand Down Expand Up @@ -441,29 +443,32 @@ std::unique_ptr<Document> Document::loadFromData(const char* data, size_t length
std::unique_ptr<Document> 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
Expand All @@ -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<int>(std::ceil(m_rootElement->intrinsicWidth()));
height = static_cast<int>(std::ceil(m_rootElement->intrinsicHeight()));
width = static_cast<int>(std::ceil(intrinsicWidth));
height = static_cast<int>(std::ceil(intrinsicHeight));
} else if(width > 0 && height <= 0) {
height = static_cast<int>(std::ceil(width * m_rootElement->intrinsicHeight() / m_rootElement->intrinsicWidth()));
height = static_cast<int>(std::ceil(width * intrinsicHeight / intrinsicWidth));
} else if(height > 0 && width <= 0) {
width = static_cast<int>(std::ceil(height * m_rootElement->intrinsicWidth() / m_rootElement->intrinsicHeight()));
width = static_cast<int>(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);
Expand Down
14 changes: 14 additions & 0 deletions source/svgelement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 9 additions & 2 deletions source/svgelement.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, SVGElement*, std::less<>> 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 {
Expand Down

0 comments on commit 98a632f

Please sign in to comment.