• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/ports/SkTypeface_fontations.h"
8 
9 #include "include/codec/SkCodec.h"
10 #include "include/codec/SkPngDecoder.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkFontMetrics.h"
15 #include "include/core/SkImage.h"
16 #include "include/core/SkPictureRecorder.h"
17 #include "include/core/SkStream.h"
18 #include "include/effects/SkGradientShader.h"
19 #include "include/pathops/SkPathOps.h"
20 #include "include/private/base/SkMutex.h"
21 #include "src/base/SkScopeExit.h"
22 #include "src/core/SkFontDescriptor.h"
23 #include "src/core/SkFontPriv.h"
24 #include "src/ports/SkTypeface_fontations_priv.h"
25 #include "src/ports/fontations/src/skpath_bridge.h"
26 
27 namespace {
28 
29 [[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false;
30 
streamToData(const std::unique_ptr<SkStreamAsset> & font_data)31 sk_sp<SkData> streamToData(const std::unique_ptr<SkStreamAsset>& font_data) {
32     if (!font_data) {
33         return SkData::MakeEmpty();
34     }
35     // TODO(drott): Remove this once SkData::MakeFromStream is able to do this itself.
36     if (font_data->getData()) {
37         return font_data->getData();
38     }
39     if (font_data->getMemoryBase() && font_data->getLength()) {
40         return SkData::MakeWithCopy(font_data->getMemoryBase(), font_data->getLength());
41     }
42     font_data->rewind();
43     return SkData::MakeFromStream(font_data.get(), font_data->getLength());
44 }
45 
make_bridge_font_ref(sk_sp<SkData> fontData,uint32_t index)46 rust::Box<::fontations_ffi::BridgeFontRef> make_bridge_font_ref(sk_sp<SkData> fontData,
47                                                                 uint32_t index) {
48     rust::Slice<const uint8_t> slice{fontData->bytes(), fontData->size()};
49     return fontations_ffi::make_font_ref(slice, index);
50 }
51 
52 static_assert(sizeof(fontations_ffi::SkiaDesignCoordinate) ==
53                       sizeof(SkFontArguments::VariationPosition::Coordinate) &&
54               sizeof(fontations_ffi::SkiaDesignCoordinate::axis) ==
55                       sizeof(SkFontArguments::VariationPosition::Coordinate::axis) &&
56               sizeof(fontations_ffi::SkiaDesignCoordinate::value) ==
57                       sizeof(SkFontArguments::VariationPosition::Coordinate::value) &&
58               offsetof(fontations_ffi::SkiaDesignCoordinate, axis) ==
59                       offsetof(SkFontArguments::VariationPosition::Coordinate, axis) &&
60               offsetof(fontations_ffi::SkiaDesignCoordinate, value) ==
61                       offsetof(SkFontArguments::VariationPosition::Coordinate, value) &&
62               "Struct fontations_ffi::SkiaDesignCoordinate must match "
63               "SkFontArguments::VariationPosition::Coordinate.");
64 
make_normalized_coords(fontations_ffi::BridgeFontRef const & bridgeFontRef,const SkFontArguments::VariationPosition & variationPosition)65 rust::Box<fontations_ffi::BridgeNormalizedCoords> make_normalized_coords(
66         fontations_ffi::BridgeFontRef const& bridgeFontRef,
67         const SkFontArguments::VariationPosition& variationPosition) {
68     // Cast is safe because of static_assert matching the structs above.
69     rust::Slice<const fontations_ffi::SkiaDesignCoordinate> coordinates(
70             reinterpret_cast<const fontations_ffi::SkiaDesignCoordinate*>(
71                     variationPosition.coordinates),
72             variationPosition.coordinateCount);
73     return resolve_into_normalized_coords(bridgeFontRef, coordinates);
74 }
75 
SkMatrixFromFontationsTransform(const fontations_ffi::Transform & transformArg)76 SkMatrix SkMatrixFromFontationsTransform(const fontations_ffi::Transform& transformArg) {
77     return SkMatrix::MakeAll(transformArg.xx,
78                              -transformArg.xy,
79                              transformArg.dx,
80                              -transformArg.yx,
81                              transformArg.yy,
82                              -transformArg.dy,
83                              0.f,
84                              0.f,
85                              1.0f);
86 }
87 
isLCD(const SkScalerContextRec & rec)88 bool isLCD(const SkScalerContextRec& rec) { return SkMask::kLCD16_Format == rec.fMaskFormat; }
89 
bothZero(SkScalar a,SkScalar b)90 bool bothZero(SkScalar a, SkScalar b) { return 0 == a && 0 == b; }
91 
isAxisAligned(const SkScalerContextRec & rec)92 bool isAxisAligned(const SkScalerContextRec& rec) {
93     return 0 == rec.fPreSkewX && (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
94                                   bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
95 }
96 
97 }  // namespace
98 
SkTypeface_Make_Fontations(std::unique_ptr<SkStreamAsset> fontData,const SkFontArguments & args)99 sk_sp<SkTypeface> SkTypeface_Make_Fontations(std::unique_ptr<SkStreamAsset> fontData,
100                                              const SkFontArguments& args) {
101     return SkTypeface_Fontations::MakeFromStream(std::move(fontData), args);
102 }
103 
SkTypeface_Make_Fontations(sk_sp<SkData> fontData,const SkFontArguments & args)104 sk_sp<SkTypeface> SkTypeface_Make_Fontations(sk_sp<SkData> fontData,
105                                              const SkFontArguments& args) {
106     return SkTypeface_Fontations::MakeFromData(std::move(fontData), args);
107 }
108 
SkTypeface_Fontations(sk_sp<SkData> fontData,const SkFontStyle & style,uint32_t ttcIndex,rust::Box<fontations_ffi::BridgeFontRef> && fontRef,rust::Box<fontations_ffi::BridgeMappingIndex> && mappingIndex,rust::Box<fontations_ffi::BridgeNormalizedCoords> && normalizedCoords,rust::Box<fontations_ffi::BridgeOutlineCollection> && outlines,rust::Vec<uint32_t> && palette)109 SkTypeface_Fontations::SkTypeface_Fontations(
110         sk_sp<SkData> fontData,
111         const SkFontStyle& style,
112         uint32_t ttcIndex,
113         rust::Box<fontations_ffi::BridgeFontRef>&& fontRef,
114         rust::Box<fontations_ffi::BridgeMappingIndex>&& mappingIndex,
115         rust::Box<fontations_ffi::BridgeNormalizedCoords>&& normalizedCoords,
116         rust::Box<fontations_ffi::BridgeOutlineCollection>&& outlines,
117         rust::Vec<uint32_t>&& palette)
118         : SkTypeface(style, true)
119         , fFontData(std::move(fontData))
120         , fTtcIndex(ttcIndex)
121         , fBridgeFontRef(std::move(fontRef))
122         , fMappingIndex(std::move(mappingIndex))
123         , fBridgeNormalizedCoords(std::move(normalizedCoords))
124         , fOutlines(std::move(outlines))
125         , fPalette(std::move(palette)) {}
126 
MakeFromStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args)127 sk_sp<SkTypeface> SkTypeface_Fontations::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
128                                                         const SkFontArguments& args) {
129     return MakeFromData(streamToData(stream), args);
130 }
131 
MakeFromData(sk_sp<SkData> data,const SkFontArguments & args)132 sk_sp<SkTypeface> SkTypeface_Fontations::MakeFromData(sk_sp<SkData> data,
133                                                       const SkFontArguments& args) {
134     uint32_t ttcIndex = args.getCollectionIndex() & 0xFFFF;
135     rust::Box<fontations_ffi::BridgeFontRef> bridgeFontRef = make_bridge_font_ref(data, ttcIndex);
136     if (!fontations_ffi::font_ref_is_valid(*bridgeFontRef)) {
137         return nullptr;
138     }
139 
140     rust::Box<fontations_ffi::BridgeMappingIndex> mappingIndex =
141             fontations_ffi::make_mapping_index(*bridgeFontRef);
142 
143     SkFontArguments::VariationPosition variationPosition = args.getVariationDesignPosition();
144     std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> concatenatedCoords = nullptr;
145     // Handle FreeType behaviour of upper 15 bits of collection index
146     // representing a named instance choice. If so, prepopulate the variation
147     // coordinates with the values from the named instance and append the user
148     // coordinates after that so they can override the named instance's
149     // coordinates.
150     if (args.getCollectionIndex() & 0xFFFF0000) {
151         size_t numNamedInstanceCoords =
152                 fontations_ffi::coordinates_for_shifted_named_instance_index(
153                         *bridgeFontRef,
154                         args.getCollectionIndex(),
155                         rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate>());
156         concatenatedCoords.reset(
157                 new SkFontArguments::VariationPosition::Coordinate
158                         [numNamedInstanceCoords + variationPosition.coordinateCount]);
159 
160         rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate> targetSlice(
161                 reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(concatenatedCoords.get()),
162                 numNamedInstanceCoords);
163         size_t retrievedNamedInstanceCoords =
164                 fontations_ffi::coordinates_for_shifted_named_instance_index(
165                         *bridgeFontRef, args.getCollectionIndex(), targetSlice);
166         if (numNamedInstanceCoords != retrievedNamedInstanceCoords) {
167             return nullptr;
168         }
169         for (int i = 0; i < variationPosition.coordinateCount; ++i) {
170             concatenatedCoords[numNamedInstanceCoords + i] = variationPosition.coordinates[i];
171         }
172         variationPosition.coordinateCount += numNamedInstanceCoords;
173         variationPosition.coordinates = concatenatedCoords.get();
174     }
175 
176     rust::Box<fontations_ffi::BridgeNormalizedCoords> normalizedCoords =
177             make_normalized_coords(*bridgeFontRef, variationPosition);
178     SkFontStyle style;
179     fontations_ffi::BridgeFontStyle fontStyle;
180     if (fontations_ffi::get_font_style(*bridgeFontRef, *normalizedCoords, fontStyle)) {
181         style = SkFontStyle(fontStyle.weight,
182                             fontStyle.width,
183                             static_cast<SkFontStyle::Slant>(fontStyle.slant));
184     }
185     rust::Box<fontations_ffi::BridgeOutlineCollection> outlines =
186             fontations_ffi::get_outline_collection(*bridgeFontRef);
187 
188     rust::Slice<const fontations_ffi::PaletteOverride> paletteOverrides(
189             reinterpret_cast<const ::fontations_ffi::PaletteOverride*>(args.getPalette().overrides),
190             args.getPalette().overrideCount);
191     rust::Vec<uint32_t> palette =
192             resolve_palette(*bridgeFontRef, args.getPalette().index, paletteOverrides);
193 
194     return sk_sp<SkTypeface>(new SkTypeface_Fontations(data,
195                                                        style,
196                                                        ttcIndex,
197                                                        std::move(bridgeFontRef),
198                                                        std::move(mappingIndex),
199                                                        std::move(normalizedCoords),
200                                                        std::move(outlines),
201                                                        std::move(palette)));
202 }
203 
204 namespace sk_fontations {
205 
AxisWrapper(SkFontParameters::Variation::Axis axisArray[],size_t axisCount)206 AxisWrapper::AxisWrapper(SkFontParameters::Variation::Axis axisArray[], size_t axisCount)
207         : fAxisArray(axisArray), fAxisCount(axisCount) {}
208 
populate_axis(size_t i,uint32_t axisTag,float min,float def,float max,bool hidden)209 bool AxisWrapper::populate_axis(
210         size_t i, uint32_t axisTag, float min, float def, float max, bool hidden) {
211     if (i >= fAxisCount) {
212         return false;
213     }
214     SkFontParameters::Variation::Axis& axis = fAxisArray[i];
215     axis.tag = axisTag;
216     axis.min = min;
217     axis.def = def;
218     axis.max = max;
219     axis.setHidden(hidden);
220     return true;
221 }
222 
size() const223 size_t AxisWrapper::size() const { return fAxisCount; }
224 
225 }  // namespace sk_fontations
226 
onGetUPEM() const227 int SkTypeface_Fontations::onGetUPEM() const {
228     return fontations_ffi::units_per_em_or_zero(*fBridgeFontRef);
229 }
230 
onGetFamilyName(SkString * familyName) const231 void SkTypeface_Fontations::onGetFamilyName(SkString* familyName) const {
232     rust::String readFamilyName = fontations_ffi::family_name(*fBridgeFontRef);
233     *familyName = SkString(readFamilyName.data(), readFamilyName.size());
234 }
235 
onGetPostScriptName(SkString * postscriptName) const236 bool SkTypeface_Fontations::onGetPostScriptName(SkString* postscriptName) const {
237     rust::String readPsName;
238     if (fontations_ffi::postscript_name(*fBridgeFontRef, readPsName)) {
239         if (postscriptName) {
240             *postscriptName = SkString(readPsName.data(), readPsName.size());
241         }
242         return true;
243     }
244 
245     return false;
246 }
247 
onGlyphMaskNeedsCurrentColor() const248 bool SkTypeface_Fontations::onGlyphMaskNeedsCurrentColor() const {
249     fGlyphMasksMayNeedCurrentColorOnce([this] {
250         static constexpr SkFourByteTag COLRTag = SkSetFourByteTag('C', 'O', 'L', 'R');
251         fGlyphMasksMayNeedCurrentColor = this->getTableSize(COLRTag) > 0;
252     });
253     return fGlyphMasksMayNeedCurrentColor;
254 }
255 
onCharsToGlyphs(const SkUnichar * chars,int count,SkGlyphID glyphs[]) const256 void SkTypeface_Fontations::onCharsToGlyphs(const SkUnichar* chars,
257                                             int count,
258                                             SkGlyphID glyphs[]) const {
259     size_t realCount = SkToSizeT(count);
260     rust::Slice<const uint32_t> codepointSlice{reinterpret_cast<const uint32_t*>(chars), realCount};
261     rust::Slice<uint16_t> glyphSlice{reinterpret_cast<uint16_t*>(glyphs), realCount};
262     fontations_ffi::lookup_glyph_or_zero(*fBridgeFontRef, *fMappingIndex,
263                                          codepointSlice, glyphSlice);
264 }
onCountGlyphs() const265 int SkTypeface_Fontations::onCountGlyphs() const {
266     return fontations_ffi::num_glyphs(*fBridgeFontRef);
267 }
268 
getGlyphToUnicodeMap(SkUnichar * codepointForGlyphMap) const269 void SkTypeface_Fontations::getGlyphToUnicodeMap(SkUnichar* codepointForGlyphMap) const {
270     size_t numGlyphs = SkToSizeT(onCountGlyphs());
271     if (!codepointForGlyphMap) {
272         SkASSERT(numGlyphs == 0);
273     }
274     rust::Slice<uint32_t> codepointForGlyphSlice{reinterpret_cast<uint32_t*>(codepointForGlyphMap),
275                                                  numGlyphs};
276     fontations_ffi::fill_glyph_to_unicode_map(*fBridgeFontRef, codepointForGlyphSlice);
277 }
278 
onFilterRec(SkScalerContextRec * rec) const279 void SkTypeface_Fontations::onFilterRec(SkScalerContextRec* rec) const {
280     rec->useStrokeForFakeBold();
281 
282     // Opportunistic hinting downgrades copied from SkFontHost_FreeType.cpp
283     SkFontHinting h = rec->getHinting();
284     if (SkFontHinting::kFull == h && !isLCD(*rec)) {
285         // Collapse full->normal hinting if we're not doing LCD.
286         h = SkFontHinting::kNormal;
287     }
288 
289     // Rotated text looks bad with hinting, so we disable it as needed.
290     if (!isAxisAligned(*rec)) {
291         h = SkFontHinting::kNone;
292     }
293     rec->setHinting(h);
294 }
295 
296 class SkrifaLocalizedStrings : public SkTypeface::LocalizedStrings {
297 public:
SkrifaLocalizedStrings(rust::Box<::fontations_ffi::BridgeLocalizedStrings> bridge_localized_strings)298     SkrifaLocalizedStrings(
299             rust::Box<::fontations_ffi::BridgeLocalizedStrings> bridge_localized_strings)
300             : fBridgeLocalizedStrings(std::move(bridge_localized_strings)) {}
next(SkTypeface::LocalizedString * localized_string)301     bool next(SkTypeface::LocalizedString* localized_string) override {
302         fontations_ffi::BridgeLocalizedName localizedName;
303         if (!fontations_ffi::localized_name_next(*fBridgeLocalizedStrings, localizedName)) {
304             return false;
305         }
306         localized_string->fString =
307                 SkString(localizedName.string.data(), localizedName.string.size());
308         localized_string->fLanguage =
309                 SkString(localizedName.language.data(), localizedName.language.size());
310         return true;
311     }
312 
313 private:
314     rust::Box<::fontations_ffi::BridgeLocalizedStrings> fBridgeLocalizedStrings;
315 };
316 
onCreateFamilyNameIterator() const317 SkTypeface::LocalizedStrings* SkTypeface_Fontations::onCreateFamilyNameIterator() const {
318     return new SkrifaLocalizedStrings(fontations_ffi::get_localized_strings(*fBridgeFontRef));
319 }
320 
321 class SkFontationsScalerContext : public SkScalerContext {
322 public:
SkFontationsScalerContext(const SkTypeface_Fontations & realTypeface,const SkScalerContextEffects & effects,const SkDescriptor * desc,SkTypeface & proxyTypeface)323     SkFontationsScalerContext(const SkTypeface_Fontations& realTypeface,
324                               const SkScalerContextEffects& effects,
325                               const SkDescriptor* desc,
326                               SkTypeface& proxyTypeface)
327             : SkScalerContext(proxyTypeface, effects, desc) // proxyTypeface owns the realTypeface
328             , fBridgeFontRef(realTypeface.getBridgeFontRef())
329             , fBridgeNormalizedCoords(realTypeface.getBridgeNormalizedCoords())
330             , fOutlines(realTypeface.getOutlines())
331             , fMappingIndex(realTypeface.getMappingIndex())
332             , fPalette(realTypeface.getPalette())
333             , fHintingInstance(fontations_ffi::no_hinting_instance()) {
334         fRec.computeMatrices(
335                 SkScalerContextRec::PreMatrixScale::kVertical, &fScale, &fRemainingMatrix);
336 
337         fDoLinearMetrics = this->isLinearMetrics();
338         // See below for the exception for SkFontHinting::kSlight.
339         fontations_ffi::AutoHintingControl autoHintingControl =
340                 SkToBool(fRec.fFlags & kForceAutohinting_Flag)
341                         ? fontations_ffi::AutoHintingControl::ForceForGlyfAndCff
342                         : fontations_ffi::AutoHintingControl::AutoAsFallback;
343 
344         // Hinting-reliant fonts exist that display incorrect contours when not executing their
345         // hinting instructions. Detect those and force-enable hinting for them.
346         if (fontations_ffi::hinting_reliant(fOutlines)) {
347             fHintingInstance = fontations_ffi::make_mono_hinting_instance(
348                     fOutlines, fScale.y(), fBridgeNormalizedCoords);
349             fDoLinearMetrics = false;
350         } else {
351             if (SkMask::kBW_Format == fRec.fMaskFormat) {
352                 if (fRec.getHinting() == SkFontHinting::kNone) {
353                     fHintingInstance = fontations_ffi::no_hinting_instance();
354                     fDoLinearMetrics = true;
355                 } else {
356                     fHintingInstance = fontations_ffi::make_mono_hinting_instance(
357                             fOutlines, fScale.y(), fBridgeNormalizedCoords);
358                     fDoLinearMetrics = false;
359                 }
360             } else {
361                 switch (fRec.getHinting()) {
362                     case SkFontHinting::kNone:
363                         fHintingInstance = fontations_ffi::no_hinting_instance();
364                         fDoLinearMetrics = true;
365                         break;
366                     case SkFontHinting::kSlight:
367                         // Unhinted metrics.
368                         if (autoHintingControl !=
369                             fontations_ffi::AutoHintingControl::ForceForGlyfAndCff) {
370                             autoHintingControl =
371                                     fontations_ffi::AutoHintingControl::PreferAutoOverHintsForGlyf;
372                         }
373                         fHintingInstance = fontations_ffi::make_hinting_instance(
374                                 fOutlines,
375                                 fScale.y(),
376                                 fBridgeNormalizedCoords,
377                                 true /* do_light_hinting */,
378                                 false /* do_lcd_antialiasing */,
379                                 false /* lcd_orientation_vertical */,
380                                 autoHintingControl);
381                         fDoLinearMetrics = true;
382                         break;
383                     case SkFontHinting::kNormal:
384                         // No hinting to subpixel coordinates.
385                         fHintingInstance = fontations_ffi::make_hinting_instance(
386                                 fOutlines,
387                                 fScale.y(),
388                                 fBridgeNormalizedCoords,
389                                 false /* do_light_hinting */,
390                                 false /* do_lcd_antialiasing */,
391                                 false /* lcd_orientation_vertical */,
392                                 autoHintingControl);
393                         break;
394                     case SkFontHinting::kFull:
395                         // Attempt to make use of hinting to subpixel coordinates.
396                         fHintingInstance = fontations_ffi::make_hinting_instance(
397                                 fOutlines,
398                                 fScale.y(),
399                                 fBridgeNormalizedCoords,
400                                 false /* do_light_hinting */,
401                                 isLCD(fRec) /* do_lcd_antialiasing */,
402                                 SkToBool(fRec.fFlags &
403                                          SkScalerContext::
404                                                  kLCD_Vertical_Flag) /* lcd_orientation_vertical */,
405                                 autoHintingControl);
406                 }
407             }
408         }
409     }
410 
getContourHeightForLetter(SkUnichar letter,SkScalar & height)411     bool getContourHeightForLetter(SkUnichar letter, SkScalar& height) {
412         SkGlyphID glyphId;
413         rust::Slice<const uint32_t> codepointSlice{reinterpret_cast<const uint32_t*>(&letter), 1};
414         rust::Slice<uint16_t> glyphSlice{reinterpret_cast<uint16_t*>(&glyphId), 1};
415         fontations_ffi::lookup_glyph_or_zero(fBridgeFontRef, fMappingIndex,
416                                              codepointSlice, glyphSlice);
417         if (!glyphId) {
418             return false;
419         }
420         SkPath glyphPath;
421         if (!generateYScalePathForGlyphId(glyphId, &glyphPath, fScale.y(), *fHintingInstance)) {
422             return false;
423         }
424         height = glyphPath.getBounds().height();
425         return true;
426     }
427 
428     // yScale is only used if hintinInstance is set to Unhinted,
429     // otherwise the size is controlled by the configured hintingInstance.
430     // hintingInstance argument is needed as COLRv1 drawing performs unhinted,
431     // unscaled path retrieval.
generateYScalePathForGlyphId(uint16_t glyphId,SkPath * path,float yScale,const fontations_ffi::BridgeHintingInstance & hintingInstance)432     bool generateYScalePathForGlyphId(
433             uint16_t glyphId,
434             SkPath* path,
435             float yScale,
436             const fontations_ffi::BridgeHintingInstance& hintingInstance) {
437         fontations_ffi::BridgeScalerMetrics scalerMetrics;
438 
439         // See https://crbug.com/390889644 - while SkScalerContexts are single-thread
440         // in general, generateYScalePathForGlyphId() can be called from a COLRv1
441         // SkDrawable as well (see generateDrawable()). For this reason access to the
442         // path extraction array needs to be made thread-safe here.
443         SkAutoMutexExclusive l(fPathMutex);
444         // Keep allocations in check. The rust side pre-allocates a fixed amount,
445         // and afer leaving this function, we shrink to the same amount.
446         SK_AT_SCOPE_EXIT(fontations_ffi::shrink_verbs_points_if_needed(fPathVerbs, fPathPoints));
447 
448         if (!fontations_ffi::get_path_verbs_points(fOutlines,
449                                                    glyphId,
450                                                    yScale,
451                                                    fBridgeNormalizedCoords,
452                                                    hintingInstance,
453                                                    fPathVerbs,
454                                                    fPathPoints,
455                                                    scalerMetrics)) {
456             return false;
457         }
458         *path = SkPath::Make(reinterpret_cast<const SkPoint*>(fPathPoints.data()),
459                              fPathPoints.size(),
460                              fPathVerbs.data(),
461                              fPathVerbs.size(),
462                              nullptr,
463                              0,
464                              SkPathFillType::kWinding);
465 
466         // See https://issues.skia.org/345178242 for details:
467         // The FreeType backend performs a path simplification here based on the
468         // equivalent of what we have here as scalerMetrics.has_overlaps
469         // Since PathOps::Simplify fails or at times produces incorrect simplified
470         // contours, skip that step here.
471         return true;
472     }
473 
474 protected:
475     struct ScalerContextBits {
476         using value_type = uint16_t;
477         static const constexpr value_type PATH = 1;
478         static const constexpr value_type COLRv0 = 2;
479         static const constexpr value_type COLRv1 = 3;
480         static const constexpr value_type BITMAP = 4;
481     };
482 
generateMetrics(const SkGlyph & glyph,SkArenaAlloc *)483     GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
484         GlyphMetrics mx(glyph.maskFormat());
485 
486         bool has_colrv1_glyph = false;
487         bool has_colrv0_glyph = false;
488         bool has_bitmap_glyph = false;
489         if (fontations_ffi::has_any_color_table(fBridgeFontRef)) {
490             has_colrv1_glyph = fontations_ffi::has_colrv1_glyph(fBridgeFontRef, glyph.getGlyphID());
491             has_colrv0_glyph = fontations_ffi::has_colrv0_glyph(fBridgeFontRef, glyph.getGlyphID());
492             has_bitmap_glyph = fontations_ffi::has_bitmap_glyph(fBridgeFontRef, glyph.getGlyphID());
493         }
494 
495         // Local overrides for color fonts etc. may alter the request for linear metrics.
496         bool doLinearMetrics = fDoLinearMetrics;
497 
498         if (has_bitmap_glyph) {
499             // Bitmap advance metrics can originate from different strike sizes in the bitmap
500             // font and are thus not linearly scaling with font size.
501             doLinearMetrics = false;
502         }
503         if (has_colrv0_glyph || has_colrv1_glyph) {
504             // We prefer color vector glyphs, and hinting is disabled for those.
505             doLinearMetrics = true;
506         }
507 
508         float x_advance = 0.0f;
509         x_advance = fontations_ffi::unhinted_advance_width_or_zero(
510                 fBridgeFontRef, fScale.y(), fBridgeNormalizedCoords, glyph.getGlyphID());
511         if (!doLinearMetrics) {
512             float hinted_advance = 0;
513             fontations_ffi::scaler_hinted_advance_width(
514                     fOutlines, *fHintingInstance, glyph.getGlyphID(), hinted_advance);
515             // FreeType rounds the advance to full pixels when in hinting modes.
516             // Match FreeType and round here.
517             // See
518             // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afloader.c#L422
519             // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttgload.c#L823
520             hinted_advance = roundf(hinted_advance);
521             // TODO(drott): Remove this workaround for fontations returning 0
522             // for a space glyph without contours, compare
523             // https://github.com/googlefonts/fontations/issues/905
524             if (hinted_advance != x_advance && hinted_advance != 0) {
525                 x_advance = hinted_advance;
526             }
527         }
528         mx.advance = fRemainingMatrix.mapXY(x_advance, SkFloatToScalar(0.f));
529 
530         if (has_colrv1_glyph || has_colrv0_glyph) {
531             mx.extraBits = has_colrv1_glyph ? ScalerContextBits::COLRv1 : ScalerContextBits::COLRv0;
532             mx.maskFormat = SkMask::kARGB32_Format;
533             mx.neverRequestPath = true;
534 
535             fontations_ffi::ClipBox clipBox;
536             if (has_colrv1_glyph && fontations_ffi::get_colrv1_clip_box(fBridgeFontRef,
537                                                                         fBridgeNormalizedCoords,
538                                                                         glyph.getGlyphID(),
539                                                                         fScale.y(),
540                                                                         clipBox)) {
541                 // Flip y.
542                 SkRect boundsRect = SkRect::MakeLTRB(
543                         clipBox.x_min, -clipBox.y_max, clipBox.x_max, -clipBox.y_min);
544 
545                 if (!fRemainingMatrix.isIdentity()) {
546                     SkPath boundsPath = SkPath::Rect(boundsRect);
547                     boundsPath.transform(fRemainingMatrix);
548                     boundsRect = boundsPath.getBounds();
549                 }
550 
551                 boundsRect.roundOut(&mx.bounds);
552 
553             } else {
554                 uint16_t upem = fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
555                 if (upem == 0) {
556                     mx.bounds = SkRect::MakeEmpty();
557                 } else {
558                     SkMatrix fullTransform;
559                     fRec.getSingleMatrix(&fullTransform);
560                     fullTransform.preScale(1.f / upem, 1.f / upem);
561 
562                     sk_fontations::BoundsPainter boundsPainter(*this, fullTransform, upem);
563                     bool result = fontations_ffi::draw_colr_glyph(fBridgeFontRef,
564                                                                   fBridgeNormalizedCoords,
565                                                                   glyph.getGlyphID(),
566                                                                   boundsPainter);
567                     if (result) {
568                         boundsPainter.getBoundingBox().roundOut(&mx.bounds);
569                     } else {
570                         mx.bounds = SkRect::MakeEmpty();
571                     }
572                 }
573             }
574         } else if (has_bitmap_glyph) {
575             mx.maskFormat = SkMask::kARGB32_Format;
576             mx.neverRequestPath = true;
577             mx.extraBits = ScalerContextBits::BITMAP;
578 
579             rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
580                     fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), fScale.y());
581             rust::cxxbridge1::Slice<const uint8_t> png_data =
582                     fontations_ffi::png_data(*bitmap_glyph);
583 
584             if (png_data.empty()) {
585                 return mx;
586             }
587 
588             const fontations_ffi::BitmapMetrics bitmapMetrics =
589                     fontations_ffi::bitmap_metrics(*bitmap_glyph);
590 
591             std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
592                     SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
593             if (!codec) {
594                 return mx;
595             }
596 
597             SkImageInfo info = codec->getInfo();
598 
599             SkRect bounds = SkRect::Make(info.bounds());
600             SkMatrix matrix = fRemainingMatrix;
601 
602             // We deal with two scale factors here: Scaling from font units to
603             // device pixels, and scaling the embedded PNG from its number of
604             // rows to a specific size, depending on the ppem values in the
605             // bitmap glyph information.
606             SkScalar imageToSize = fScale.y() / bitmapMetrics.ppem_y;
607             float fontUnitsToSize = fScale.y() / fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
608 
609             // The offset from origin is given in font units, so requires a
610             // different scale factor than the scaling of the image.
611             matrix.preTranslate( bitmapMetrics.bearing_x * fontUnitsToSize,
612                                 -bitmapMetrics.bearing_y * fontUnitsToSize);
613             matrix.preScale(imageToSize, imageToSize);
614             matrix.preTranslate( bitmapMetrics.inner_bearing_x,
615                                 -bitmapMetrics.inner_bearing_y);
616 
617             // For sbix bitmap glyphs, the origin is the bottom left of the image.
618             float heightAdjustment =
619                     bitmapMetrics.placement_origin_bottom_left ? bounds.height() : 0;
620             matrix.preTranslate(0, -heightAdjustment);
621 
622             if (this->isSubpixel()) {
623                 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
624                                      SkFixedToScalar(glyph.getSubYFixed()));
625             }
626             matrix.mapRect(&bounds);
627             mx.bounds = SkRect::Make(bounds.roundOut());
628 
629             if (SkIsFinite(bitmapMetrics.advance)) {
630                 mx.advance = matrix.mapVector(bitmapMetrics.advance, 0);
631             }
632         } else {
633             mx.extraBits = ScalerContextBits::PATH;
634             mx.computeFromPath = true;
635         }
636         return mx;
637     }
638 
generatePngImage(const SkGlyph & glyph,void * imageBuffer)639     void generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
640         SkASSERT(glyph.maskFormat() == SkMask::kARGB32_Format);
641         SkBitmap dstBitmap;
642         dstBitmap.setInfo(
643                 SkImageInfo::Make(
644                         glyph.width(), glyph.height(), kN32_SkColorType, kPremul_SkAlphaType),
645                 glyph.rowBytes());
646         dstBitmap.setPixels(imageBuffer);
647 
648         SkCanvas canvas(dstBitmap);
649 
650         canvas.translate(-glyph.left(), -glyph.top());
651 
652         rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
653                 fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), fScale.y());
654         rust::cxxbridge1::Slice<const uint8_t> png_data = fontations_ffi::png_data(*bitmap_glyph);
655         SkASSERT(png_data.size());
656 
657         std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
658                 SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
659 
660         if (!codec) {
661             return;
662         }
663 
664         auto [glyph_image, result] = codec->getImage();
665         if (result != SkCodec::Result::kSuccess) {
666             return;
667         }
668 
669         canvas.clear(SK_ColorTRANSPARENT);
670         canvas.concat(fRemainingMatrix);
671 
672         if (this->isSubpixel()) {
673             canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
674                              SkFixedToScalar(glyph.getSubYFixed()));
675         }
676         const fontations_ffi::BitmapMetrics bitmapMetrics =
677                 fontations_ffi::bitmap_metrics(*bitmap_glyph);
678 
679         // We need two different scale factors here, one for font units to size,
680         // one for scaling the embedded PNG, see generateMetrics() for details.
681         SkScalar imageScaleFactor = fScale.y() / bitmapMetrics.ppem_y;
682 
683         float fontUnitsToSize = fScale.y() / fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
684         canvas.translate( bitmapMetrics.bearing_x * fontUnitsToSize,
685                          -bitmapMetrics.bearing_y * fontUnitsToSize);
686         canvas.scale(imageScaleFactor, imageScaleFactor);
687         canvas.translate( bitmapMetrics.inner_bearing_x,
688                          -bitmapMetrics.inner_bearing_y);
689 
690         float heightAdjustment =
691                 bitmapMetrics.placement_origin_bottom_left ? glyph_image->height() : 0;
692 
693         canvas.translate(0, -heightAdjustment);
694 
695         SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest);
696         canvas.drawImage(glyph_image, 0, 0, sampling);
697     }
698 
generateImage(const SkGlyph & glyph,void * imageBuffer)699     void generateImage(const SkGlyph& glyph, void* imageBuffer) override {
700         ScalerContextBits::value_type format = glyph.extraBits();
701         if (format == ScalerContextBits::PATH) {
702             const SkPath* devPath = glyph.path();
703             SkASSERT_RELEASE(devPath);
704             SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer),
705                                glyph.iRect(),
706                                glyph.rowBytes(),
707                                glyph.maskFormat());
708             SkASSERT(SkMask::kARGB32_Format != mask.fFormat);
709             const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
710             const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
711             // See https://issues.skia.org/issues/396360753
712             // We would like Fontations anti-aliasing on a surface with unknown pixel geometry to
713             // look like the FreeType backend in order to avoid perceived regressions
714             // in sharpness, so we ignore SkScalerContext::kGenA8FromLCD_Flag in fRec.fFlags.
715             const bool a8LCD = false;
716             const bool hairline = glyph.pathIsHairline();
717 
718             // Path offseting for subpixel positioning is not needed here,
719             // as this is done in SkScalerContext::internalGetPath.
720             GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline);
721 
722         } else if (format == ScalerContextBits::COLRv1 || format == ScalerContextBits::COLRv0) {
723             SkASSERT(glyph.maskFormat() == SkMask::kARGB32_Format);
724             SkBitmap dstBitmap;
725             dstBitmap.setInfo(
726                     SkImageInfo::Make(
727                             glyph.width(), glyph.height(), kN32_SkColorType, kPremul_SkAlphaType),
728                     glyph.rowBytes());
729             dstBitmap.setPixels(imageBuffer);
730 
731             SkCanvas canvas(dstBitmap);
732             if constexpr (kSkShowTextBlitCoverage) {
733                 canvas.clear(0x33FF0000);
734             } else {
735                 canvas.clear(SK_ColorTRANSPARENT);
736             }
737             canvas.translate(-glyph.left(), -glyph.top());
738 
739             drawCOLRGlyph(glyph, fRec.fForegroundColor, &canvas);
740         } else if (format == ScalerContextBits::BITMAP) {
741             generatePngImage(glyph, imageBuffer);
742         } else {
743             SK_ABORT("Bad format");
744         }
745     }
746 
generatePath(const SkGlyph & glyph,SkPath * path,bool * modified)747     bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override {
748         SkASSERT(glyph.extraBits() == ScalerContextBits::PATH);
749 
750         bool result = generateYScalePathForGlyphId(
751                 glyph.getGlyphID(), path, fScale.y(), *fHintingInstance);
752         if (!result) {
753             return false;
754         }
755 
756         *path = path->makeTransform(fRemainingMatrix);
757 
758         if (fScale.y() != 1.0f || !fRemainingMatrix.isIdentity()) {
759             *modified = true;
760         }
761         return true;
762     }
763 
drawCOLRGlyph(const SkGlyph & glyph,SkColor foregroundColor,SkCanvas * canvas)764     bool drawCOLRGlyph(const SkGlyph& glyph, SkColor foregroundColor, SkCanvas* canvas) {
765         uint16_t upem = fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
766         if (upem == 0) {
767             return false;
768         }
769 
770         SkMatrix scalerMatrix;
771         fRec.getSingleMatrix(&scalerMatrix);
772         SkAutoCanvasRestore autoRestore(canvas, true /* doSave */);
773 
774         // Scale down so that COLR operations can happen in glyph coordinates.
775         SkMatrix upemToPpem = SkMatrix::Scale(1.f / upem, 1.f / upem);
776         scalerMatrix.preConcat(upemToPpem);
777         canvas->concat(scalerMatrix);
778         SkPaint defaultPaint;
779         defaultPaint.setColor(SK_ColorRED);
780         sk_fontations::ColorPainter colorPainter(*this, *canvas, fPalette, foregroundColor,
781                                                  SkMask::kBW_Format != fRec.fMaskFormat, upem);
782         bool result = fontations_ffi::draw_colr_glyph(
783                 fBridgeFontRef, fBridgeNormalizedCoords, glyph.getGlyphID(), colorPainter);
784         return result;
785     }
786 
generateDrawable(const SkGlyph & glyph)787     sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override {
788         struct GlyphDrawable : public SkDrawable {
789             SkFontationsScalerContext* fSelf;
790             SkGlyph fGlyph;
791             GlyphDrawable(SkFontationsScalerContext* self, const SkGlyph& glyph)
792                     : fSelf(self), fGlyph(glyph) {}
793             SkRect onGetBounds() override { return fGlyph.rect(); }
794             size_t onApproximateBytesUsed() override { return sizeof(GlyphDrawable); }
795             void maybeShowTextBlitCoverage(SkCanvas* canvas) {
796                 if constexpr (kSkShowTextBlitCoverage) {
797                     SkPaint paint;
798                     paint.setColor(0x3300FF00);
799                     paint.setStyle(SkPaint::kFill_Style);
800                     canvas->drawRect(this->onGetBounds(), paint);
801                 }
802             }
803         };
804         struct ColrGlyphDrawable : public GlyphDrawable {
805             using GlyphDrawable::GlyphDrawable;
806             void onDraw(SkCanvas* canvas) override {
807                 this->maybeShowTextBlitCoverage(canvas);
808                 fSelf->drawCOLRGlyph(fGlyph, fSelf->fRec.fForegroundColor, canvas);
809             }
810         };
811         ScalerContextBits::value_type format = glyph.extraBits();
812         if (format == ScalerContextBits::COLRv1 || format == ScalerContextBits::COLRv0) {
813             return sk_sp<SkDrawable>(new ColrGlyphDrawable(this, glyph));
814         }
815         return nullptr;
816     }
817 
generateFontMetrics(SkFontMetrics * out_metrics)818     void generateFontMetrics(SkFontMetrics* out_metrics) override {
819         fontations_ffi::Metrics metrics =
820                 fontations_ffi::get_skia_metrics(fBridgeFontRef, fScale.y(), fBridgeNormalizedCoords);
821         out_metrics->fTop = -metrics.top;
822         out_metrics->fAscent = -metrics.ascent;
823         out_metrics->fDescent = -metrics.descent;
824         out_metrics->fBottom = -metrics.bottom;
825         out_metrics->fLeading = metrics.leading;
826         out_metrics->fAvgCharWidth = metrics.avg_char_width;
827         out_metrics->fMaxCharWidth = metrics.max_char_width;
828         out_metrics->fXMin = metrics.x_min;
829         out_metrics->fXMax = metrics.x_max;
830         out_metrics->fXHeight = -metrics.x_height;
831         out_metrics->fCapHeight = -metrics.cap_height;
832         out_metrics->fFlags = 0;
833 
834         // Cap height synthesis.
835         if (!out_metrics->fCapHeight) {
836             SkScalar height;
837             if (getContourHeightForLetter('H', height)) {
838                 out_metrics->fCapHeight = height;
839             } else  {
840                 out_metrics->fCapHeight = metrics.ascent;
841             }
842         }
843 
844         if (!out_metrics->fXHeight) {
845             SkScalar xHeight;
846             if (getContourHeightForLetter('x', xHeight)) {
847                 out_metrics->fXHeight = xHeight;
848             } else {
849                 out_metrics->fXHeight = metrics.ascent;
850             }
851         }
852 
853         if (fontations_ffi::table_data(fBridgeFontRef,
854                                        SkSetFourByteTag('f', 'v', 'a', 'r'),
855                                        0,
856                                        rust::Slice<uint8_t>())) {
857             out_metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
858         }
859         auto setMetric = [](float& dstMetric, const float srcMetric,
860                             uint32_t& flags, const SkFontMetrics::FontMetricsFlags flag)
861         {
862             if (std::isnan(srcMetric)) {
863                 dstMetric = 0;
864             } else {
865                 dstMetric = srcMetric;
866                 flags |= flag;
867             }
868         };
869         setMetric(out_metrics->fUnderlinePosition, -metrics.underline_position,
870                   out_metrics->fFlags, SkFontMetrics::kUnderlinePositionIsValid_Flag);
871         setMetric(out_metrics->fUnderlineThickness, metrics.underline_thickness,
872                   out_metrics->fFlags, SkFontMetrics::kUnderlineThicknessIsValid_Flag);
873 
874         setMetric(out_metrics->fStrikeoutPosition, -metrics.strikeout_position,
875                   out_metrics->fFlags, SkFontMetrics::kStrikeoutPositionIsValid_Flag);
876         setMetric(out_metrics->fStrikeoutThickness, metrics.strikeout_thickness,
877                   out_metrics->fFlags, SkFontMetrics::kStrikeoutThicknessIsValid_Flag);
878     }
879 
880 private:
881     SkVector fScale;
882     SkMatrix fRemainingMatrix;
883     sk_sp<SkData> fFontData = nullptr;
884     const fontations_ffi::BridgeFontRef& fBridgeFontRef;
885     const fontations_ffi::BridgeNormalizedCoords& fBridgeNormalizedCoords;
886     const fontations_ffi::BridgeOutlineCollection& fOutlines;
887     const fontations_ffi::BridgeMappingIndex& fMappingIndex;
888     const SkSpan<const SkColor> fPalette;
889     rust::Box<fontations_ffi::BridgeHintingInstance> fHintingInstance;
890     bool fDoLinearMetrics = false;
891     // Keeping the path extraction target buffers around significantly avoids
892     // allocation churn.
893     SkMutex fPathMutex;
894     rust::Vec<uint8_t> fPathVerbs;
895     rust::Vec<fontations_ffi::FfiPoint> fPathPoints;
896 
897     friend class sk_fontations::ColorPainter;
898 };
899 
onOpenStream(int * ttcIndex) const900 std::unique_ptr<SkStreamAsset> SkTypeface_Fontations::onOpenStream(int* ttcIndex) const {
901     *ttcIndex = fTtcIndex;
902     return std::make_unique<SkMemoryStream>(fFontData);
903 }
904 
onMakeClone(const SkFontArguments & args) const905 sk_sp<SkTypeface> SkTypeface_Fontations::onMakeClone(const SkFontArguments& args) const {
906     // Matching DWrite implementation, return self if ttc index mismatches.
907     if (fTtcIndex != SkTo<uint32_t>(args.getCollectionIndex())) {
908         return sk_ref_sp(this);
909     }
910 
911     int numAxes = onGetVariationDesignPosition(nullptr, 0);
912     auto fusedDesignPosition =
913             std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>(numAxes);
914     int retrievedAxes = onGetVariationDesignPosition(fusedDesignPosition.get(), numAxes);
915     if (numAxes != retrievedAxes) {
916         return nullptr;
917     }
918 
919     // We know the internally retrieved axes are normalized, contain a value for every possible
920     // axis, other axes do not exist, so we only need to override any of those.
921     for (int i = 0; i < numAxes; ++i) {
922         const SkFontArguments::VariationPosition& argPosition = args.getVariationDesignPosition();
923         for (int j = 0; j < argPosition.coordinateCount; ++j) {
924             if (fusedDesignPosition[i].axis == argPosition.coordinates[j].axis) {
925                 fusedDesignPosition[i].value = argPosition.coordinates[j].value;
926             }
927         }
928     }
929 
930     SkFontArguments fusedArgs;
931     fusedArgs.setVariationDesignPosition({fusedDesignPosition.get(), SkToInt(numAxes)});
932     fusedArgs.setPalette(args.getPalette());
933 
934     rust::cxxbridge1::Box<fontations_ffi::BridgeNormalizedCoords> normalized_args =
935             make_normalized_coords(*fBridgeFontRef, fusedArgs.getVariationDesignPosition());
936 
937     if (!fontations_ffi::normalized_coords_equal(*normalized_args, *fBridgeNormalizedCoords)) {
938         return MakeFromData(fFontData, fusedArgs);
939     }
940 
941     // TODO(crbug.com/skia/330149870): Palette differences are not fused, see DWrite backend impl.
942     rust::Slice<const fontations_ffi::PaletteOverride> argPaletteOverrides(
943             reinterpret_cast<const fontations_ffi::PaletteOverride*>(args.getPalette().overrides),
944             args.getPalette().overrideCount);
945     rust::Vec<uint32_t> newPalette =
946             resolve_palette(*fBridgeFontRef, args.getPalette().index, argPaletteOverrides);
947 
948     if (fPalette.size() != newPalette.size() ||
949         memcmp(fPalette.data(), newPalette.data(), fPalette.size() * sizeof(fPalette[0]))) {
950         return MakeFromData(fFontData, fusedArgs);
951     }
952 
953     return sk_ref_sp(this);
954 }
955 
onCreateScalerContext(const SkScalerContextEffects & effects,const SkDescriptor * desc) const956 std::unique_ptr<SkScalerContext> SkTypeface_Fontations::onCreateScalerContext(
957         const SkScalerContextEffects& effects, const SkDescriptor* desc) const {
958     return this->onCreateScalerContextAsProxyTypeface(effects, desc, nullptr);
959 }
960 
onCreateScalerContextAsProxyTypeface(const SkScalerContextEffects & effects,const SkDescriptor * desc,SkTypeface * proxyTypeface) const961 std::unique_ptr<SkScalerContext> SkTypeface_Fontations::onCreateScalerContextAsProxyTypeface(
962                                     const SkScalerContextEffects& effects,
963                                     const SkDescriptor* desc,
964                                     SkTypeface* proxyTypeface) const {
965     return std::make_unique<SkFontationsScalerContext>(
966             *this,
967             effects,
968             desc,
969             proxyTypeface ? *proxyTypeface : *const_cast<SkTypeface_Fontations*>(this));
970 }
971 
onGetAdvancedMetrics() const972 std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Fontations::onGetAdvancedMetrics() const {
973     std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
974 
975     if (!fontations_ffi::is_embeddable(*fBridgeFontRef)) {
976         info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
977     }
978 
979     if (!fontations_ffi::is_subsettable(*fBridgeFontRef)) {
980         info->fFlags |= SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag;
981     }
982 
983     if (fontations_ffi::table_data(
984                 *fBridgeFontRef, SkSetFourByteTag('f', 'v', 'a', 'r'), 0, rust::Slice<uint8_t>())) {
985         info->fFlags |= SkAdvancedTypefaceMetrics::kVariable_FontFlag;
986     }
987 
988     // Metrics information.
989     fontations_ffi::Metrics metrics =
990             fontations_ffi::get_unscaled_metrics(*fBridgeFontRef, *fBridgeNormalizedCoords);
991     info->fAscent = metrics.ascent;
992     info->fDescent = metrics.descent;
993     info->fCapHeight = metrics.cap_height;
994 
995     info->fBBox = SkIRect::MakeLTRB((int32_t)metrics.x_min,
996                                     (int32_t)metrics.top,
997                                     (int32_t)metrics.x_max,
998                                     (int32_t)metrics.bottom);
999 
1000     // Style information.
1001     if (fontations_ffi::is_fixed_pitch(*fBridgeFontRef)) {
1002         info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1003     }
1004 
1005     fontations_ffi::BridgeFontStyle fontStyle;
1006     if (fontations_ffi::get_font_style(*fBridgeFontRef, *fBridgeNormalizedCoords, fontStyle)) {
1007         if (fontStyle.slant == SkFontStyle::Slant::kItalic_Slant) {
1008             info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1009         }
1010     }
1011 
1012     if (fontations_ffi::is_serif_style(*fBridgeFontRef)) {
1013         info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1014     } else if (fontations_ffi::is_script_style(*fBridgeFontRef)) {
1015         info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1016     }
1017 
1018     info->fItalicAngle = fontations_ffi::italic_angle(*fBridgeFontRef);
1019 
1020     return info;
1021 }
1022 
onGetFontDescriptor(SkFontDescriptor * desc,bool * serialize) const1023 void SkTypeface_Fontations::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
1024     SkString familyName;
1025     onGetFamilyName(&familyName);
1026     desc->setFamilyName(familyName.c_str());
1027     desc->setStyle(this->fontStyle());
1028     desc->setFactoryId(FactoryId);
1029 
1030     // TODO: keep the index to emit here
1031     desc->setPaletteIndex(0);
1032     SkSpan<const SkColor> palette = getPalette();
1033     // TODO: omit override when palette[n] == CPAL[paletteIndex][n]
1034     size_t paletteOverrideCount = palette.size();
1035     auto overrides = desc->setPaletteEntryOverrides(paletteOverrideCount);
1036     for (size_t i = 0; i < paletteOverrideCount; ++i) {
1037         overrides[i] = {(uint16_t)i, palette[i]};
1038     }
1039 
1040     *serialize = true;
1041 }
1042 
onGetTableData(SkFontTableTag tag,size_t offset,size_t length,void * data) const1043 size_t SkTypeface_Fontations::onGetTableData(SkFontTableTag tag,
1044                                              size_t offset,
1045                                              size_t length,
1046                                              void* data) const {
1047     rust::Slice<uint8_t> dataSlice;
1048     if (data) {
1049         dataSlice = rust::Slice<uint8_t>(reinterpret_cast<uint8_t*>(data), length);
1050     }
1051     size_t copied = fontations_ffi::table_data(*fBridgeFontRef, tag, offset, dataSlice);
1052     // If data is nullptr, the Rust side doesn't see a length limit.
1053     return std::min(copied, length);
1054 }
1055 
onGetTableTags(SkFontTableTag tags[]) const1056 int SkTypeface_Fontations::onGetTableTags(SkFontTableTag tags[]) const {
1057     uint16_t numTables = fontations_ffi::table_tags(*fBridgeFontRef, rust::Slice<uint32_t>());
1058     if (!tags) {
1059         return numTables;
1060     }
1061     rust::Slice<uint32_t> copyToTags(tags, numTables);
1062     return fontations_ffi::table_tags(*fBridgeFontRef, copyToTags);
1063 }
1064 
onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],int coordinateCount) const1065 int SkTypeface_Fontations::onGetVariationDesignPosition(
1066         SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const {
1067     rust::Slice<fontations_ffi::SkiaDesignCoordinate> copyToCoordinates;
1068     if (coordinates) {
1069         copyToCoordinates = rust::Slice<fontations_ffi::SkiaDesignCoordinate>(
1070                 reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(coordinates),
1071                 coordinateCount);
1072     }
1073     return fontations_ffi::variation_position(*fBridgeNormalizedCoords, copyToCoordinates);
1074 }
1075 
onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],int parameterCount) const1076 int SkTypeface_Fontations::onGetVariationDesignParameters(
1077         SkFontParameters::Variation::Axis parameters[], int parameterCount) const {
1078     sk_fontations::AxisWrapper axisWrapper(parameters, parameterCount);
1079     return fontations_ffi::populate_axes(*fBridgeFontRef, axisWrapper);
1080 }
1081 
1082 namespace sk_fontations {
1083 
1084 namespace {
1085 
1086 const uint16_t kForegroundColorPaletteIndex = 0xFFFF;
1087 
populateStopsAndColors(std::vector<SkScalar> & dest_stops,std::vector<SkColor4f> & dest_colors,const SkSpan<const SkColor> & palette,SkColor foregroundColor,fontations_ffi::BridgeColorStops & color_stops)1088 void populateStopsAndColors(std::vector<SkScalar>& dest_stops,
1089                             std::vector<SkColor4f>& dest_colors,
1090                             const SkSpan<const SkColor>& palette,
1091                             SkColor foregroundColor,
1092                             fontations_ffi::BridgeColorStops& color_stops) {
1093     SkASSERT(dest_stops.size() == 0);
1094     SkASSERT(dest_colors.size() == 0);
1095     size_t num_color_stops = fontations_ffi::num_color_stops(color_stops);
1096     dest_stops.reserve(num_color_stops);
1097     dest_colors.reserve(num_color_stops);
1098 
1099     fontations_ffi::ColorStop color_stop;
1100     while (fontations_ffi::next_color_stop(color_stops, color_stop)) {
1101         dest_stops.push_back(color_stop.stop);
1102         SkColor4f dest_color;
1103         if (color_stop.palette_index == kForegroundColorPaletteIndex) {
1104             dest_color = SkColor4f::FromColor(foregroundColor);
1105         } else {
1106             dest_color = SkColor4f::FromColor(palette[color_stop.palette_index]);
1107         }
1108         dest_color.fA *= color_stop.alpha;
1109         dest_colors.push_back(dest_color);
1110     }
1111 }
1112 
lerpSkColor(SkColor4f c0,SkColor4f c1,float t)1113 SkColor4f lerpSkColor(SkColor4f c0, SkColor4f c1, float t) {
1114     // Due to the floating point calculation in the caller, when interpolating between very
1115     // narrow stops, we may get values outside the interpolation range, guard against these.
1116     if (t < 0) {
1117         return c0;
1118     }
1119     if (t > 1) {
1120         return c1;
1121     }
1122 
1123     const auto c0_4f = skvx::float4::Load(c0.vec());
1124     const auto c1_4f = skvx::float4::Load(c1.vec());
1125     const auto c_4f = c0_4f + (c1_4f - c0_4f) * t;
1126 
1127     SkColor4f l;
1128     c_4f.store(l.vec());
1129     return l;
1130 }
1131 
1132 enum TruncateStops { TruncateStart, TruncateEnd };
1133 
1134 // Truncate a vector of color stops at a previously computed stop position and insert at that
1135 // position the color interpolated between the surrounding stops.
truncateToStopInterpolating(SkScalar zeroRadiusStop,std::vector<SkColor4f> & colors,std::vector<SkScalar> & stops,TruncateStops truncateStops)1136 void truncateToStopInterpolating(SkScalar zeroRadiusStop,
1137                                  std::vector<SkColor4f>& colors,
1138                                  std::vector<SkScalar>& stops,
1139                                  TruncateStops truncateStops) {
1140     if (stops.size() <= 1u || zeroRadiusStop < stops.front() || stops.back() < zeroRadiusStop) {
1141         return;
1142     }
1143 
1144     size_t afterIndex =
1145             (truncateStops == TruncateStart)
1146                     ? std::lower_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin()
1147                     : std::upper_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin();
1148 
1149     const float t =
1150             (zeroRadiusStop - stops[afterIndex - 1]) / (stops[afterIndex] - stops[afterIndex - 1]);
1151     SkColor4f lerpColor = lerpSkColor(colors[afterIndex - 1], colors[afterIndex], t);
1152 
1153     if (truncateStops == TruncateStart) {
1154         stops.erase(stops.begin(), stops.begin() + afterIndex);
1155         colors.erase(colors.begin(), colors.begin() + afterIndex);
1156         stops.insert(stops.begin(), 0);
1157         colors.insert(colors.begin(), lerpColor);
1158     } else {
1159         stops.erase(stops.begin() + afterIndex, stops.end());
1160         colors.erase(colors.begin() + afterIndex, colors.end());
1161         stops.insert(stops.end(), 1);
1162         colors.insert(colors.end(), lerpColor);
1163     }
1164 }
1165 
1166 // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite
ToSkBlendMode(uint16_t colrV1CompositeMode)1167 inline SkBlendMode ToSkBlendMode(uint16_t colrV1CompositeMode) {
1168     switch (colrV1CompositeMode) {
1169         case 0:
1170             return SkBlendMode::kClear;
1171         case 1:
1172             return SkBlendMode::kSrc;
1173         case 2:
1174             return SkBlendMode::kDst;
1175         case 3:
1176             return SkBlendMode::kSrcOver;
1177         case 4:
1178             return SkBlendMode::kDstOver;
1179         case 5:
1180             return SkBlendMode::kSrcIn;
1181         case 6:
1182             return SkBlendMode::kDstIn;
1183         case 7:
1184             return SkBlendMode::kSrcOut;
1185         case 8:
1186             return SkBlendMode::kDstOut;
1187         case 9:
1188             return SkBlendMode::kSrcATop;
1189         case 10:
1190             return SkBlendMode::kDstATop;
1191         case 11:
1192             return SkBlendMode::kXor;
1193         case 12:
1194             return SkBlendMode::kPlus;
1195         case 13:
1196             return SkBlendMode::kScreen;
1197         case 14:
1198             return SkBlendMode::kOverlay;
1199         case 15:
1200             return SkBlendMode::kDarken;
1201         case 16:
1202             return SkBlendMode::kLighten;
1203         case 17:
1204             return SkBlendMode::kColorDodge;
1205         case 18:
1206             return SkBlendMode::kColorBurn;
1207         case 19:
1208             return SkBlendMode::kHardLight;
1209         case 20:
1210             return SkBlendMode::kSoftLight;
1211         case 21:
1212             return SkBlendMode::kDifference;
1213         case 22:
1214             return SkBlendMode::kExclusion;
1215         case 23:
1216             return SkBlendMode::kMultiply;
1217         case 24:
1218             return SkBlendMode::kHue;
1219         case 25:
1220             return SkBlendMode::kSaturation;
1221         case 26:
1222             return SkBlendMode::kColor;
1223         case 27:
1224             return SkBlendMode::kLuminosity;
1225         default:
1226             return SkBlendMode::kDst;
1227     }
1228 }
1229 
ToSkTileMode(uint8_t extendMode)1230 inline SkTileMode ToSkTileMode(uint8_t extendMode) {
1231     switch (extendMode) {
1232         case 1:
1233             return SkTileMode::kRepeat;
1234         case 2:
1235             return SkTileMode::kMirror;
1236         default:
1237             return SkTileMode::kClamp;
1238     }
1239 }
1240 }  // namespace
1241 
ColorPainter(SkFontationsScalerContext & scaler_context,SkCanvas & canvas,SkSpan<const SkColor> palette,SkColor foregroundColor,bool antialias,uint16_t upem)1242 ColorPainter::ColorPainter(SkFontationsScalerContext& scaler_context,
1243                            SkCanvas& canvas,
1244                            SkSpan<const SkColor> palette,
1245                            SkColor foregroundColor,
1246                            bool antialias,
1247                            uint16_t upem)
1248         : fScalerContext(scaler_context)
1249         , fCanvas(canvas)
1250         , fPalette(palette)
1251         , fForegroundColor(foregroundColor)
1252         , fAntialias(antialias)
1253         , fUpem(upem) {}
1254 
push_transform(const fontations_ffi::Transform & transform_arg)1255 void ColorPainter::push_transform(const fontations_ffi::Transform& transform_arg) {
1256     fCanvas.save();
1257     fCanvas.concat(SkMatrixFromFontationsTransform(transform_arg));
1258 }
1259 
pop_transform()1260 void ColorPainter::pop_transform() { fCanvas.restore(); }
1261 
push_clip_glyph(uint16_t glyph_id)1262 void ColorPainter::push_clip_glyph(uint16_t glyph_id) {
1263     fCanvas.save();
1264     SkPath path;
1265     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1266     fCanvas.clipPath(path, fAntialias);
1267 }
1268 
push_clip_rectangle(float x_min,float y_min,float x_max,float y_max)1269 void ColorPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) {
1270     fCanvas.save();
1271     SkRect clipRect = SkRect::MakeLTRB(x_min, -y_min, x_max, -y_max);
1272     fCanvas.clipRect(clipRect, fAntialias);
1273 }
1274 
pop_clip()1275 void ColorPainter::pop_clip() { fCanvas.restore(); }
1276 
configure_solid_paint(uint16_t palette_index,float alpha,SkPaint & paint)1277 void ColorPainter::configure_solid_paint(uint16_t palette_index, float alpha, SkPaint& paint) {
1278     paint.setAntiAlias(fAntialias);
1279     SkColor4f color;
1280     if (palette_index == kForegroundColorPaletteIndex) {
1281         color = SkColor4f::FromColor(fForegroundColor);
1282     } else {
1283         color = SkColor4f::FromColor(fPalette[palette_index]);
1284     }
1285     color.fA *= alpha;
1286     paint.setShader(nullptr);
1287     paint.setColor(color);
1288 }
1289 
fill_solid(uint16_t palette_index,float alpha)1290 void ColorPainter::fill_solid(uint16_t palette_index, float alpha) {
1291     SkPaint paint;
1292     configure_solid_paint(palette_index, alpha, paint);
1293     fCanvas.drawPaint(paint);
1294 }
1295 
fill_glyph_solid(uint16_t glyph_id,uint16_t palette_index,float alpha)1296 void ColorPainter::fill_glyph_solid(uint16_t glyph_id, uint16_t palette_index, float alpha) {
1297     SkPath path;
1298     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1299 
1300     SkPaint paint;
1301     configure_solid_paint(palette_index, alpha, paint);
1302     fCanvas.drawPath(path, paint);
1303 }
1304 
configure_linear_paint(const fontations_ffi::FillLinearParams & linear_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode,SkPaint & paint,SkMatrix * paintTransform)1305 void ColorPainter::configure_linear_paint(const fontations_ffi::FillLinearParams& linear_params,
1306                                           fontations_ffi::BridgeColorStops& bridge_stops,
1307                                           uint8_t extend_mode,
1308                                           SkPaint& paint,
1309                                           SkMatrix* paintTransform) {
1310     paint.setAntiAlias(fAntialias);
1311 
1312     std::vector<SkScalar> stops;
1313     std::vector<SkColor4f> colors;
1314 
1315     populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1316 
1317     if (stops.size() == 1) {
1318         paint.setColor(colors[0]);
1319         return;
1320     }
1321 
1322     SkPoint linePositions[2] = {
1323             SkPoint::Make(SkFloatToScalar(linear_params.x0), -SkFloatToScalar(linear_params.y0)),
1324             SkPoint::Make(SkFloatToScalar(linear_params.x1), -SkFloatToScalar(linear_params.y1))};
1325     SkTileMode tileMode = ToSkTileMode(extend_mode);
1326 
1327     sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
1328             linePositions,
1329             colors.data(),
1330             SkColorSpace::MakeSRGB(),
1331             stops.data(),
1332             stops.size(),
1333             tileMode,
1334             SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1335                                             SkGradientShader::Interpolation::ColorSpace::kSRGB,
1336                                             SkGradientShader::Interpolation::HueMethod::kShorter},
1337             paintTransform));
1338 
1339     SkASSERT(shader);
1340     // An opaque color is needed to ensure the gradient is not modulated by alpha.
1341     paint.setColor(SK_ColorBLACK);
1342     paint.setShader(shader);
1343 }
1344 
fill_linear(const fontations_ffi::FillLinearParams & linear_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1345 void ColorPainter::fill_linear(const fontations_ffi::FillLinearParams& linear_params,
1346                                fontations_ffi::BridgeColorStops& bridge_stops,
1347                                uint8_t extend_mode) {
1348     SkPaint paint;
1349 
1350     configure_linear_paint(linear_params, bridge_stops, extend_mode, paint);
1351 
1352     fCanvas.drawPaint(paint);
1353 }
1354 
fill_glyph_linear(uint16_t glyph_id,const fontations_ffi::Transform & transform,const fontations_ffi::FillLinearParams & linear_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1355 void ColorPainter::fill_glyph_linear(uint16_t glyph_id,
1356                                      const fontations_ffi::Transform& transform,
1357                                      const fontations_ffi::FillLinearParams& linear_params,
1358                                      fontations_ffi::BridgeColorStops& bridge_stops,
1359                                      uint8_t extend_mode) {
1360     SkPath path;
1361     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1362 
1363     SkPaint paint;
1364     SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1365     configure_linear_paint(linear_params, bridge_stops, extend_mode, paint, &paintTransform);
1366     fCanvas.drawPath(path, paint);
1367 }
1368 
configure_radial_paint(const fontations_ffi::FillRadialParams & fill_radial_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode,SkPaint & paint,SkMatrix * paintTransform)1369 void ColorPainter::configure_radial_paint(
1370         const fontations_ffi::FillRadialParams& fill_radial_params,
1371         fontations_ffi::BridgeColorStops& bridge_stops,
1372         uint8_t extend_mode,
1373         SkPaint& paint,
1374         SkMatrix* paintTransform) {
1375     paint.setAntiAlias(fAntialias);
1376 
1377     SkPoint start = SkPoint::Make(fill_radial_params.x0, -fill_radial_params.y0);
1378     SkPoint end = SkPoint::Make(fill_radial_params.x1, -fill_radial_params.y1);
1379 
1380     float startRadius = fill_radial_params.r0;
1381     float endRadius = fill_radial_params.r1;
1382 
1383     std::vector<SkScalar> stops;
1384     std::vector<SkColor4f> colors;
1385 
1386     populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1387 
1388     // Draw single color if there's only one stop.
1389     if (stops.size() == 1) {
1390         paint.setColor(colors[0]);
1391         fCanvas.drawPaint(paint);
1392         return;
1393     }
1394 
1395     SkTileMode tileMode = ToSkTileMode(extend_mode);
1396 
1397     // For negative radii, interpolation is needed to prepare parameters suitable
1398     // for invoking the shader. Implementation below as resolution discussed in
1399     // https://github.com/googlefonts/colr-gradients-spec/issues/367.
1400     // Truncate to manually interpolated color for tile mode clamp, otherwise
1401     // calculate positive projected circles.
1402     if (startRadius < 0 || endRadius < 0) {
1403         if (startRadius == endRadius && startRadius < 0) {
1404             paint.setColor(SK_ColorTRANSPARENT);
1405             // return true;
1406             return;
1407         }
1408 
1409         if (tileMode == SkTileMode::kClamp) {
1410             SkVector startToEnd = end - start;
1411             SkScalar radiusDiff = endRadius - startRadius;
1412             SkScalar zeroRadiusStop = 0.f;
1413             TruncateStops truncateSide = TruncateStart;
1414             if (startRadius < 0) {
1415                 truncateSide = TruncateStart;
1416 
1417                 // Compute color stop position where radius is = 0.  After the scaling
1418                 // of stop positions to the normal 0,1 range that we have done above,
1419                 // the size of the radius as a function of the color stops is: r(x) = r0
1420                 // + x*(r1-r0) Solving this function for r(x) = 0, we get: x = -r0 /
1421                 // (r1-r0)
1422                 zeroRadiusStop = -startRadius / (endRadius - startRadius);
1423                 startRadius = 0.f;
1424                 SkVector startEndDiff = end - start;
1425                 startEndDiff.scale(zeroRadiusStop);
1426                 start = start + startEndDiff;
1427             }
1428 
1429             if (endRadius < 0) {
1430                 truncateSide = TruncateEnd;
1431                 zeroRadiusStop = -startRadius / (endRadius - startRadius);
1432                 endRadius = 0.f;
1433                 SkVector startEndDiff = end - start;
1434                 startEndDiff.scale(1 - zeroRadiusStop);
1435                 end = end - startEndDiff;
1436             }
1437 
1438             if (!(startRadius == 0 && endRadius == 0)) {
1439                 truncateToStopInterpolating(zeroRadiusStop, colors, stops, truncateSide);
1440             } else {
1441                 // If both radii have become negative and where clamped to 0, we need to
1442                 // produce a single color cone, otherwise the shader colors the whole
1443                 // plane in a single color when two radii are specified as 0.
1444                 if (radiusDiff > 0) {
1445                     end = start + startToEnd;
1446                     endRadius = radiusDiff;
1447                     colors.erase(colors.begin(), colors.end() - 1);
1448                     stops.erase(stops.begin(), stops.end() - 1);
1449                 } else {
1450                     start -= startToEnd;
1451                     startRadius = -radiusDiff;
1452                     colors.erase(colors.begin() + 1, colors.end());
1453                     stops.erase(stops.begin() + 1, stops.end());
1454                 }
1455             }
1456         } else {
1457             if (startRadius < 0 || endRadius < 0) {
1458                 auto roundIntegerMultiple = [](SkScalar factorZeroCrossing, SkTileMode tileMode) {
1459                     int roundedMultiple = factorZeroCrossing > 0 ? ceilf(factorZeroCrossing)
1460                                                                  : floorf(factorZeroCrossing) - 1;
1461                     if (tileMode == SkTileMode::kMirror && roundedMultiple % 2 != 0) {
1462                         roundedMultiple += roundedMultiple < 0 ? -1 : 1;
1463                     }
1464                     return roundedMultiple;
1465                 };
1466 
1467                 SkVector startToEnd = end - start;
1468                 SkScalar radiusDiff = endRadius - startRadius;
1469                 SkScalar factorZeroCrossing = (startRadius / (startRadius - endRadius));
1470                 bool inRange = 0.f <= factorZeroCrossing && factorZeroCrossing <= 1.0f;
1471                 SkScalar direction = inRange && radiusDiff < 0 ? -1.0f : 1.0f;
1472                 SkScalar circleProjectionFactor =
1473                         roundIntegerMultiple(factorZeroCrossing * direction, tileMode);
1474                 startToEnd.scale(circleProjectionFactor);
1475                 startRadius += circleProjectionFactor * radiusDiff;
1476                 endRadius += circleProjectionFactor * radiusDiff;
1477                 start += startToEnd;
1478                 end += startToEnd;
1479             }
1480         }
1481     }
1482 
1483     // An opaque color is needed to ensure the gradient is not modulated by alpha.
1484     paint.setColor(SK_ColorBLACK);
1485 
1486     paint.setShader(SkGradientShader::MakeTwoPointConical(
1487             start,
1488             startRadius,
1489             end,
1490             endRadius,
1491             colors.data(),
1492             SkColorSpace::MakeSRGB(),
1493             stops.data(),
1494             stops.size(),
1495             tileMode,
1496             SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1497                                             SkGradientShader::Interpolation::ColorSpace::kSRGB,
1498                                             SkGradientShader::Interpolation::HueMethod::kShorter},
1499             paintTransform));
1500 }
1501 
fill_radial(const fontations_ffi::FillRadialParams & fill_radial_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1502 void ColorPainter::fill_radial(const fontations_ffi::FillRadialParams& fill_radial_params,
1503                                fontations_ffi::BridgeColorStops& bridge_stops,
1504                                uint8_t extend_mode) {
1505     SkPaint paint;
1506 
1507     configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint);
1508 
1509     fCanvas.drawPaint(paint);
1510 }
1511 
fill_glyph_radial(uint16_t glyph_id,const fontations_ffi::Transform & transform,const fontations_ffi::FillRadialParams & fill_radial_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1512 void ColorPainter::fill_glyph_radial(uint16_t glyph_id,
1513                                      const fontations_ffi::Transform& transform,
1514                                      const fontations_ffi::FillRadialParams& fill_radial_params,
1515                                      fontations_ffi::BridgeColorStops& bridge_stops,
1516                                      uint8_t extend_mode) {
1517     SkPath path;
1518     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1519 
1520     SkPaint paint;
1521     SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1522     configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint, &paintTransform);
1523     fCanvas.drawPath(path, paint);
1524 }
1525 
configure_sweep_paint(const fontations_ffi::FillSweepParams & sweep_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode,SkPaint & paint,SkMatrix * paintTransform)1526 void ColorPainter::configure_sweep_paint(const fontations_ffi::FillSweepParams& sweep_params,
1527                                          fontations_ffi::BridgeColorStops& bridge_stops,
1528                                          uint8_t extend_mode,
1529                                          SkPaint& paint,
1530                                          SkMatrix* paintTransform) {
1531     paint.setAntiAlias(fAntialias);
1532 
1533     SkPoint center = SkPoint::Make(sweep_params.x0, -sweep_params.y0);
1534 
1535     std::vector<SkScalar> stops;
1536     std::vector<SkColor4f> colors;
1537 
1538     populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1539 
1540     if (stops.size() == 1) {
1541         paint.setColor(colors[0]);
1542         fCanvas.drawPaint(paint);
1543         return;
1544     }
1545 
1546     // An opaque color is needed to ensure the gradient is not modulated by alpha.
1547     paint.setColor(SK_ColorBLACK);
1548     SkTileMode tileMode = ToSkTileMode(extend_mode);
1549 
1550     paint.setColor(SK_ColorBLACK);
1551     paint.setShader(SkGradientShader::MakeSweep(
1552             center.x(),
1553             center.y(),
1554             colors.data(),
1555             SkColorSpace::MakeSRGB(),
1556             stops.data(),
1557             stops.size(),
1558             tileMode,
1559             sweep_params.start_angle,
1560             sweep_params.end_angle,
1561             SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1562                                             SkGradientShader::Interpolation::ColorSpace::kSRGB,
1563                                             SkGradientShader::Interpolation::HueMethod::kShorter},
1564             paintTransform));
1565 }
1566 
fill_sweep(const fontations_ffi::FillSweepParams & sweep_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1567 void ColorPainter::fill_sweep(const fontations_ffi::FillSweepParams& sweep_params,
1568                               fontations_ffi::BridgeColorStops& bridge_stops,
1569                               uint8_t extend_mode) {
1570     SkPaint paint;
1571 
1572     configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint);
1573 
1574     fCanvas.drawPaint(paint);
1575 }
1576 
fill_glyph_sweep(uint16_t glyph_id,const fontations_ffi::Transform & transform,const fontations_ffi::FillSweepParams & sweep_params,fontations_ffi::BridgeColorStops & bridge_stops,uint8_t extend_mode)1577 void ColorPainter::fill_glyph_sweep(uint16_t glyph_id,
1578                                     const fontations_ffi::Transform& transform,
1579                                     const fontations_ffi::FillSweepParams& sweep_params,
1580                                     fontations_ffi::BridgeColorStops& bridge_stops,
1581                                     uint8_t extend_mode) {
1582     SkPath path;
1583     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1584 
1585     SkPaint paint;
1586     SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1587     configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint, &paintTransform);
1588     fCanvas.drawPath(path, paint);
1589 }
1590 
push_layer(uint8_t compositeMode)1591 void ColorPainter::push_layer(uint8_t compositeMode) {
1592     SkPaint paint;
1593     paint.setBlendMode(ToSkBlendMode(compositeMode));
1594     fCanvas.saveLayer(nullptr, &paint);
1595 }
1596 
pop_layer()1597 void ColorPainter::pop_layer() { fCanvas.restore(); }
1598 
BoundsPainter(SkFontationsScalerContext & scaler_context,SkMatrix initialTransfom,uint16_t upem)1599 BoundsPainter::BoundsPainter(SkFontationsScalerContext& scaler_context,
1600                              SkMatrix initialTransfom,
1601                              uint16_t upem)
1602         : fScalerContext(scaler_context)
1603         , fMatrixStack({initialTransfom})
1604         , fUpem(upem)
1605         , fBounds(SkRect::MakeEmpty()) {}
1606 
getBoundingBox()1607 SkRect BoundsPainter::getBoundingBox() { return fBounds; }
1608 
1609 // fontations_ffi::ColorPainter interface.
push_transform(const fontations_ffi::Transform & transform_arg)1610 void BoundsPainter::push_transform(const fontations_ffi::Transform& transform_arg) {
1611     SkMatrix newTop(fMatrixStack.back());
1612     newTop.preConcat(SkMatrixFromFontationsTransform(transform_arg));
1613     fMatrixStack.push_back(newTop);
1614 }
pop_transform()1615 void BoundsPainter::pop_transform() {
1616     fMatrixStack.pop_back();
1617 }
1618 
push_clip_glyph(uint16_t glyph_id)1619 void BoundsPainter::push_clip_glyph(uint16_t glyph_id) {
1620     SkPath path;
1621     fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance());
1622     path.transform(fMatrixStack.back());
1623     fBounds.join(path.getBounds());
1624 }
1625 
push_clip_rectangle(float x_min,float y_min,float x_max,float y_max)1626 void BoundsPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) {
1627     SkRect clipRect = SkRect::MakeLTRB(x_min, -y_min, x_max, -y_max);
1628     SkPath rectPath = SkPath::Rect(clipRect);
1629     rectPath.transform(fMatrixStack.back());
1630     fBounds.join(rectPath.getBounds());
1631 }
1632 
fill_glyph_solid(uint16_t glyph_id,uint16_t,float)1633 void BoundsPainter::fill_glyph_solid(uint16_t glyph_id, uint16_t, float) {
1634     push_clip_glyph(glyph_id);
1635     pop_clip();
1636 }
1637 
fill_glyph_radial(uint16_t glyph_id,const fontations_ffi::Transform &,const fontations_ffi::FillRadialParams &,fontations_ffi::BridgeColorStops &,uint8_t)1638 void BoundsPainter::fill_glyph_radial(uint16_t glyph_id,
1639                                       const fontations_ffi::Transform&,
1640                                       const fontations_ffi::FillRadialParams&,
1641                                       fontations_ffi::BridgeColorStops&,
1642                                       uint8_t) {
1643     push_clip_glyph(glyph_id);
1644     pop_clip();
1645 }
fill_glyph_linear(uint16_t glyph_id,const fontations_ffi::Transform &,const fontations_ffi::FillLinearParams &,fontations_ffi::BridgeColorStops &,uint8_t)1646 void BoundsPainter::fill_glyph_linear(uint16_t glyph_id,
1647                                       const fontations_ffi::Transform&,
1648                                       const fontations_ffi::FillLinearParams&,
1649                                       fontations_ffi::BridgeColorStops&,
1650                                       uint8_t) {
1651     push_clip_glyph(glyph_id);
1652     pop_clip();
1653 }
1654 
fill_glyph_sweep(uint16_t glyph_id,const fontations_ffi::Transform &,const fontations_ffi::FillSweepParams &,fontations_ffi::BridgeColorStops &,uint8_t)1655 void BoundsPainter::fill_glyph_sweep(uint16_t glyph_id,
1656                                      const fontations_ffi::Transform&,
1657                                      const fontations_ffi::FillSweepParams&,
1658                                      fontations_ffi::BridgeColorStops&,
1659                                      uint8_t) {
1660     push_clip_glyph(glyph_id);
1661     pop_clip();
1662 }
1663 
1664 }  // namespace sk_fontations
1665