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

TextFormatter.getWidthForTextInEm vs SVGContext.measureText used for width calculations #1465

Closed
rvilarl opened this issue Oct 21, 2022 · 7 comments

Comments

@rvilarl
Copy link
Collaborator

rvilarl commented Oct 21, 2022

As discussed in #1459 measureText is slower, however much more precise in the calculation.

If we want to move away from this function we should improve the TextFormatter calculation.

@sschmidTU
Copy link
Contributor

I vote for speed ;) did anyone ever have serious issues with the precision?

@rvilarl
Copy link
Collaborator Author

rvilarl commented Oct 23, 2022

I have been investigating the speed issue and I found a possible solution to get speed but also accuracy. What I did:

  • first I disabled cache both on TextFormatter and SVGContext.
  • then I called the function 1000 times to get a significant duration.

The above showed, as expected, that SVGContext was much slower but it also revealed that the CanvasContext variant was very accurate and fast enough.
So I took an additional step:

  • move from SVG calculation to Canvas calculation within SVGContext
    The results are very good:

"a very long String with Mixed Case Text,(0123456789)" 1000 iterations without cache

  • Textformatter calculates 469.89322916666663 in 2 ms
  • SVGContext.measureText calculates 430.35418701171875 in 1122 ms
  • SVGContext using Canvas calculates 430.45074462890625 in 10 ms
    I would recomment to move to the Canvas approach it is accurate, quick enough and allows to use any Font.

@sschmidTU @AaronDavidNewman @ronyeh what do you think?

@AaronDavidNewman
Copy link
Collaborator

It seems promising but where do you get a HTMLCanvasElement when you are in SvgContext?

@ronyeh
Copy link
Collaborator

ronyeh commented Oct 24, 2022

Aaron brings up a good point. There can be implementations where SVG is supported but Canvas is not.

For example, see the demos in:

vexflow/demos/node/ (especially svg.js).

If we want to use the canvas implementation for measuring text, we need to make sure we have a fallback to the existing or SVG-only method of measuring text. Let's try not to break the small demos in vexflow/demos/.

@rvilarl
Copy link
Collaborator Author

rvilarl commented Oct 24, 2022

@AaronDavidNewman @ronyeh please have a look at the file changes in #1466
@ronyeh vexflow/demos/node/svg.js is not broken

@AaronDavidNewman
Copy link
Collaborator

Did you try using an SVGContext for the measurement, but different than the main one used for the music?

The real problem with using SVGContext is the forced reflow, because you are forcing a redraw every time you measure. Canvas may also be faster in general, I don't know.

I think this idea has promise, but I don't think we should keep a canvas element in FontInfo. I think we should continue to use TextFormatter for this purpose, regardless of how the estimation is actually done. Because TextFormatter has information about fonts and how they're used that isn't contained elsewhere. And we may find an even better way to do estimation in the future (like a browser API) and the dependent logic won't have to change.

  1. A user can set a renderer context specifically for text measurement. Like TextFormatter.setMeasureContext(ctx2).
  2. If TextFormatter doesn't have the exact font available for estimation, it could fall back to the measuring method, if a context is provided (or maybe it always uses the measurement if available, or maybe that's also something you can set).
  3. TextFormatter already has a cache for font settings/text so we don't need dueling caches.

@rvilarl
Copy link
Collaborator Author

rvilarl commented Oct 25, 2022

Did you try using an SVGContext for the measurement, but different than the main one used for the music?

No I did not.

The real problem with using SVGContext is the forced reflow, because you are forcing a redraw every time you measure. Canvas may also be faster in general, I don't know.

The measures above indicate 100 times quicker

I think this idea has promise, but I don't think we should keep a canvas element in FontInfo. I think we should continue to use TextFormatter for this purpose, regardless of how the estimation is actually done. Because TextFormatter has information about fonts and how they're used that isn't contained elsewhere. And we may find an even better way to do estimation in the future (like a browser API) and the dependent logic won't have to change.

Do you want me to move the methods from Font to TextFormatter?

  1. A user can set a renderer context specifically for text measurement. Like TextFormatter.setMeasureContext(ctx2).

I would leave the conetxt as an internal detail. As you said this might change to a different implementation in the future.

  1. If TextFormatter doesn't have the exact font available for estimation, it could fall back to the measuring method, if a context is provided (or maybe it always uses the measurement if available, or maybe that's also something you can set).

If the canvas method works for all, I would use this one and reduce the ammount of static data.

  1. TextFormatter already has a cache for font settings/text so we don't need dueling caches.

I think that with the speed we have, the cache is not required.

@rvilarl rvilarl closed this as completed Oct 28, 2022
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

No branches or pull requests

4 participants