Skip to content
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

Use usvg to convert SVG to TinyVG. #5

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

RazrFalcon
Copy link

@RazrFalcon RazrFalcon commented Dec 23, 2021

A bare minimum implementation. Produces only text version of TinyVG.

Adds basically full SVG support. resvg is one of the best SVG parsers out there. More specifically it adds text, markers, all the shapes, full SVG path support, CSS, referenced gradients, use/symbol resolving, nested svg and many many more.

Unlike C# version, the final binary is just 2MB or even less if we disable text and strip it.

Unsupported:

  • fill/stroke opacity
  • group opacity
  • basic shapes detection (lines, rectangles)
  • binary TinyVG output

Closes #3

@ikskuh
Copy link

ikskuh commented Dec 23, 2021

This looks promising! Before i merge this i wanna make sure that i can run the benchmarks with the new version as well.

First thing i noticed: It doesn't do invalid argument checking. passing svg2tvgt --strict foo.svg foo.tvgt will tell you "file not found" as a file --strict doesn't exist. But this is something i can check for myself.

Have you enabled PR editing? I'd like to try fixing it myself to get some hands on Rust

@RazrFalcon
Copy link
Author

Yes, editing is enabled.

This is not a final version since I plan to add binary writing first. So not need to merge it anytime soon.
There is not --strict option either.

Benchmarking of what? SVG to TinyVG conversion? usvg supports like 20x more than the previous version, so it would be apples and oranges.

@ikskuh
Copy link

ikskuh commented Dec 23, 2021

Yes, editing is enabled.

Okay, cool. I fixed two bugs in the text generation so i can actually convert to text format. I would keep it that way until we know that everything works as using the text format is definitly easier for debugging and we know that there aren't encoder errors, but only converter errors

Benchmarking of what? SVG to TinyVG conversion? usvg supports like 20x more than the previous version, so it would be apples and oranges.

The benchmark also yields the PDF comparison renderings which i'm generating right now. I will upload my test set for this here, including the SVG input and the PDF output, so you can run the same tests locally. FreeSVG is truly a source of fucked up SVG files 😆

Get the files here:
https://mq32.de/public/tinyvg-dataset-01.zip

Copy link

@ikskuh ikskuh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already identified places where some bugs happen

src/tools/svg2tvgt/src/tvg.rs Outdated Show resolved Hide resolved
src/tools/svg2tvgt/src/tvg.rs Outdated Show resolved Hide resolved
src/tools/svg2tvgt/src/tvg.rs Show resolved Hide resolved
@RazrFalcon
Copy link
Author

If you're looking for really bad SVG, you're welcome: https://github.com/RazrFalcon/resvg-test-suite/tree/master/svg

@RazrFalcon
Copy link
Author

I will take a look into binary encoding tomorrow.

@lyze237
Copy link

lyze237 commented Jan 7, 2022

Thanks for creating that, works quite well already! :)

I have a couple users already using that converter and some of them asked if it would be possible to add transform= attributes as elements with that attribute don't render at all right now.

@RazrFalcon
Copy link
Author

Can you provide a file that fails? transform are supported.

@lyze237
Copy link

lyze237 commented Jan 8, 2022

Sure, sorry should have provided that in the first comment.

Commit: 962ea343bc13cb04b5c1e5374aa0319bd465925b
Clean build: rm r target && cargo build --release

+ svg2tvgtrust fire-1295306.svg fire-1295306.svg.rust.tvgt
+ tvg-text fire-1295306.svg.rust.tvgt -o fire-1295306.svg.rust.tvg
+ tvg-render fire-1295306.svg.rust.tvg

Produces a completely purple (=> transparent) tga file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><radialGradient id="a" cx="71.845" cy="47.719" r="25.128" gradientTransform="matrix(-.00903 -2.7717 2.0794 -.00409 -67.416 1235.7)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2e916"/><stop offset=".312" stop-color="#f11e25"/><stop offset=".729" stop-color="#f2e916"/><stop offset="1" stop-color="#f11e25"/></radialGradient><radialGradient id="b" cx="32.209" cy="49.871" r="16.697" gradientTransform="matrix(2.1889 -.3504 .62278 4.0978 -71.12 844.64)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#faff00"/><stop offset="1" stop-color="#ff713f"/></radialGradient></defs><path fill="url(#a)" d="M4.266 1016.5C14.937 1013.796 17.004 1014.653 15.458 1021.59 17.855 1013.492 13.768 1001.975 6.876 991.75 15.748 992.064 23.083 1001.222 22.648 1017.943 21.83 1005.929 29.175 996.344 35 989.995 34.664 994.485 34.825 1000.25 33.758 1007.307 32.518 1015.514 33.149 1017.597 33.149 1017.597 31.583 1008.956 35.228 1004.69 46.037 992.687 41.406 999.777 45.16 1013.712 43.768 1024.078 42.438 1008.118 53.52 1012.987 52.06 1005.31 53.988 1013.405 48.532 1024.33 49.808 1030.614 53.518 1025.564 53.36 1021.269 53.096 1015.744 55.046 1018.736 54.894 1018.545 55.64 1020.48 59.72 1036.467 55.13 1047.895 35.77 1049.283 25.072 1050.572-4.904 1044.224 9.304 1028.311 11.298 1026.077 15.955 1018.187 4.265 1016.501z" transform="translate(0 -988.36)"/><path fill="url(#b)" d="M17.038 1016.7C12.165 1029.149-19.714 1033.538 32.169 1050.301 50.288 1047.631 64.911 1047.471 61.943 1019.407 59.413 1036.471 55.711 1031.362 51.216 1036.801 55.766 1033.154 51.356 1019.516 58.262 1010.959 51.779 1012.219 46.3 1030.145 39.077 1033.493 56.044 1016.708 42.755 1010.653 48.345 997.539 47.107 1007.543 35.96 1006.53 35.425 1022.784 35.173 1017.072 35.472 1023.199 35.58 1017.284 35.73 1009.09 33.356 1000.77 38.36 991.676 33.66 991.485 20.075 1026.247 24.61 1034.4 19.195 1025.075 28.31 1007.135 16.306 988.49 20.823 1009.465 18.817 1010.328 17.039 1016.7z" transform="matrix(.81593 -.10258 .10857 .86357 -107.09 -843.32)"/></svg>

Even though it generates output:

(tvg 1
  (64 64 1/1 u8888 default)
  (
    (0.949 0.914 0.086 1.000)
    (0.945 0.118 0.145 1.000)
    (0.980 1.000 0.000 1.000)
    (1.000 0.443 0.247 1.000)
  )
  (
    (
      fill_path
      (radial (31.162128 1036.3721) (83.41329 1036.2693) 0 1)
      (
        (4.266 1016.5)
        (
          (bezier - (14.937 1013.796) (17.004 1014.653) (15.458 1021.59))
          (bezier - (17.855 1013.492) (13.768 1001.975) (6.876 991.75))
          (bezier - (15.748 992.064) (23.083 1001.222) (22.648 1017.943))
          (bezier - (21.83 1005.929) (29.175 996.344) (35 989.995))
          (bezier - (34.664 994.485) (34.825 1000.25) (33.758 1007.307))
          (bezier - (32.518 1015.514) (33.149 1017.597) (33.149 1017.597))
          (bezier - (31.583 1008.956) (35.228 1004.69) (46.037 992.687))
          (bezier - (41.406 999.777) (45.16 1013.712) (43.768 1024.078))
          (bezier - (42.438 1008.118) (53.52 1012.987) (52.06 1005.31))
          (bezier - (53.988 1013.405) (48.532 1024.33) (49.808 1030.614))
          (bezier - (53.518 1025.564) (53.36 1021.269) (53.096 1015.744))
          (bezier - (55.046 1018.736) (54.894 1018.545) (55.64 1020.48))
          (bezier - (59.72 1036.467) (55.13 1047.895) (35.77 1049.283))
          (bezier - (25.072 1050.572) (-4.904 1044.224) (9.304 1028.311))
          (bezier - (11.298 1026.077) (15.955 1018.187) (4.265 1016.501))
          (close -)
        )
      )
    )
    (
      fill_path
      (radial (30.44094 1037.7153) (40.8395 1106.1364) 2 3)
      (
        (17.038 1016.7)
        (
          (bezier - (12.165 1029.149) (-19.714 1033.538) (32.169 1050.301))
          (bezier - (50.288 1047.631) (64.911 1047.471) (61.943 1019.407))
          (bezier - (59.413 1036.471) (55.711 1031.362) (51.216 1036.801))
          (bezier - (55.766 1033.154) (51.356 1019.516) (58.262 1010.959))
          (bezier - (51.779 1012.219) (46.3 1030.145) (39.077 1033.493))
          (bezier - (56.044 1016.708) (42.755 1010.653) (48.345 997.539))
          (bezier - (47.107 1007.543) (35.96 1006.53) (35.425 1022.784))
          (bezier - (35.173 1017.072) (35.472 1023.199) (35.58 1017.284))
          (bezier - (35.73 1009.09) (33.356 1000.77) (38.36 991.676))
          (bezier - (33.66 991.485) (20.075 1026.247) (24.61 1034.4))
          (bezier - (19.195 1025.075) (28.31 1007.135) (16.306 988.49))
          (bezier - (20.823 1009.465) (18.817 1010.328) (17.039 1016.7))
          (close -)
        )
      )
    )
  )
)

Here is a zip file with svg, tvgt, tvg and tga files: fire-1295306.zip

@RazrFalcon
Copy link
Author

RazrFalcon commented Jan 8, 2022

gradientTransforms are ignored, because TinyVG doesn't support them. We can still apply them, but they will not look as expected as soon as gradientTransform is more complex than scale+translate. And in your's case it looks like you have skew/rotation.

@raeleus
Copy link

raeleus commented Feb 7, 2022

Hello, I've been working with Lyze and I'm hoping to make a video about TinyVG in a couple months. In using the converter in my workflow, I noticed that SVG's that I've made implement a sort of crop to cut off parts of the image. Can the converter cut these parts off or should that be left to the libraries implementing TinyVG? Please see below:

SVG
button-nw svg.zip

Resulting TVG
button-nw.zip

Should look like this
button-nw

Thanks for your consideration.

@RazrFalcon
Copy link
Author

@raeleus Can you provide both images as PNG to clearly see the difference?

@raeleus
Copy link

raeleus commented Feb 8, 2022

I rendered in my test app. This is how it is supposed to look:
image

This is what I got:
image

@RazrFalcon
Copy link
Author

Seem like a tvg-text or tvg-render bug to me. Produced tvgt looks perfectly fine to me.

@ikskuh
Copy link

ikskuh commented Feb 8, 2022

@raeleus what renderer do you use? svg2tvgt doesn't do any cropping, so it looks like your renderer is actually not clipping the final result to (0, 0) to (5, 5)` but is just drawing all paths.

tvg-render produces the correct result

@raeleus
Copy link

raeleus commented Feb 10, 2022

I'm using Lyze's implementation for libGDX, but it looks like he figured out a solution. Thanks for the help!

@ikskuh
Copy link

ikskuh commented Feb 20, 2022

I found another SVG file that converts to an invalid result:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24">
  <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
</svg>

This is rendered as

while the correct output (generated by the old .NET tool) looks like this:
.

The tool also provides invalid output if a path ends with a move to 0 0 (m 0 0) or similar because it will emit an empty path segment then.

@RazrFalcon
Copy link
Author

RazrFalcon commented Feb 20, 2022

Can you provide a textual/lisp output of the .Net version? usvg is definitely correct here, meaning I'm writing curves wrong.

The tool also provides invalid output if a path ends with a move to 0 0 (m 0 0) or similar because it will emit an empty path segment then.

This is the correct output, according to SVG. It has a concept of zero-length paths. I assume TinyVG doesn't. Will skip them then.
But note that SVG can also have something like M 10 10 L 10 10 and so on. So what we need is to calculate a length of a subpath. Will see.

@ikskuh
Copy link

ikskuh commented Feb 20, 2022

Can you provide a textual/lisp output of the .Net version? usvg is definitely correct here, meaning I'm writing curves wrong.

Sure!

settings.tvgt
(tvg 1
  (24 24 1/1024 u8888 default)
  (
    (0 0 0)
  )
  (
    (fill_path (flat 0)
      (
        (12 15.5)
        (
          (arc_ellipse - 3.5 3.5 0 false false (8.5 12))
          (arc_ellipse - 3.5 3.5 0 false false (12 8.5))
          (arc_ellipse - 3.5 3.5 0 false false (15.5 12))
          (arc_ellipse - 3.5 3.5 0 false false (12 15.5))
        )
        (19.43 12.97)
        (
          (bezier - (19.470001 12.650001) (19.5 12.33) (19.5 12))
          (bezier - (19.5 11.67) (19.47 11.34) (19.43 11))
          (line - 21.54 9.37)
          (bezier - (21.730001 9.22) (21.78 8.95) (21.660002 8.73))
          (line - 19.660002 5.2699995)
          (bezier - (19.54 5.0499997) (19.270002 4.9599996) (19.050001 5.0499997))
          (line - 16.560001 6.0499997)
          (bezier - (16.04 5.66) (15.500002 5.3199997) (14.870001 5.0699997))
          (line - 14.500001 2.4199996)
          (arc_ellipse - 0.506 0.506 0 false true (14 2))
          (horiz - 10)
          (bezier - (9.75 2) (9.54 2.18) (9.5 2.42))
          (line - 9.13 5.07)
          (bezier - (8.5 5.32) (7.96 5.6600003) (7.44 6.05))
          (line - 4.95 5.05)
          (bezier - (4.73 4.96) (4.46 5.05) (4.3399997 5.27))
          (line - 2.3399997 8.73)
          (bezier - (2.2099996 8.95) (2.2699997 9.219999) (2.4599996 9.37))
          (line - 4.57 11)
          (bezier - (4.53 11.34) (4.5 11.67) (4.5 12))
          (bezier - (4.5 12.33) (4.53 12.65) (4.57 12.97))
          (line - 2.4600003 14.63)
          (bezier - (2.2700002 14.78) (2.2100003 15.05) (2.3400004 15.27))
          (line - 4.34 18.73)
          (bezier - (4.46 18.949999) (4.73 19.029999) (4.9500003 18.949999))
          (line - 7.4400005 17.939999)
          (bezier - (7.9600005 18.339998) (8.5 18.679998) (9.130001 18.929998))
          (line - 9.500001 21.579998)
          (bezier - (9.540001 21.819998) (9.750001 21.999998) (10.000001 21.999998))
          (horiz - 14.000001)
          (bezier - (14.250001 21.999998) (14.460001 21.819998) (14.500001 21.579998))
          (line - 14.870001 18.929998)
          (bezier - (15.500001 18.669998) (16.04 18.339998) (16.560001 17.939999))
          (line - 19.050001 18.949999)
          (bezier - (19.27 19.029999) (19.54 18.949999) (19.660002 18.73))
          (line - 21.660002 15.2699995)
          (bezier - (21.780003 15.049999) (21.730001 14.78) (21.54 14.629999))
          (line - 19.43 12.969999)
          (close -)
        )
      )
    )
  )
)

I assume TinyVG doesn't. Will skip them then.

Yeah, an empty path doesn't make sense in the design of TinyVG. I think it's enough to strip off any single M command at the end of a path

But note that SVG can also have something like M 10 10 L 10 10 and so on.

I know, the .NET conversion tool already handles this case

@RazrFalcon
Copy link
Author

I've converted the produced tvgt to SVG by hand and it looks perfectly fine. So the data is correct. Maybe I misunderstood the spec somewhere?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24">
  <path d="
      M 12 15.5
      C 10.067003375592224 15.5 8.5 13.932996624407778 8.5 12
      C 8.5 10.067003375592224 10.067003375592222 8.5 12 8.5
      C 13.932996624407778 8.5 15.5 10.067003375592222 15.5 12
      C 15.5 13.93299662440777 13.932996624407778 15.5 12 15.5
      M 19.43 12.97
      C 19.47 12.65 19.5 12.33 19.5 12
      C 19.5 11.67 19.47 11.34 19.43 11
      L 21.54 9.37
      C 21.73 9.22 21.78 8.95 21.66 8.73
      L 19.66 5.27
      C 19.54 5.05 19.27 4.96 19.05 5.05
      L 16.56 6.05
      C 16.04 5.66 15.5 5.32 14.87 5.07
      L 14.5 2.42
      C 14.46 2.18 14.25 2 14 2
      L 10 2
      C 9.75 2 9.54 2.18 9.5 2.42
      L 9.13 5.07
      C 8.5 5.32 7.96 5.66 7.44 6.05
      L 4.95 5.05
      C 4.73 4.96 4.46 5.05 4.34 5.27
      L 2.34 8.73
      C 2.21 8.95 2.27 9.22 2.46 9.37
      L 4.57 11
      C 4.53 11.34 4.5 11.67 4.5 12
      C 4.5 12.33 4.53 12.65 4.57 12.97
      L 2.46 14.63
      C 2.27 14.78 2.21 15.05 2.34 15.27
      L 4.34 18.73
      C 4.46 18.95 4.73 19.03 4.95 18.95
      L 7.44 17.94
      C 7.96 18.34 8.5 18.68 9.13 18.93
      L 9.5 21.58
      C 9.54 21.82 9.75 22 10 22
      L 14 22
      C 14.25 22 14.46 21.82 14.5 21.58
      L 14.87 18.93
      C 15.5 18.67 16.04 18.34 16.56 17.94
      L 19.05 18.95
      C 19.27 19.03 19.54 18.95 19.66 18.73
      L 21.66 15.27
      C 21.78 15.05 21.73 14.78 21.54 14.63
      L 19.43 12.97
      Z"/>
</svg>

The only difference I see is that you file has resolution of 1/1024, while mine is 1/1.

I would suggest double-checking the tvgt-to-tvg converter or the renderer.

@ikskuh
Copy link

ikskuh commented Feb 20, 2022

I've converted the produced tvgt to SVG by hand and it looks perfectly fine. So the data is correct. Maybe I misunderstood the spec somewhere?

The arc definition has one boolean that is inversly defined to SVG. Here are:

Original SVG
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24">
  <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
</svg>
conversion output of .NET converter
(tvg 1
  (24 24 1/1024 u8888 default)
  (
    (0 0 0)
  )
  (
    (fill_path (flat 0)
      (
        (12 15.5)
        (
          (arc_ellipse - 3.5 3.5 0 false false (8.5 12))
          (arc_ellipse - 3.5 3.5 0 false false (12 8.5))
          (arc_ellipse - 3.5 3.5 0 false false (15.5 12))
          (arc_ellipse - 3.5 3.5 0 false false (12 15.5))
        )
        (19.43 12.97)
        (
          (bezier - (19.470001 12.650001) (19.5 12.33) (19.5 12))
          (bezier - (19.5 11.67) (19.47 11.34) (19.43 11))
          (line - 21.54 9.37)
          (bezier - (21.730001 9.22) (21.78 8.95) (21.660002 8.73))
          (line - 19.660002 5.2699995)
          (bezier - (19.54 5.0499997) (19.270002 4.9599996) (19.050001 5.0499997))
          (line - 16.560001 6.0499997)
          (bezier - (16.04 5.66) (15.500002 5.3199997) (14.870001 5.0699997))
          (line - 14.500001 2.4199996)
          (arc_ellipse - 0.506 0.506 0 false true (14 2))
          (horiz - 10)
          (bezier - (9.75 2) (9.54 2.18) (9.5 2.42))
          (line - 9.13 5.07)
          (bezier - (8.5 5.32) (7.96 5.6600003) (7.44 6.05))
          (line - 4.95 5.05)
          (bezier - (4.73 4.96) (4.46 5.05) (4.3399997 5.27))
          (line - 2.3399997 8.73)
          (bezier - (2.2099996 8.95) (2.2699997 9.219999) (2.4599996 9.37))
          (line - 4.57 11)
          (bezier - (4.53 11.34) (4.5 11.67) (4.5 12))
          (bezier - (4.5 12.33) (4.53 12.65) (4.57 12.97))
          (line - 2.4600003 14.63)
          (bezier - (2.2700002 14.78) (2.2100003 15.05) (2.3400004 15.27))
          (line - 4.34 18.73)
          (bezier - (4.46 18.949999) (4.73 19.029999) (4.9500003 18.949999))
          (line - 7.4400005 17.939999)
          (bezier - (7.9600005 18.339998) (8.5 18.679998) (9.130001 18.929998))
          (line - 9.500001 21.579998)
          (bezier - (9.540001 21.819998) (9.750001 21.999998) (10.000001 21.999998))
          (horiz - 14.000001)
          (bezier - (14.250001 21.999998) (14.460001 21.819998) (14.500001 21.579998))
          (line - 14.870001 18.929998)
          (bezier - (15.500001 18.669998) (16.04 18.339998) (16.560001 17.939999))
          (line - 19.050001 18.949999)
          (bezier - (19.27 19.029999) (19.54 18.949999) (19.660002 18.73))
          (line - 21.660002 15.2699995)
          (bezier - (21.780003 15.049999) (21.730001 14.78) (21.54 14.629999))
          (line - 19.43 12.969999)
          (close -)
        )
      )
    )
  )
)
back conversion of .NET converter
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
  <path style="fill:#000000;stroke:none;" d="M12,15.5A3.5,3.5,0,0,1,8.5,12A3.5,3.5,0,0,1,12,8.5A3.5,3.5,0,0,1,15.5,12A3.5,3.5,0,0,1,12,15.5M19.4296875,12.9697265625C19.4697265625,12.650390625,19.5,12.330078125,19.5,12C19.5,11.669921875,19.4697265625,11.33984375,19.4296875,11L21.5400390625,9.3701171875C21.73046875,9.2197265625,21.7802734375,8.9501953125,21.66015625,8.73046875L19.66015625,5.26953125C19.5400390625,5.0498046875,19.26953125,4.9599609375,19.0498046875,5.0498046875L16.5595703125,6.0498046875C16.0400390625,5.66015625,15.5,5.3203125,14.8701171875,5.0703125L14.5,2.419921875A0.505859375,0.505859375,0,0,0,14,2H10C9.75,2,9.5400390625,2.1796875,9.5,2.419921875L9.1298828125,5.0703125C8.5,5.3203125,7.9599609375,5.66015625,7.4404296875,6.0498046875L4.9501953125,5.0498046875C4.73046875,4.9599609375,4.4599609375,5.0498046875,4.33984375,5.26953125L2.33984375,8.73046875C2.2099609375,8.9501953125,2.26953125,9.2197265625,2.4599609375,9.3701171875L4.5703125,11C4.5302734375,11.33984375,4.5,11.669921875,4.5,12C4.5,12.330078125,4.5302734375,12.650390625,4.5703125,12.9697265625L2.4599609375,14.6298828125C2.26953125,14.7802734375,2.2099609375,15.0498046875,2.33984375,15.26953125L4.33984375,18.73046875C4.4599609375,18.9501953125,4.73046875,19.0302734375,4.9501953125,18.9501953125L7.4404296875,17.9404296875C7.9599609375,18.33984375,8.5,18.6796875,9.1298828125,18.9296875L9.5,21.580078125C9.5400390625,21.8203125,9.75,22,10,22H14C14.25,22,14.4599609375,21.8203125,14.5,21.580078125L14.8701171875,18.9296875C15.5,18.669921875,16.0400390625,18.33984375,16.5595703125,17.9404296875L19.0498046875,18.9501953125C19.26953125,19.0302734375,19.5400390625,18.9501953125,19.66015625,18.73046875L21.66015625,15.26953125C21.7802734375,15.0498046875,21.73046875,14.7802734375,21.5400390625,14.6298828125L19.4296875,12.9697265625Z"/>
</svg>
conversion output of Rust converter
(tvg 1
  (24 24 1/1 u8888 default)
  (
    (0.000 0.000 0.000 1.000)
  )
  (
    (
      fill_path
      (flat 0)
      (
        (12 15.5)
        (
          (bezier - (10.067003375592224 15.5) (8.5 13.932996624407778) (8.5 12))
          (bezier - (8.5 10.067003375592224) (10.067003375592222 8.5) (12 8.5))
          (bezier - (13.932996624407776 8.5) (15.5 10.067003375592224) (15.5 12))
          (bezier - (15.5 13.932996624407776) (13.932996624407776 15.5) (12 15.5))
        )
        (19.43 12.97)
        (
          (bezier - (19.47 12.65) (19.5 12.33) (19.5 12))
          (bezier - (19.5 11.67) (19.47 11.34) (19.43 11))
          (line - 21.54 9.37)
          (bezier - (21.73 9.22) (21.78 8.95) (21.66 8.73))
          (line - 19.66 5.27)
          (bezier - (19.54 5.05) (19.27 4.96) (19.05 5.05))
          (line - 16.56 6.05)
          (bezier - (16.04 5.66) (15.5 5.32) (14.87 5.07))
          (line - 14.5 2.42)
          (bezier - (14.46 2.18) (14.25 2) (14 2))
          (line - 10 2)
          (bezier - (9.75 2) (9.54 2.18) (9.5 2.42))
          (line - 9.13 5.07)
          (bezier - (8.5 5.32) (7.96 5.66) (7.44 6.05))
          (line - 4.95 5.05)
          (bezier - (4.73 4.96) (4.46 5.05) (4.34 5.27))
          (line - 2.34 8.73)
          (bezier - (2.21 8.95) (2.27 9.22) (2.46 9.37))
          (line - 4.57 11)
          (bezier - (4.53 11.34) (4.5 11.67) (4.5 12))
          (bezier - (4.5 12.33) (4.53 12.65) (4.57 12.97))
          (line - 2.46 14.63)
          (bezier - (2.27 14.78) (2.21 15.05) (2.34 15.27))
          (line - 4.34 18.73)
          (bezier - (4.46 18.95) (4.73 19.03) (4.95 18.95))
          (line - 7.44 17.94)
          (bezier - (7.96 18.34) (8.5 18.68) (9.13 18.93))
          (line - 9.5 21.58)
          (bezier - (9.54 21.82) (9.75 22) (10 22))
          (line - 14 22)
          (bezier - (14.25 22) (14.46 21.82) (14.5 21.58))
          (line - 14.87 18.93)
          (bezier - (15.5 18.67) (16.04 18.34) (16.56 17.94))
          (line - 19.05 18.95)
          (bezier - (19.27 19.03) (19.54 18.95) (19.66 18.73))
          (line - 21.66 15.27)
          (bezier - (21.78 15.05) (21.73 14.78) (21.54 14.63))
          (line - 19.43 12.97)
          (close -)
        )
      )
    )
  )
)
back conversion of Rust converter
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
  <path style="fill:#000000;stroke:none;" d="M12,16C10,16,9,14,9,12C9,10,10,9,12,9C14,9,16,10,16,12C16,14,14,16,12,16M19,13C19,13,20,12,20,12C20,12,19,11,19,11L22,9C22,9,22,9,22,9L20,5C20,5,19,5,19,5L17,6C16,6,16,5,15,5L15,2C14,2,14,2,14,2L10,2C10,2,10,2,10,2L9,5C9,5,8,6,7,6L5,5C5,5,4,5,4,5L2,9C2,9,2,9,2,9L5,11C5,11,5,12,5,12C5,12,5,13,5,13L2,15C2,15,2,15,2,15L4,19C4,19,5,19,5,19L7,18C8,18,9,19,9,19L10,22C10,22,10,22,10,22L14,22C14,22,14,22,15,22L15,19C16,19,16,18,17,18L19,19C19,19,20,19,20,19L22,15C22,15,22,15,22,15L19,13Z"/>
</svg>

@RazrFalcon
Copy link
Author

I'm not sure I understood the problem. There are no arcs after the Rust converter. They will be converted into curves.

@ikskuh
Copy link

ikskuh commented Feb 21, 2022

I'm not sure I understood the problem. There are no arcs after the Rust converter. They will be converted into curves.

Then it looks like this conversion from arc to bezier might be wrong? The C# converter just uses the absolute coordinates and passes them to TinyVG, so bezier rendering is definitly correct on my end

@RazrFalcon
Copy link
Author

ArcTo to CurveTo is guarantee to be correct. It is well tested. And resvg produces the correct output.

@ikskuh
Copy link

ikskuh commented Feb 21, 2022

The only difference I see is that you file has resolution of 1/1024, while mine is 1/1.

I think we both have ignored that fact. Sorry for accusing your project of invalid data 🙇

This is the problem here. 1/1 means we can only ever store integer data. The original converter runs the output process several times, starting at the smallest possible scale and reducing precision by one binary digit every iteration when a value is out of bounds. By this, it always has the best possible precision.

Changing this to 1/32 is already giving way better results without those weird jaggies as coordinates don't snap to integers anymore.

@RazrFalcon
Copy link
Author

Good. Can you point me to the code that does precision optimization so I can port it to Rust?

@ikskuh
Copy link

ikskuh commented Feb 21, 2022

It's this part here:

// this loop will only execute when a coordinate won't fit the target range.
// The next lower scale is then selected and it's retried.
while (true)
{
try
{
var sb = new StringBuilder();
var stream = new TvgStream(sb, result);
stream.WriteLine("(tvg 1");
// Console.WriteLine("Use scale factor {0} for size limit {1}", 1 << scale_bits, coordinate_limit);
stream.WriteLine(" ({0} {1} 1/{2} {3} {4})",
result.image_width,
result.image_height,
1 << result.scale_bits,
"u8888",
"default"
);
stream.WriteLine(" (");
// color table
foreach (var col in result.color_table)
{
if (col.A != 1.0)
{
stream.WriteLine(" ({0} {1} {2} {3})", col.R, col.G, col.B, col.A);
}
else
{
stream.WriteLine(" ({0} {1} {2})", col.R, col.G, col.B);
}
}
stream.WriteLine(" )");
stream.WriteLine(" (");
// Console.WriteLine("Found {0} colors in {1} nodes!", result.color_table.Length, intermediate_buffer.node_count);
var pos_pre = sb.Length;
{
// var stream = new TvgStream { stream = ms, ar = result };
TranslateNodes(result, stream, document);
}
if (pos_pre == sb.Length)
{
// throw new NotSupportedException("This SVG does not contain any supported elements!");
}
stream.WriteLine(" )");
stream.WriteLine(")");
return sb.ToString();
}
catch (UnitRangeException ex)
{
if (result.scale_bits == 0)
throw;
Application.Report("Reducing bit range trying to fit {0}", ex.Value);
result.scale_bits -= 1;
}
}

combined with this:

public void WriteUnit(float value)
{
int scale = (1 << ar.scale_bits);
try
{
checked
{
int unit = (int)(value * scale + 0.5);
if (unit < short.MinValue || unit > short.MaxValue)
throw new UnitRangeException(value, unit, scale);
Write("{0}", value);
}
}
catch (OverflowException)
{
throw new UnitRangeException(value, scale);
}
}

@RazrFalcon
Copy link
Author

Thanks, I will take a look.

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

Successfully merging this pull request may close these issues.

Port svg2tvgt to Rust based on resvg
4 participants