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