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

“White on Black” VF Rasterization Issue #8

Open
readroberts opened this issue Aug 10, 2019 · 1 comment
Open

“White on Black” VF Rasterization Issue #8

readroberts opened this issue Aug 10, 2019 · 1 comment

Comments

@readroberts
Copy link

“White on Black” VF Rasterization Issue

This issue was initially brought up for discussion during an OpenType meeting that took place on 2019-06-12, but was deferred for further consideration. Adobe would like to move forward on this important issue.

Background

Overlapping white paths on a filled (aka black) background result in black “fill” in the intersections of the white paths. This applies to both TrueType and CFF2 outlines. This is a particular problem in variable fonts, as overlap removal, which is used to avoid such problems in other formats, often cannot be used. Overlap removal can result in incompatible topology of the glyphs for the masters. It can also result in short line segments and sharp angles that generally do not interpolate well. The “White On Black VF” variable font demonstrates this issue: https://v-fonts.com/fonts/white-on-black-vf

Any solution also has to support exporting the glyph paths as a graphics object, such as SVG. Otherwise, the same fill problem will occur.

The open source Source Han (3) and Noto CJK (2) Pan-CJK typeface families, which are broadly distributed, include glyphs for 80 such characters that are in Unicode as explicit “white on black” characters: U+24EB ⓫ through U+24F4 ⓴, U+24FF ⓿, U+2776 ❶ through U+277F ❿, U+1F10C 🄌, U+1F150 🅐 through U+1F169 🅩, and U+1F170 🅰 through U+1F18F 🆏. The Adobe-Japan1-7 glyph set, which serves as the basis for hundreds of Japanese fonts, includes 807 such glyphs, almost all of which are in Supplement 4: CIDs 8286–8294 (9 glyphs; Supplement 0), 10503–10762 (260 glyphs; Supplement 4), 11037–11305 (269 glyphs; Supplement 4), and 11576–11844 (269 glyphs; Supplement 4). Some of these glyphs, of course, will not exhibit this issue, because their “white” glyphs are simple, such as those for U+1F158 🅘 and U+1F178 🅸. Some glyphs, such as those for U+2779 ❹, U+277D ❽, U+1F150 🅐, U+1F160 🅠, U+1F167 🅧, U+1F170 🅰, U+1F180 🆀, and U+1F187 🆇, might be designed in such a way that their subpaths overlap or self-overlap. Of course, it is possible to design such glyphs with overlapping subpaths removed, but it is not ideal. Furthermore, the “white on black” glyphs in Adobe-Japan1-7 include many that are for hiragana, katakana, and a small number of kanji (aka ideographs). Designing these glyphs, especially across a rich weight range, in such a way that overlap removal results in glyphs with identical topologies generally means that the design needs to be compromised, particularly for the heavy master. In other words, this is a real-world problem that needs to be solved, not worked around.

There are undoubtedly many other glyphs that have this problem that are yet to be found in shipping fonts that may be converted to variable fonts. It has become increasingly common to design glyphs with overlapping elements, since this greatly eases editing and revision, and also has better interpolation results. It is useful to not have to do overlap removal when these sources are converted to variable fonts, as the original elements often interpolate better than the same data after overlap removal.

Suggested guidelines

We cannot expect environments that use paths exported as graphics to support new logic or data types. I had originally thought this might be possible, but others have pointed out that graphics objects can end up in a number of well supported formats, such as SVG, and the number of consumers is very large.

We should not require that glyphs with this issue should require the addition of additional glyphs to express glyph components. Shipping standard fonts are already at the 64K glyph limit.

The solution is something we can roll out fairly quickly. Variable fonts workflows are still new and small in number; it is better to get changes worked out soon.

Solutions

During the June meeting, three possible solutions were discussed:

  1. Rasterizer change, with heuristics to define layers in exported glyphs.

Read Roberts suggested the idea of solving the rasterization issue by adding a new fill rule, which would fill on only a positive winding number, rather than a non-zero winding number. The advantage of this is that it is a very simple and safe change. The Adobe core rasterizers and the Freetype rasterizer already count the winding order, and allow specifying a fill rule. Changing the fill rule simply means adding another if-else clause to the code. Of course, a larger change is required to read a specification of which glyphs require the positive fill rule, and to pass this down through the API’s to the rasterizer.

Exporting the glyph as a graphic object would need to be handled by exporting it with layers. Simple heuristics can take care of this. Extract contours into separate layers. Successive contours with the same orientation can be combined. Successive filled paths would be combined in a single layer. Successive ‘white’ paths would be added to a mask or clipping path for the prior layer with filled paths.

We’ll need to identify the glyphs that are affected, in a place appropriate for both TrueType and CFF2. The ‘GDEF’ table is what we propose:

GDEF.minorVersion = 4

This header adds a new field:
Offset32 whiteOnBlackGlyphsOffset Offset to White on Black Glyphs Table, from beginning of GDEF header (may be NULL)

White on Black Glyphs Table:
uint16 format Format. Set to 1.
Offset32 coverageOffset offset to Coverage table, from beginning of White on Black Glyphs Table, indicating glyphs to be specially treated as containing “white” on “black” elements

  1. Extend the ‘COLR’ table

The ‘COLR’ table offers the facility of defining a base glyph, and accompanying layers that are each represented by an additional glyph. The base glyph is used by workflows that are not aware of the ‘COLR’ table, and to provide the GID used by other tables that need to reference the glyph , such as the cmap, layout, and metrics tables. Each layer glyph is associated with a color and alpha channel. This approach would solve the problem by representing the background black figure with one layer glyph, and the ‘white’ components with one or more additional layer glyphs. This would require a bit of information to identify the use case, but there are ways to do this without adding any new fields or structures. , as the ‘white’ contours need to be used as a mask, rather than as a colored graphics object. This will require a new flag to be associated with the glyph.

This would require a change to the rasterization logic, as instead of each layer overwriting the previous layer, the ‘white’ layers would need to be used as a mask. Some change would also be needed to the logic to export the layers: the ‘white’ layer would need to be defined as a mask or re-built as a clipping path.

  1. Implement a new table for complex graphics operations.

This was proposed by Renzhi Li, who suggested that it would be useful for associating actual graphic operations with glyphs.

Adobe’s preference

Although all of the proposed solutions could technically work for most fonts, Adobe has a strong preference for Solution #1. This solution does not require adding any additional glyphs to the font, which would otherwise be a non-starter for typefaces whose glyph sets are already full (such as Source Han and Noto CJK). Fixing what can be described as a fundamental rasterization issue in the rasterizer logic is very simple, and hinting and anti-aliasing logic would not be affected. Exporting glyphs will require new logic, but obviously not more than would be required for the other solutions.

Solution #3, which involves a new table for complex graphics operations, is not well defined, and is likely to have an extremely large implementation cost with very little practical benefits.

Solution #2, which involves extending the ‘COLR’ table, may look like a natural fit at first glance: it is well supported in many workflows, and is intended to support glyph layers. However, this solution would require at least two additional layers per problematic base glyph. Such an expansion of the glyph set is not a problem for color fonts, which typically do not have very large glyphs sets, but is a complete non-starter for glyph sets of shipping fonts that are already at the 64K limit (aka full), such as Source Han and Noto CJK. There are ways to work around this by extending the ‘COLR’ table format, such as to add the layer glyph data as records in the ‘COLR’ table, or to use the layer glyph GID indices as indices to contours in the base glyph, but such solutions are inelegant compared to Solution #1.

The problem is really one of rasterization for normal text fonts. Since the ‘COLR’ table conveys the meaning of being a color font, it doesn’t seem to be the right place to be solving this problem, which is fundamentally a non-color one. Also, this could lead to problems with heuristics, such as font classification.

There may also be some loss of effectiveness of anti-aliasing and hinting. Text renderers have been heavily optimized over the years for different environments. We suspect that some of this optimization could be lost when the ‘white’ paths are applied as a mask.

Use of the ‘COLR’ table currently requires explicit opt-in on the part of the user, which is passed down through the API. This new use of the ‘COLR’ table would need to differentiate between the current use case, which is opt-in only, and the new use case, which involves the ‘COLR’ table being applied to the glyphs in question.

@readroberts
Copy link
Author

readroberts commented Oct 15, 2019

Since there have been no objections to this proposal, I will advance to specific recommended changes to the OpenType Specification. Please see:

White-On-Black OpenType Specification Changes

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

1 participant