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