-
-
Notifications
You must be signed in to change notification settings - Fork 58
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
Could/should SVGGraphics2D dimensions use double precision? #37
Comments
I don't recall exactly but I suppose my thinking was influenced by:
I'm not against switching the coordinates to use floating precision, but what are your thoughts on a robust implementation that takes care of the last point above? |
I would say there's an expectation of the availability of floating-point precision in SVG these days. I’m just adding some perspective on the overall validity of the original question of supporting the precision, whether optional or default, separate from the caveats and also the limitations of overriding the AWT graphics2D class which has so many integer-based methods. I did an awful lot of SVG work in my last job (which only ended a few weeks ago), in the JavaFX context vs. AWT/Swing, so I belatedly looked into what was going on behind the scenes in the API calls to see if it is informative towards this AWT context and/or the potential for browser rendering gaps. In JavaFX, the Shape classes can set String-based content that is presumed to be SVG commands. These in turn get forwarded to the QuantumToolkit which then returns to the JavaFX Path2D class to do basic input parsing of the SVG commands in similar fashion to what we are used to when writing output parsers (the source code for the JavaFX Path2D class is open so is easy to find in the run-time JAR and to look at in an IDE, and I recommend doing so). Everything is declared as single-precision, and my recollection is that this is the SVG standard par the W3C (yes, in Java double-precision is usually MORE efficient, but a lot of standards, libraries, and languages prefer single-precision). Floating-point precision was absolutely critical for shrunk-down 16x16 vectorized icons in particular, in my last job. And as the use cases of an SVG output parser in downstream workflows is wide-open, I think it highly likely that a lot of consumers of SVG output from Java AWT (or from JavaFX via AWT using the excellent jfxConverter toolkit that I have also made some contributions to but did not author), will want, need, and/or demand floating-point precision vs. integers. Speaking from my own experience, in my long-term job that I held right before the recent short-term startup company job, my review committee for end user beta feedback had me remove SVG support from the app (not the code; just the one line that exposed the menu export method), due to it not being as clean (gaps, integer-slamming, etc.) as PDF, EPS, and other vector output formats. But as SVG is an important part of today’s ecosystem, and is often the only valid choice for a lot of application stacks, I think it’s important we be as up to date as possible in addressing the downstream client needs. The almost universal dominance of retina displays in today's world, means the question of integer addressing of pixels vs. floating-point has a far larger impact than when this library was initially authored. |
Some useful advice in this o'Reilly article on SVG and numeric precision: https://oreillymedia.github.io/Using_SVG/extras/ch08-precision.html "For reliable results cross-browser, use numbers with no more than 2 digits after the decimal and four digits before it." Note that they do mention that scientific notation is supported by the spec (which, as I remembered correctly, is stuck at single-precision as well), as did several other references I found. Additionally, the SVG's that I consumed in my last application development role often had scientific notation as the SVG's were produced by a UX mockup tool and that was how it exported the graphics. https://github.com/cjlano/svg I recommend looking at the Python code in the open source SVG parser linked above, even though it is input-based vs. output-based, as it covers scientific notation using Regex -- maybe not as important on the output side, but sometimes a parser has utility methods that collate information and then have to streamline to remove redundancies, so there might be some parsing of that nature at the output side as well. At any rate, as with the JavaFX Path2D code, it's another example of working with floating-point precision with SVG, and whether any assumptions get made. As for how to enhance an AWT Graphics2D based approach to output parsing with floating-point support, you can see how I did this for EPS in my open source parser at my GitHub page: https://github.com/mhschmieder/epstoolkit Most of the work is decoupled from AWT and Graphics2D and placed in an EpsGraphicsOperators class. I think I borrowed this approach from the jFreeOrg PDF library, but I'll re-check that later. If we needed to let the consumer of the SVG parser library decide on floating-point vs. integer, I think that could be cached as a RenderingHint like so many other things, and deferred until the lowest-level write-to-file operations that output the final SVG commands. The renderingHints approach also makes it easier to specify an algorithm for the integer-conversion ("algorithm" probably isn't the best word for a simple choice of floor, round, ceil, etc.). |
Now I will derail my own ticket entirely - sorry about that! However - this more or less questions whether or not the intention of this issue is relevant at all: I'm aware, that somewhere along the line the SVG needs to be rastered to pixels, which must always result in "a loss of precision" - as one can't display 0.34 pixels.
TL;DR: Edit: Keep it simple! use natural numbers wherever possible and don't bother forcing things upon your result, that will overwhelm your rasterizer. Why I can only blame myself here: The rasterizer asks: "Shall I paint the pixel?" and my answer is: 0.35... the rasterizer could now round up or down or try to blur or fill the pixel partially by introducing an alpha value. I suppose my answer is: This whole issue is caused by: Poor decisions based on the values I had and not on the results I wanted. |
I meant to add that the rejection of SVG by my former employer was before I switched to the JFreeOrg library. I did that on my own while unemployed last summer, and it made a big quality and performance difference. This is my second or third SVG Export solution (the others were all based on Apache Batik) and definitely my final one, which is why I'm motivated to help where I can. Just remember that some of my notes about precision are situationally important. For instance, showing every detail of an IFFT (Inverse Finite Fourier Transform, often used to show an Impulse Response), is critical from a data analysis and comprehension point of view vs. an aesthetic basis. And 16x16 down-sized vector graphics icons may be sharper when left to something like a JavaFX rendering engine to properly downscale, if the original larger-sized SVG vector graphics are precise. On the other side of that issue, a small vector path/shape that is upsized quite a bit, is going to start looking pretty jaggedy if it was slammed to integers. So that's an aesthetic issue when dealing with GUI components that are defined as SVG paths (which in turn might be consuming exports from an app, using an SVG writer). But also a detailed chart with millions of data points (such as an IFFFT), where the point of interest is quite momentary in the time signal, won't give the end user any more detail once zoomed, if the necessary precision was lost in translation. |
Concerning this ticket: If integer dimensions are required, a value of 0 for the maximum fractional digits can be set.
I would not opose such a solution and would still claim, that this would be closer to the SVG specification, as SVGs indeed need not have integer dimensions. To also have said the following:
As you already stated - whether and if something is a good idea depends highly on the context and the intended purpose of the SVG. Concerning my other ramblings: Having read some articles and discussions concerning this topic convinced me, that I should rather prepare the values to be fit being displayed without issues, than trying to convince browsers to handle it differently. Maybe this issue still has merit - I don't know how fx webviews/other components are handling this tbh. Your SVG is rastered at the end - be ready for that and expect losses. This is more or less what I learned here. :) (Even though it is indeed painfully obvious.) |
Thanks for the thoughtful response. I have improved the clarity of my previous response and have augmented with an additional point of interest; still situational in nature. Which just leads me to conclude that the user domain will determine the need, but it should default to what it has been, for the element of least surprise. And using the RenderingHint mechanism to help isolate the changes (along with floating-point versions of the key drawing commands), should also allow for backward compatibility of results (though this would need testing). |
Thanks for all the inputs. I'm going to attempt switching the width and height attributes to double precision. One side-note: all the methods that have int parameters and are marked with @OverRide annotations come from the Java2D Graphics2D API and won't ever be changed in JFreeSVG. Most of them are, in fact, there to ensure the Graphics2D class maintains compatibility with the older java.awt.Graphics class. You can generally perform the same operations, but with double precision, using the draw(Shape) and fill(Shape) methods that were introduced by Java2D (back in 1999). |
Sounds like a good plan, David. And of course this is what I was alluding to when mentioning limitations (integer basis) of AWT's Graphics base class earlier. I think I closely followed your own methodology when I did my EPS library over again from scratch last summer. It allows for a very clean decoupling of integer-vs-float parameter passing and handling, using draw() and shape() as primary "heavy lifters" that then forward to format-specific writers outside the Graphics class hierarchy. As for scientific notation, SVG supports it and many of the numbers I received in SVG icons exported from the UX tool had them, but there was no real rhyme or reason to it, so it was clearly based on nothing mort than space savings in a text-based file. There may be other contexts where scientific notation is desired and should be consistently applied. |
Fix will be included in version 5.0 |
TL;DR:
Why not use double precision for width and height in SVGGraphics2D?
When applying the matrix:
PlanarMatrix transformation = new PlanarMatrix().translate(x, y).scale(1.0, -1.0);
To a created svg, I encountered an odd behaviour and that lead to some questions.
The matrix is used to mirror shapes on the y axis in place. Meaning: The x and y translation is used to translate the mirrored shapes back to their original position, after they have been mirrored.
This matrix had originally been defined and applied to paths rendered to BufferedImage objects.
However if this same method is applied to the SVG, then the position will be off.
The reason is rather obvious:
My translation assumes that the Graphics dimensions equal the dimensions of the contained path. but the svg paths (as is true for paths in general) does not really need to have integer dimensions. A SVG could easily have a height of 5.678, but the used Graphics would have a height of 6px.
Obviously I have to adapt this to reflect the fact, that the path dimensions and the Graphics dimensions can differ. Easy and not a problem at all.
Question:
But what about SVGGraphics2D? Why does it use Integer dimensions in the first place?
(I am fully aware, that I am talking about differences <1 here, and that this might seem to be a non-issue - but the differences are just big enough to be visible and that bothers me. (although I can fix this on my end however.))
The text was updated successfully, but these errors were encountered: