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

fix #281601 and fix #284218 [workaround]: broken on-screen rendering … #5794

Merged
merged 2 commits into from
Jun 6, 2020

Conversation

AntonioBL
Copy link
Contributor

…of synthetically emboldened fonts

Resolves: https://musescore.org/en/node/284218 and https://musescore.org/en/node/281601

See https://musescore.org/en/node/281601#comment-900261 for the explanation of the Qt bug involved.
This is a workaround.

  • I signed CLA
  • I made sure the code in the PR follows the coding rules
  • I made sure the code compiles on my machine
  • I made sure there are no unnecessary changes in the code
  • I made sure the title of the PR reflects the core meaning of the issue you are solving
  • I made sure the commit message(s) contain a description and answer the question "Why do those changes fix that particular issue?" or "Why are those changes really necessary as improvements?"
  • I made sure the commit message title starts with "fix #424242:" if there is a related issue
  • [N/A] I created the test (mtest, vtest, script test) to verify the changes I made

@AntonioBL
Copy link
Contributor Author

Important note: this PR must be thoroughly tested to check if it does not re-introduce kerning problems (especially under Windows).

libmscore/textbase.cpp Outdated Show resolved Hide resolved
libmscore/textbase.cpp Outdated Show resolved Hide resolved
// only needed for certain artificially emboldened fonts
// see https://musescore.org/en/node/281601#comment-900261
// in Qt 5.12.x this workaround should be no more necessary if
// env variable QT_MAX_CACHED_GLYPH_SIZE is set to 1
Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz Mar 8, 2020

Choose a reason for hiding this comment

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

Would it make sense then to use #if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0))?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe, but we should also check that it is possible to defined QT_MAX_CACHED_GLYPH_SIZE=1 inside MuseScore code (maybe before all the initializations) and that it works without slowing the program (in my pc it does not slow down the program, but better safe than sorry).
This comment is just a reminder that in the future (if and when we will use a newer version of Qt) the workaround could be no more needed.

libmscore/textbase.cpp Outdated Show resolved Hide resolved
@dmitrio95
Copy link
Contributor

Thanks for the workaround and for the explanation you have linked! I have also noticed that if zoom factor in QPainter's world transformation is close to 1 then no issue occurs, but I didn't know the reason of that.

However I am not sure that it would be correct to use this workaround. The reason of my concern is that changing font size actually changes various font properties and metrics in the way which doesn't directly correspond to scaling glyphs of fixed size. One of important aspects of that is spacing between letters being dependent on font size so scaling font size by some factor doesn't actually scale size of a text block by the same factor.

In MuseScore this leads to this workaround leading to noticeable shifts in relative letters positions and occasional collisions of text with other elements when changing zoom level (which is especially noticeable when changing zoom level with Ctrl + mouse wheel). These occasional collisions result from layout assuming font size different to what is used by drawing procedure with this workaround. The implementation in this PR limits this effect to bold text only but independently of font face, so even text using fonts not suffering from the emboldening issues will show the behaviour which I described above.

The part with collisions should probably be resolved if layout would use the same font size as with this workaround, but this also may be not the correct way to handle text layout. It might be arguable and dependent on a particular context, but the primary purpose of layout in MuseScore editor (at least in page mode) seems to be to show the appearance which would be the same as what would be produced after printing the score on a paper. If we accept this purpose of layout as the main one in this context, then the correct way to handle text layout seems to be what is implemented in MuseScore right now: using font size suitable for paper printing and scaling text by pixel transformations rather than by changing font size.

As of Qt 5.12, my self-builds with Qt 5.12.5 (on Linux) still show this emboldening issue, but setting QT_MAX_CACHED_GLYPH_SIZE=1 indeed fixes the issue. I didn't check its effect on text painting performance though but could do that if needed.

@dmitrio95
Copy link
Contributor

By the way, this issue also seems to belong to the same set of font emboldening issues.

@AntonioBL
Copy link
Contributor Author

@dmitrio95 : you are right: I can see this spacing shift when zooming in and out.
I expected that scaling the text dimension and the coordinate system by the same amount would lead to the same text appearance: why isn't it so?
I remember a problem of kerning under Windows being related to an integer truncation, but that was happening only under Windows (see https://musescore.org/en/node/117191 and #3298 ), and I basically was using a similar approach (I don't remember the fine details at the moment: it was a long work of trial and error to find a working solution). I strongly feared that this PR could reopen that bug, but you say it is misbehaving also under Linux. 😞

By the way, issue https://musescore.org/en/node/279766 is exactly this bug.

I don't know if it is possible to somehow identify the problematic fonts.

@mirabilos
Copy link
Contributor

mirabilos commented Mar 8, 2020 via email

@AntonioBL
Copy link
Contributor Author

Well, I tried three times to ask for help in Qt forum these years, but they answered only once.
So I am not confident about having an answer from their side.
In case it is needed for further investigations, here is a minimal example, which can be compiled via QtCreator.
font_bold.zip

@mirabilos
Copy link
Contributor

mirabilos commented Mar 9, 2020 via email

@AntonioBL AntonioBL marked this pull request as draft April 20, 2020 08:56
@AntonioBL AntonioBL changed the base branch from master to 3.x May 4, 2020 18:57
@AntonioBL AntonioBL marked this pull request as ready for review May 4, 2020 19:04
@AntonioBL
Copy link
Contributor Author

I think I managed to make the workaround work.
Tests are welcome, as well as suggestions to improve the PR. 😉

@AntonioBL
Copy link
Contributor Author

Mmmh, the dimension is not yet the correct one, as seen in the vtests, but I think this could be the correct way of takling this bug (hopefully).

@AntonioBL
Copy link
Contributor Author

Ok. I found that this workaround works for screen painting only, but not for png, pdf or svg export. In those cases a further rescaling involving screen dpi and painting device dpi is involved, but I could not find something which perfectly works in all cases (and by perfectly I mean 100% superposition).
By the way, text drawing is a real mess, especially when comparing output between different operating systems: text position is slightly different between the three operating systems for example when exporting a pdf.

libmscore/textbase.cpp Outdated Show resolved Hide resolved
@Jojo-Schmitz
Copy link
Contributor

The remaining small vtest differences might well be due to some floating point rounding going on?

qreal dx = p->worldTransform().dx();
qreal dy = p->worldTransform().dy();
qreal factor = 1 / mm; // correction factor for bold text drawing
p->setMatrix(QMatrix(mm * factor, 0.0, 0.0, mm * factor, dx, dy));
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn’t mm * factor = 1 ?

Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz May 6, 2020

Choose a reason for hiding this comment

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

due to floating point arithmetic not exactly. But should be... so also might be the cause for the remaining small offsets. Guess p->setMatrix(QMatrix(1.0, 0.0, 0.0, 1.0, dx, dy)); would be more correct here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. It is a leftover because at the beginning I was manually adjusting the factor to see when the problem was disappearing.
I found that the problem is indeed the fact that the diagonal elements of the painter matrix are not 1. If you look at exported output in the current MuseScore version, you will see that for svg there is no problem (m11 is forced to 1); for pdf and png when the resolution is 360 or larger there is no problem (m11 is greater or equal to 1, since it is resolution / internal resolution and the internal resolution at the moment is 360).
Well, for pdf the synthetically emboldened fonts are not emboldened, so there is not actually a problem (if we can say so) for m11<1.
For png export, when the resolution is lower than 360, for some font dimensions we see the problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was wondering if maybe use this workaround only when painting to the screen (I also don't know what happens when using two screens at different resolutions), and let the export function unchanged. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

MuseScore doesn't deal too well with 2 screens with different DPIs already (it always only takes the settings of the primary screen as far as I can tell), I don't expect this PR to change that

@mirabilos
Copy link
Contributor

mirabilos commented May 6, 2020 via email

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented May 6, 2020

Yeah, but mathematically, so we could inline this, perhaps as: p->setMatrix(QMatrix(/* mm * factor / 1, 0.0, 0.0, / mm * factor */ 1, dx, dy));

Yes, better for documentation. I'd use 1.0 though

@AntonioBL
Copy link
Contributor Author

By the way, have you tried the artifacts? Do they work ok for you?

@Jojo-Schmitz
Copy link
Contributor

Well, your 2nd commit didn't generate artifacts

@AntonioBL
Copy link
Contributor Author

Ouch, I forgot the collect_artifacts. 😅
The new build should create them.

@AntonioBL
Copy link
Contributor Author

You see problem in exported images only when the resolution is (significantly) below 360.
You can try for example 200 or 100 dpi.

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented May 6, 2020

before (at 96DPI, same as screen, to not compare apples to oranges ;-)):
image
after:
image

@mirabilos
Copy link
Contributor

mirabilos commented May 6, 2020 via email

@mirabilos
Copy link
Contributor

mirabilos commented May 6, 2020 via email

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented May 6, 2020

Would a 170DPI screen count as high (enough) for testing? (it is for reproducing the issue)

@AntonioBL
Copy link
Contributor Author

OK, but please make that still use the (system) freetype library, ideally with fontconfig, so that the user settings for that (what kind of kerning (autohinter, bytecode, etc.) and which fonts to allow with which substitutions etc.) are still honoured. I currently use that to ensure only fonts with a licence suitable for embedding end up in the PDF (glyph substitution can choose an arbitrary one from the system otherwise). Too bad the Qt people appear to not be interested (you said some‐ thing about them not having responded to inquiries regarding this)…

One additional problem of Qt is that even when they solve a bug, most of the time they do not backport it to older Qt versions (such as Qt 5.9.x that we are using for MuseScore).

I think MuseScore is already using Freetype to draw score symbols, but texts have additional difficulties (kerning, ligatures, etc...). Qt is using Harfbuzz for text shaping, but I don't know how complicate a text engine can become.

@AntonioBL
Copy link
Contributor Author

Would a 170DPI screen count as high (enough) for testing? (it is for reproducing the issue)

Maybe, no idea. I think the most problematic cases could be those that require the "-x" ("--gui-scaling") option.

@mirabilos
Copy link
Contributor

mirabilos commented May 6, 2020 via email

@Jojo-Schmitz
Copy link
Contributor

Would a 170DPI screen count as high (enough) for testing? (it is for reproducing the issue)

Maybe, no idea. I think the most problematic cases could be those that require the "-x" ("--gui-scaling") option.

Looks good there too, not using those options

@AntonioBL
Copy link
Contributor Author

I limited the workaround only for the cases in which it could be really necessary. Unfortunately, I don't know how to limit it only to misbehaving fonts (so it is applied to all bold fonts when not exporting a vectorial format and when m11 is < 1).
I also changed the default pdf export dpi to the internal rendering dpi, so that m11 = 1 in that case (similar to svg export case). I don't think it will change much, since pdf should already be a vectorial file (and dpi for pdf export does not make much sense, except if rasterizing the output file).

@AntonioBL AntonioBL marked this pull request as ready for review May 6, 2020 17:03
@AntonioBL AntonioBL mentioned this pull request May 6, 2020
12 tasks
@AntonioBL
Copy link
Contributor Author

I found a better way to deal with font rescaling between the different dpi involved.
I was misled by Qt documentation https://doc.qt.io/qt-5/qtextlayout.html#QTextLayout-3 the function already exists for Qt < 5.13 (but the QPaintDevice is not const).

@mirabilos
Copy link
Contributor

Aww yes: https://doc.qt.io/qt-5.12/qtextlayout.html#QTextLayout-2

This kind of documentation issue is… troubling.

And so much thanks for working on it!

@@ -130,7 +130,7 @@ void Preferences::init(bool storeInMemoryOnly)
{PREF_EXPORT_MP3_BITRATE, new IntPreference(128, false)},
{PREF_EXPORT_MUSICXML_EXPORTBREAKS, new EnumPreference(QVariant::fromValue(MusicxmlExportBreaks::ALL), false)},
{PREF_EXPORT_MUSICXML_EXPORTLAYOUT, new BoolPreference(true, false)},
{PREF_EXPORT_PDF_DPI, new IntPreference(300, false)},
{PREF_EXPORT_PDF_DPI, new IntPreference(DPI, false)},
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn’t this change the default dpi for the PDF export from 300 to 360?

If so, at least https://github.com/musescore/MuseScore/blob/master/build/Linux%2BBSD/mscore.1.in#L209 needs adjusting, and maybe the QCommandLineArg thingy used for --help as well.

Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz May 8, 2020

Choose a reason for hiding this comment

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

That line in the manual page is about PNG resolution and that has been changed to 360 a while ago already

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder whether changing PDF's DPI is the right way, IMHO printers usually default to 300 DPI?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, pdf is a vectorial format, so dpi does not really make sense for it (except if it is rasterizing things as images).
By using pdf dpi equal to internal DPI, we ensure that the matrix elements are 1 and therefore no problems with fonts should appear.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, yes, the DPI is only used for embedded images I guess.

@mirabilos
Copy link
Contributor

mirabilos commented May 8, 2020 via email

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented May 9, 2020

Free manpage fix for you ☻

See #6066

@anatoly-os
Copy link
Contributor

@AntonioBL Do I understand correctly that the workaround is not needed with let's say Qt 5.12.8 and higher?

@AntonioBL
Copy link
Contributor Author

@AntonioBL Do I understand correctly that the workaround is not needed with let's say Qt 5.12.8 and higher?

In principle, the workaround is not needed for Qt >=5.12 if we set QT_MAX_CACHED_GLYPH_SIZE=1 during the program initialization with qputenv (if we do not change the zoom logic).

Maybe the best thing to be done would be to change the zoom logic so that the diagonal elements of the painter are always 1.0 (the coordinate system is not scaled); I think this could also help for Windows font rendering and maybe, once this is implemented, we could return to native font engine (instead of FreeType engine; this would help for loading DRM-protected fonts). However, that would be a huge reworking (with possible additional side-effect bugs).

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.

6 participants