Skip to content

Fix abs_bounding_box calculation for image #924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 23, 2025
Merged

Conversation

Dabble63
Copy link
Contributor

No description provided.

@LaurenzV
Copy link
Collaborator

It's not immediately obvious to me why the previous code is wrong, do you have an example test case where the current code yields a wrong bbox?

@Dabble63
Copy link
Contributor Author

test3
The above file was what I was using for my tests, but I tried the image in various orientations and got strange results.
Gemini explained the issue as follows and my tests showed that the above fix corrected the issue. I have not dived deeply into the code myself so it is possible that the fix causes other issues however I did not see any in my tests.
"""Let's break down why this is incorrect:

rect: This represents the rectangle defined by the x, y, width, and height attributes of the element. It exists in the coordinate system of its parent element.
abs_transform: This is the absolute transformation matrix that maps the image content from its own intrinsic coordinate space directly to the final canvas space.
The code attempts to transform rect (which is in the parent's coordinate space) using abs_transform (which expects coordinates in the image content's space). This mixes two different coordinate systems, leading to an incorrect abs_bounding_box.

The Image node in usvg represents the actual image content. Its abs_bounding_box should, therefore, be the bounding box of this content on the canvas. The code correctly calculates view_box, which is the rectangle the image content occupies within its parent's coordinate system, respecting the preserveAspectRatio attribute.

The correct abs_bounding_box should be the view_box transformed by the parent's absolute transform (parent.abs_transform).
"""

@LaurenzV
Copy link
Collaborator

Yeah you are right, it's currently wrong. I can't push to your branch, can you please add the following test case? Then we can merge

commit e969a4b3ad37cc394e74ae1d07f57982a8e0786f
Author: Laurenz Stampfl <[email protected]>
Date:   Sun Jun 22 11:40:31 2025 +0200

    Add test case

diff --git a/crates/usvg/tests/parser.rs b/crates/usvg/tests/parser.rs
index 3ed40a50..82a78c08 100644
--- a/crates/usvg/tests/parser.rs
+++ b/crates/usvg/tests/parser.rs
@@ -1,6 +1,7 @@
 // Copyright 2018 the Resvg Authors
 // SPDX-License-Identifier: Apache-2.0 OR MIT
 
+use tiny_skia_path::Rect;
 use usvg::Color;
 
 #[test]
@@ -502,3 +503,33 @@ fn svg_without_xmlns() {
     let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).unwrap();
     assert_eq!(tree.size(), usvg::Size::from_wh(100.0, 100.0).unwrap());
 }
+
+#[test]
+fn image_bbox_with_parent_transform() {
+    let svg = "
+    <svg viewBox='0 0 200 200' 
+         xmlns='http://www.w3.org/2000/svg'
+         xmlns:xlink='http://www.w3.org/1999/xlink'>
+        <g transform='translate(25 25)'>
+            <image id='image1' x='10' y='10' width='50' height='50' xlink:href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAABb0lEQVR4Xu3VUQ0AIAzEUOZfA87wAgkq+vGmoGlz2exz73IZAyNIpsUHEaTVQ5BYD0EEqRmI8fghgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxnAdKSlrwlejIDgAAAABJRU5ErkJggg=='/>
+        </g>
+    </svg>
+    ";
+
+    let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).unwrap();
+
+    let usvg::Node::Group(group_node1) = &tree.root().children()[0] else {
+        unreachable!()
+    };
+    let usvg::Node::Group(group_node2) = &group_node1.children()[0] else {
+        unreachable!()
+    };
+    let usvg::Node::Image(image_node) = &group_node2.children()[0] else {
+        unreachable!()
+    };
+
+    assert_eq!(
+        image_node.abs_bounding_box(),
+        Rect::from_xywh(35.0, 35.0, 50.0, 50.0).unwrap()
+    );
+}

@RazrFalcon
Copy link
Collaborator

I'm surprised that AI was able to figure it out... it does fix the issue.

Now, the reason why it doesn't affect tests is because abs_bounding_box doesn't actually used during rendering. It's just a useful API.
And also because we do not have any tests for this API either...

Now, I'm not sure if abs_transform is calculated correctly here, but oh well. Leave it for the next time.

@Dabble63 can you add the following code to crates/resvg/examples/bboxes.svg as well

    <text x="350" y="250">Image with transform</text>
    <image x="20" y="40" width="60" height="30" transform="rotate(45) translate(490 -40)"
           preserveAspectRatio="none" xlink:href="ferris.png"/>

as some sort of a manual test.

@Dabble63
Copy link
Contributor Author

I am not sure what the formatting issue is but otherwise looks good

@LaurenzV
Copy link
Collaborator

You just need to run ‘cargo fmt’

@Dabble63
Copy link
Contributor Author

Looks good now

@LaurenzV LaurenzV changed the title Correct abs_bounding_box for image Fix abs_bounding_box calculation for image Jun 23, 2025
@LaurenzV LaurenzV merged commit 881ed7b into linebender:main Jun 23, 2025
5 checks passed
@LaurenzV
Copy link
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants