-
-
Notifications
You must be signed in to change notification settings - Fork 681
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 misc CoreText rendering bugs and issues with clipping and composing characters #1356
Merged
ychin
merged 4 commits into
macvim-dev:master
from
ychin:coretext-rendering-fixes-composing-chars-clipping
Jan 30, 2023
Merged
Fix misc CoreText rendering bugs and issues with clipping and composing characters #1356
ychin
merged 4 commits into
macvim-dev:master
from
ychin:coretext-rendering-fixes-composing-chars-clipping
Jan 30, 2023
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51e60aa
to
36c4629
Compare
36c4629
to
bce3ca2
Compare
The setting was added to "preserve" the font's original line spacing intents, but after further investigation I cannot find why Apple's calculation for font line spacing to be so wide. Furthermore it's quite odd because the default line spacing is wide, but the only thing that setting does is by re-initializing the font under Core Text (instead of using NSFont like the default) using CTFont and somehow the issue is fixed. Inspecting font metrics (using ttx) also didn't seem to give any hints why the spacing are so wide (e.g. ascent / descent / line gap etc all look fine). A StackOverflow comment seems to suggest that Apple is simply adding a 1.2x scale to some fonts (https://stackoverflow.com/questions/5511830/how-does-line-spacing-work-in-core-text-and-why-is-it-different-from-nslayoutm) which seems to match the observation. My guess is that Apple looks at some fonts and think they are too aggressive in their font metrics design and "helpfully" introduces a 1.2x multiplier to space them out, where Core Text is lower level than AppKit and therefore does not have this auto-inflation. By renaming the option it should make it clearer that this is a somewhat arbitrary distinction instead of it being an inherent property of the font.
I have looked and I do not believe there is a good reason to do this rounding up at all. I believe the motivation is to align the baseline with the pixel boundary, but I don't believe that is necessary, given that Core Text will properly perform the correct antialiasing for us. Most texts aren't directly aligned at the baseline anyway. Before, that ceil() option is a reason why sometimes fonts would feel pushed upwards, clipping emojis to the top, and creating a lopsided effect for fonts like Fira Code (h11), since we draw the boxes aligned to the line height at the bottom of the descender, meaning the ceil() has an effect of pushing the text up.
MacVim previously didn't really render composing characters with multiple glyphs correctly. For simple ones like 'â' it would work fine because Core Text just generates one glyph for it, but for more complicated ones like 'x⃗' the renderer didn't properly query the advance of the glyphs to put them at the correct position. Refactor the logic to keep track of the current cell/glyphs and make sure to track the advances fo the glyphs so the composing chars' glyphs would be laid out properly on top of the cell. We need to be careful with the tracking, because in Vim we force the text rendering to be monospaced, and so we maintain the tracking within a single grapheme (which can consist of multiple glyphs), but when we move to the next grapheme we reset the position so we can get proper monospaced rendering even for non-monospaced texts like CJK or stylized texts. Fix macvim-dev#995 Fix macvim-dev#1172
This fixes the issue where particularly tall characters will get clipped at the row boundary. This happens because even though a font describes the line height with font metrics, individual glyphs do not have to respect them, and we can see with emoji rendering sometimes they can poke upwards past the line height. Also, it's trivially easy to construct composing characters that become really tall, e.g. "x゙̂⃗", or Tibetan scripts like "ཧཱུྃ". To fix this, we do the following: 1. Remove the explicit clipping call at rendering. 2. Fix partial redraw to not lead to clipping / corruption. This is quite tricky, because let's say we have a character that is tall enough to touch other lines, if those lines are redraw but not the line with the tall char, the redraw will paint over the parts of the glyphs poking through. Alternatively if we redraw the line with the tall chars we also need to expand the redraw region to make sure other lines get repainted as well. To fix this properly, we should do a proper glyph calculation when we receive the draw command before we issue before we call `setNeedsDisplayInRect`, but since right now we only extract glyph info later (during drawRect call), it's too late. We just do the hacky solution by storing a variable `redrawExpandRows` that tracks how many lines to expand for all lines. It's a little hacky since this will affect all lines permanently regardless if they have tall characters or not. The proper fix may come later as an optimization (or when we do hardware rendering via Metal). 3. Re-order the rendering so we have a two pass system, where we first draw the background fill color for all rows, then the text. This helps prevent things like Vim's window split or cursorline from obscuring the text. 4. Add a preference to turn on clipping (old behavior). This helps prevent odd issues with really tall texts (e.g. Zalgo text) making it hard to see what's going on. The preference `MMRendererClipToRow` is not exposed in UI for now as it's a relatively niche. It will be exposed later when we have a dedicated render tab in settings. Note that a lot of these characters only show their full height by doing `set maxcombine=8` because the default (2) is quite low. Part of the fix for macvim-dev#995
bce3ca2
to
d1c8e61
Compare
ychin
added a commit
that referenced
this pull request
Feb 7, 2023
Updated to Vim 9.0.1276 Features ==================== Dictionary lookup -------------------- You can now use Force Touch or Cmd-Ctrl-D to look up definitions of word under the cursor (or selected text in visual mode). This will also preview URLs, and support data types such as phone numbers and addresses. #1312 #1313 This feature can also be invoked programmatically from VimScript (see `:h macvim-lookup`). #1315 Tool bar / Touch Bar / menu icons -------------------- You can now use SF Symbols for Tool bar and Touch Bar icons, including using different symbol styles such as "palette" or "multicolor". Menu items can now also use the `icon=` syntax to specify icons as well. See `:help macvim-toolbar-icon` for details. #1329 The default tool bar also has updated icons to look similar to SF Symbols used by newer macOS versions. #1214 by @sfsam Window management actions -------------------- There are new `macaction`'s for managing the MacVim window. The new `zoomLeft`/`zoomRight` actions allow you to pin the window to the left/right of the screen, and there are also new actions for interacting with Stage Manager (requires macOS 13+). See `:h macvim-actions` for details. #1330 Pre-release updates / Sparkle 2 -------------------- MacVim now supports pre-release software builds. It's sometimes hard for us to release frequent updates due to the desire to pick a stable upstream Vim version, needing to test the release on multiple OS versions, making sure there aren't half-complete or buggy features, and other reasons. This new feature now allows us to push pre-release beta builds out in a more frequent fashion, which could be useful if there are particular features or fixes that you would like to try out before the next official release. Pre-release builds will be released depending on bug fixes and features instead of a fixed cadence. Do note that these pre-release builds may not be as well-validated and may have half-baked features. If you are using the built-in auto-updater to update MacVim, you can turn this on by going to Advanced settings pane, and enable "Enable pre-release software updates". This feature is only available for macOS 10.13 or above. The auto-updater has also been updated from Sparkle 1.27.1 to 2.3.0 for 10.13+ builds. Legacy (10.9-10.12) builds are still using Sparkle 1. See #1332. New Vim features -------------------- New `smoothscroll` option allows you to scroll through a long wrapped line (using Ctrl-E or mouse wheel) without immediately jumping to the next line. (v9.0.0640) `splitscroll` option has been renamed `splitkeep`, with more flexibility than before. (v9.0.0647) Sound playback on macOS is now supported. You can use `has('sound')` to check. See `help sound` for details. (v9.0.0694) Terminals now support `:confirm` for `:q`, etc, which also means MacVim's Cmd-W will work properly for terminal windows. (v9.0.0710) Virtual text had numerous bugs fixed. General ==================== Legacy build for 10.9 - 10.12 -------------------- Per a previous announcement (#1271), the default MacVim binary will now require macOS 10.13 or above. Users of macOS 10.9 - 10.12 can use a separate "legacy" build which will still be supported. The legacy binary will still have the latest versions of Vim and be supported, but may not have all the latest features (e.g. pre-release builds). If you are using the auto-updater (Sparkle) to update MacVim, it should "just work" and find the best version for you. If you are downloading MacVim from the website, there is also a link to download the legacy version marked for 10.9+ as well. If you download the normal binary marked for 10.13+ from the website, it won't work on these older macOS versions. See #1331. Fixes ==================== CoreText Renderer clipping and rendering bugs -------------------- Unicode characters with multiple composing characters (e.g. "x⃗") will now render correctly. #1172 Texts (e.g. Tibetan, Zalgo texts) that are taller than the line height will no longer be clipped inappropriately. You can use a new setting `MMRendererClipToRow` to re-enable clipping if the tall texts are distracting. #995 / #1356 Tab crash -------------------- Fixed a crash when opening new tabs that seems to only occur in macOS 13 Ventura. #1333 Other bugs -------------------- - Fixed non-native full screen not working well with the notch on newer MacBook's when set to not show menu bar. You can also use `MMNonNativeFullScreenSafeAreaBehavior` to force MacVim to use the notch area as well if you don't mind some content being obscured. Note that the previous release also claimed it fixed this, but because the binary was built against an old macOS SDK (Big Sur), the fix did not work in the binary release. #1261 - Allow "Open untitled window: never" and "After last window closes: Quit MacVim" to be set together again. Added safeguards to make sure doing so won't immediately close the app. #1338 - Edit.Cut / Copy menu items will now be properly disabled when there isn't selected text. #1308 - Fixed potential `:emenu` crash when the menu is associated with an action in a non-valid mode. #1305 - Fixed bug where just bringing up the right-click (or the MacVim→Services) menu would somehow copy the selected texts to the system clipboard. #1300 - Fixed a Japanese input method bug where using left/right arrow to move to a different section of the input text would previously result in the candidate list not showing up at the correct position. #1312 - Fix non-CoreText renderer not handling text styles like strikethrough correctly (note: this renderer has been deprecated for a while and you should not use it). #1296 - This release uses an older sh/bash syntax file because the latest one in Vim has a bug. #1358 Misc ==================== New settings: - "No drop shadows" (Appearance). #1301 - "Treat Ctrl-click as right-click" (Input) (#1326). This was previously configurable via command-line, but now also possible in the settings pane under the new "Input" category. "About MacVim" now reports the version number in a clearer way with clearly specified release number vs Vim version. Known Issues ==================== Printing -------------------- Printing using File→Print or `:hardcopy` is currently not working under macOS 13 Ventura due to its removal of PostScript support in the Preview app. This will be fixed in a later release. See the issue for workarounds. #1347 Scripting ==================== - Scripting languages versions: - Perl is now built against 5.30, up from 5.18. - Ruby is now built against 3.2, up from 3.1. Compatibility ==================== Requires macOS 10.9 or above. (10.9 - 10.12 requires downloading a separate legacy build) Script interfaces have compatibility with these versions: - Lua 5.4 - Perl 5.30 - Python2 2.7 - Python3 3.10 - Ruby 3.2
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This fixes a couple outstanding CoreText rendering issues with more complicated characters. Separated them into separate commits to make the changes easier to identify.
Rename the "preserve line spacing" setting to "use macOS calculation"
The setting was added to "preserve" the font's original line spacing intents, but after further investigation I cannot find why Apple's calculation for font line spacing to be so wide. Furthermore it's quite odd because the default line spacing is wide, but the only thing that setting does is by re-initializing the font under Core Text (instead of using NSFont like the default) using CTFont and somehow the issue is fixed. Inspecting font metrics (using ttx) also didn't seem to give any hints why the spacing are so wide (e.g. ascent / descent / line gap etc all look fine). A StackOverflow comment seems to suggest that Apple is simply adding a 1.2x scale to some fonts (https://stackoverflow.com/questions/5511830/how-does-line-spacing-work-in-core-text-and-why-is-it-different-from-nslayoutm) which seems to match the observation. My guess is that Apple looks at some fonts and think they are too aggressive in their font metrics design and "helpfully" introduces a 1.2x multiplier to space them out, where Core Text is lower level than AppKit and therefore does not have this auto-inflation.
By renaming the option it should make it clearer that this is a somewhat arbitrary distinction instead of it being an inherent property of the font.
Remove the ceil() call around fontDescent in Core Text renderer
I have looked and I do not believe there is a good reason to do this rounding up at all. I believe the motivation is to align the baseline with the pixel boundary, but I don't believe that is necessary, given that Core Text will properly perform the correct antialiasing for us. Most texts aren't directly aligned at the baseline anyway. Before, that ceil() option is a reason why sometimes fonts would feel pushed upwards, clipping emojis to the top, and creating a lopsided effect for fonts like Fira Code (h11), since we draw the boxes aligned to the line height at the bottom of the descender, meaning the ceil() has an effect of pushing the text up.
Fix composing characters renderered at an offset
MacVim previously didn't really render composing characters with multiple glyphs correctly. For simple ones like 'â' it would work fine because Core Text just generates one glyph for it, but for more complicated ones like 'x⃗' the renderer didn't properly query the advance of the glyphs to put them at the correct position. Refactor the logic to keep track of the current cell/glyphs and make sure to track the advances fo the glyphs so the composing chars' glyphs would be laid out properly on top of the cell.
We need to be careful with the tracking, because in Vim we force the text rendering to be monospaced, and so we maintain the tracking within a single grapheme (which can consist of multiple glyphs), but when we move to the next grapheme we reset the position so we can get proper monospaced rendering even for non-monospaced texts like CJK or stylized texts.
Fix #995
Fix #1172
Fix CoreText clipping issues with tall texts
This fixes the issue where particularly tall characters will get clipped at the row boundary. This happens because even though a font describes the line height with font metrics, individual glyphs do not have to respect them, and we can see with emoji rendering sometimes they can poke upwards past the line height. Also, it's trivially easy to construct composing characters that become really tall, e.g. "x゙̂⃗", or Tibetan scripts like "ཧཱུྃ".
To fix this, we do the following:
Remove the explicit clipping call at rendering.
Fix partial redraw to not lead to clipping / corruption. This is quite tricky, because let's say we have a character that is tall enough to touch other lines, if those lines are redraw but not the line with the tall char, the redraw will paint over the parts of the glyphs poking through. Alternatively if we redraw the line with the tall chars we also need to expand the redraw region to make sure other lines get repainted as well. To fix this properly, we should do a proper glyph calculation when we receive the draw command before we issue before we call
setNeedsDisplayInRect
, but since right now we only extract glyph info later (during drawRect call), it's too late. We just do the hacky solution by storing a variableredrawExpandRows
that tracks how many lines to expand for all lines. It's a little hacky since this will affect all lines permanently regardless if they have tall characters or not. The proper fix may come later as an optimization (or when we do hardware rendering via Metal).Re-order the rendering so we have a two pass system, where we first draw the background fill color for all rows, then the text. This helps prevent things like Vim's window split or cursorline from obscuring the text.
Add a preference to turn on clipping (old behavior). This helps prevent odd issues with really tall texts (e.g. Zalgo text) making it hard to see what's going on. The preference
MMRendererClipToRow
is not exposed in UI for now as it's a relatively niche. It will be exposed later when we have a dedicated render tab in settings.Note that a lot of these characters only show their full height by doing
set maxcombine=8
because the default (2) is quite low.Part of the fix for #995