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