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