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