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