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