Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkTypeface_fontations.cpp
Go to the documentation of this file.
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 */
8
13#include "include/core/SkData.h"
21#include "src/core/SkFontPriv.h"
24
25namespace {
26
27[[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false;
28
29sk_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
37rust::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
43static_assert(sizeof(fontations_ffi::SkiaDesignCoordinate) ==
45 sizeof(fontations_ffi::SkiaDesignCoordinate::axis) ==
47 sizeof(fontations_ffi::SkiaDesignCoordinate::value) ==
49 offsetof(fontations_ffi::SkiaDesignCoordinate, axis) ==
51 offsetof(fontations_ffi::SkiaDesignCoordinate, value) ==
53 "Struct fontations_ffi::SkiaDesignCoordinate must match "
54 "SkFontArguments::VariationPosition::Coordinate.");
55
56rust::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
67SkMatrix 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
79} // namespace
80
81sk_sp<SkTypeface> SkTypeface_Make_Fontations(std::unique_ptr<SkStreamAsset> fontData,
82 const SkFontArguments& args) {
83 return SkTypeface_Fontations::MakeFromStream(std::move(fontData), args);
84}
85
86SkTypeface_Fontations::SkTypeface_Fontations(
87 sk_sp<SkData> fontData,
88 const SkFontStyle& style,
89 uint32_t ttcIndex,
90 rust::Box<fontations_ffi::BridgeFontRef>&& fontRef,
91 rust::Box<fontations_ffi::BridgeMappingIndex>&& mappingIndex,
92 rust::Box<fontations_ffi::BridgeNormalizedCoords>&& normalizedCoords,
93 rust::Box<fontations_ffi::BridgeOutlineCollection>&& outlines,
94 rust::Vec<uint32_t>&& palette)
95 : SkTypeface(style, true)
96 , fFontData(std::move(fontData))
97 , fTtcIndex(ttcIndex)
98 , fBridgeFontRef(std::move(fontRef))
99 , fMappingIndex(std::move(mappingIndex))
100 , fBridgeNormalizedCoords(std::move(normalizedCoords))
101 , fOutlines(std::move(outlines))
102 , fPalette(std::move(palette)) {}
103
104sk_sp<SkTypeface> SkTypeface_Fontations::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
105 const SkFontArguments& args) {
106 return MakeFromData(streamToData(stream), args);
107}
108
110 const SkFontArguments& args) {
111 uint32_t ttcIndex = args.getCollectionIndex();
112 rust::Box<fontations_ffi::BridgeFontRef> bridgeFontRef = make_bridge_font_ref(data, ttcIndex);
113 if (!fontations_ffi::font_ref_is_valid(*bridgeFontRef)) {
114 return nullptr;
115 }
116
117 SkFontStyle style;
118 fontations_ffi::BridgeFontStyle fontStyle;
119 if (fontations_ffi::get_font_style(*bridgeFontRef, fontStyle)) {
122 static_cast<SkFontStyle::Slant>(fontStyle.slant));
123 }
124 rust::Box<fontations_ffi::BridgeMappingIndex> mappingIndex =
125 fontations_ffi::make_mapping_index(*bridgeFontRef);
126
127 SkFontArguments::VariationPosition variationPosition = args.getVariationDesignPosition();
128 std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> concatenatedCoords = nullptr;
129 // Handle FreeType behaviour of upper 15 bits of collection index
130 // representing a named instance choice. If so, prepopulate the variation
131 // coordinates with the values from the named instance and append the user
132 // coordinates after that so they can override the named instance's
133 // coordinates.
134 if (args.getCollectionIndex() & 0xFFFF0000) {
135 size_t numNamedInstanceCoords =
136 fontations_ffi::coordinates_for_shifted_named_instance_index(
137 *bridgeFontRef,
138 args.getCollectionIndex(),
139 rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate>());
140 concatenatedCoords.reset(
142 [numNamedInstanceCoords + variationPosition.coordinateCount]);
143
144 rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate> targetSlice(
145 reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(concatenatedCoords.get()),
146 numNamedInstanceCoords);
147 size_t retrievedNamedInstanceCoords =
148 fontations_ffi::coordinates_for_shifted_named_instance_index(
149 *bridgeFontRef, args.getCollectionIndex(), targetSlice);
150 if (numNamedInstanceCoords != retrievedNamedInstanceCoords) {
151 return nullptr;
152 }
153 for (int i = 0; i < variationPosition.coordinateCount; ++i) {
154 concatenatedCoords[numNamedInstanceCoords + i] = variationPosition.coordinates[i];
155 }
156 variationPosition.coordinateCount += numNamedInstanceCoords;
157 variationPosition.coordinates = concatenatedCoords.get();
158 }
159
160 rust::Box<fontations_ffi::BridgeNormalizedCoords> normalizedCoords =
161 make_normalized_coords(*bridgeFontRef, variationPosition);
162 rust::Box<fontations_ffi::BridgeOutlineCollection> outlines =
163 fontations_ffi::get_outline_collection(*bridgeFontRef);
164
165 rust::Slice<const fontations_ffi::PaletteOverride> paletteOverrides(
166 reinterpret_cast<const ::fontations_ffi::PaletteOverride*>(args.getPalette().overrides),
167 args.getPalette().overrideCount);
168 rust::Vec<uint32_t> palette =
169 resolve_palette(*bridgeFontRef, args.getPalette().index, paletteOverrides);
170
172 style,
173 ttcIndex,
174 std::move(bridgeFontRef),
175 std::move(mappingIndex),
176 std::move(normalizedCoords),
177 std::move(outlines),
178 std::move(palette)));
179}
180
181namespace sk_fontations {
182
183// Path sanitization ported from SkFTGeometrySink.
184void PathGeometrySink::going_to(SkPoint point) {
185 if (!fStarted) {
186 fStarted = true;
187 fPath.moveTo(fCurrent);
188 }
189 fCurrent = point;
190}
191
192bool PathGeometrySink::current_is_not(SkPoint point) { return fCurrent != point; }
193
194void PathGeometrySink::move_to(float x, float y) {
195 if (fStarted) {
196 fPath.close();
197 fStarted = false;
198 }
200}
201
202void PathGeometrySink::line_to(float x, float y) {
204 if (current_is_not(pt0)) {
205 going_to(pt0);
206 fPath.lineTo(pt0);
207 }
208}
209
210void PathGeometrySink::quad_to(float cx0, float cy0, float x, float y) {
213 if (current_is_not(pt0) || current_is_not(pt1)) {
214 going_to(pt1);
215 fPath.quadTo(pt0, pt1);
216 }
217}
218void PathGeometrySink::curve_to(float cx0, float cy0, float cx1, float cy1, float x, float y) {
222 if (current_is_not(pt0) || current_is_not(pt1) || current_is_not(pt2)) {
223 going_to(pt2);
224 fPath.cubicTo(pt0, pt1, pt2);
225 }
226}
227
229
230SkPath PathGeometrySink::into_inner() && { return std::move(fPath); }
231
233 : fAxisArray(axisArray), fAxisCount(axisCount) {}
234
236 size_t i, uint32_t axisTag, float min, float def, float max, bool hidden) {
237 if (i >= fAxisCount) {
238 return false;
239 }
240 SkFontParameters::Variation::Axis& axis = fAxisArray[i];
241 axis.tag = axisTag;
242 axis.min = min;
243 axis.def = def;
244 axis.max = max;
245 axis.setHidden(hidden);
246 return true;
247}
248
249size_t AxisWrapper::size() const { return fAxisCount; }
250
251} // namespace sk_fontations
252
254 return fontations_ffi::units_per_em_or_zero(*fBridgeFontRef);
255}
256
258 rust::String readFamilyName = fontations_ffi::family_name(*fBridgeFontRef);
259 *familyName = SkString(readFamilyName.data(), readFamilyName.size());
260}
261
263 rust::String readPsName;
264 if (fontations_ffi::postscript_name(*fBridgeFontRef, readPsName)) {
265 if (postscriptName) {
266 *postscriptName = SkString(readPsName.data(), readPsName.size());
267 }
268 return true;
269 }
270
271 return false;
272}
273
275 fGlyphMasksMayNeedCurrentColorOnce([this] {
276 static constexpr SkFourByteTag COLRTag = SkSetFourByteTag('C', 'O', 'L', 'R');
277 fGlyphMasksMayNeedCurrentColor = this->getTableSize(COLRTag) > 0;
278 });
279 return fGlyphMasksMayNeedCurrentColor;
280}
281
283 int count,
284 SkGlyphID glyphs[]) const {
285 sk_bzero(glyphs, count * sizeof(glyphs[0]));
286
287 for (int i = 0; i < count; ++i) {
288 glyphs[i] = fontations_ffi::lookup_glyph_or_zero(*fBridgeFontRef, *fMappingIndex, chars[i]);
289 }
290}
292 return fontations_ffi::num_glyphs(*fBridgeFontRef);
293}
294
296 size_t numGlyphs = SkToSizeT(onCountGlyphs());
297 if (!codepointForGlyphMap) {
298 SkASSERT(numGlyphs == 0);
299 }
300 rust::Slice<uint32_t> codepointForGlyphSlice{reinterpret_cast<uint32_t*>(codepointForGlyphMap),
301 numGlyphs};
302 fontations_ffi::fill_glyph_to_unicode_map(*fBridgeFontRef, codepointForGlyphSlice);
303}
304
308
310public:
312 rust::Box<::fontations_ffi::BridgeLocalizedStrings> bridge_localized_strings)
313 : fBridgeLocalizedStrings(std::move(bridge_localized_strings)) {}
314 bool next(SkTypeface::LocalizedString* localized_string) override {
315 fontations_ffi::BridgeLocalizedName localizedName;
316 if (!fontations_ffi::localized_name_next(*fBridgeLocalizedStrings, localizedName)) {
317 return false;
318 }
319 localized_string->fString =
320 SkString(localizedName.string.data(), localizedName.string.size());
321 localized_string->fLanguage =
322 SkString(localizedName.language.data(), localizedName.language.size());
323 return true;
324 }
325
326private:
327 rust::Box<::fontations_ffi::BridgeLocalizedStrings> fBridgeLocalizedStrings;
328};
329
331 return new SkrifaLocalizedStrings(fontations_ffi::get_localized_strings(*fBridgeFontRef));
332}
333
335public:
337 const SkScalerContextEffects& effects,
338 const SkDescriptor* desc)
339 : SkScalerContext(face, effects, desc)
340 , fBridgeFontRef(
341 static_cast<SkTypeface_Fontations*>(this->getTypeface())->getBridgeFontRef())
342 , fBridgeNormalizedCoords(static_cast<SkTypeface_Fontations*>(this->getTypeface())
343 ->getBridgeNormalizedCoords())
344 , fOutlines(static_cast<SkTypeface_Fontations*>(this->getTypeface())->getOutlines())
345 , fPalette(static_cast<SkTypeface_Fontations*>(this->getTypeface())->getPalette()) {
346 fRec.getSingleMatrix(&fMatrix);
347 }
348
349 // TODO(drott): Add parameter/control for hinting here once that is available from Fontations.
350 bool generateYScalePathForGlyphId(uint16_t glyphId, SkPath* path, float yScale) {
352 fontations_ffi::BridgeScalerMetrics scalerMetrics;
353
354 if (!fontations_ffi::get_path(fOutlines,
355 glyphId,
356 yScale,
357 fBridgeNormalizedCoords,
358 pathWrapper,
359 scalerMetrics)) {
360 return false;
361 }
362 *path = std::move(pathWrapper).into_inner();
363 if (scalerMetrics.has_overlaps) {
364 // See SkScalerContext_FreeType_Base::generateGlyphPath.
365 Simplify(*path, path);
366 AsWinding(*path, path);
367 }
368 return true;
369 }
370
371protected:
373 using value_type = uint16_t;
374 static const constexpr value_type PATH = 1;
375 static const constexpr value_type COLRv0 = 2;
376 static const constexpr value_type COLRv1 = 3;
377 static const constexpr value_type BITMAP = 4;
378 };
379
381 GlyphMetrics mx(glyph.maskFormat());
382
384 SkMatrix remainingMatrix;
387 return mx;
388 }
389 float x_advance = 0.0f;
390 x_advance = fontations_ffi::advance_width_or_zero(
391 fBridgeFontRef, scale.y(), fBridgeNormalizedCoords, glyph.getGlyphID());
392 // TODO(drott): y-advance?
393 mx.advance = remainingMatrix.mapXY(x_advance, SkFloatToScalar(0.f));
394
395 // The FreeType backend has a big switch here:
396 // Scalable or bitmap, monochromatic or color, subpixel shifting bounds if needed.
397 // For now: check if COLRv1, get clipbox, else -
398 // get bounds from Path.
399 // TODO(drott): Later move bounds retrieval for monochromatic glyphs to retrieving
400 // them from Skrifa scaler, taking hinting into account.
401
402 bool has_colrv1_glyph =
403 fontations_ffi::has_colrv1_glyph(fBridgeFontRef, glyph.getGlyphID());
404 bool has_colrv0_glyph =
405 fontations_ffi::has_colrv0_glyph(fBridgeFontRef, glyph.getGlyphID());
406 bool has_bitmap_glyph =
407 fontations_ffi::has_bitmap_glyph(fBridgeFontRef, glyph.getGlyphID());
408
409 if (has_colrv1_glyph || has_colrv0_glyph) {
412 mx.neverRequestPath = true;
413
414 fontations_ffi::ClipBox clipBox;
415 if (has_colrv1_glyph && fontations_ffi::get_colrv1_clip_box(fBridgeFontRef,
416 fBridgeNormalizedCoords,
417 glyph.getGlyphID(),
418 scale.y(),
419 clipBox)) {
420 // Flip y.
421 SkRect boundsRect = SkRect::MakeLTRB(
422 clipBox.x_min, -clipBox.y_max, clipBox.x_max, -clipBox.y_min);
423
424 if (!remainingMatrix.isIdentity()) {
425 SkPath boundsPath = SkPath::Rect(boundsRect);
426 boundsPath.transform(remainingMatrix);
427 boundsRect = boundsPath.getBounds();
428 }
429
430 boundsRect.roundOut(&mx.bounds);
431
432 } else {
433 uint16_t upem = fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
434 if (upem == 0) {
436 } else {
437 SkMatrix fullTransform;
438 fRec.getSingleMatrix(&fullTransform);
439 fullTransform.preScale(1.f / upem, 1.f / upem);
440
441 sk_fontations::BoundsPainter boundsPainter(*this, fullTransform, upem);
442 bool result = fontations_ffi::draw_colr_glyph(fBridgeFontRef,
443 fBridgeNormalizedCoords,
444 glyph.getGlyphID(),
445 boundsPainter);
446 if (result) {
447 boundsPainter.getBoundingBox().roundOut(&mx.bounds);
448 } else {
450 }
451 }
452 }
453 } else if (has_bitmap_glyph) {
455 mx.neverRequestPath = true;
457
458 rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
459 fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), scale.fY);
460 rust::cxxbridge1::Slice<const uint8_t> png_data =
461 fontations_ffi::png_data(*bitmap_glyph);
462 SkASSERT(png_data.size());
463
464 const fontations_ffi::BitmapMetrics bitmapMetrics =
465 fontations_ffi::bitmap_metrics(*bitmap_glyph);
466
467 std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
468 SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
469 if (!codec) {
470 return mx;
471 }
472
473 SkImageInfo info = codec->getInfo();
474
475 SkRect bounds = SkRect::Make(info.bounds());
476 SkMatrix matrix = remainingMatrix;
477
478 // We deal with two scale factors here: Scaling from font units to
479 // device pixels, and scaling the embedded PNG from its number of
480 // rows to a specific size, depending on the ppem values in the
481 // bitmap glyph information.
482 SkScalar imageToSize = scale.fY / bitmapMetrics.ppem_y;
483 float fontUnitsToSize = scale.fY / fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
484
485 // Relevant for sbix only: Convert the potential restrictive size
486 // bounds back to unscaled pixels so that it can be intersected with
487 // the decompressed pixel size of the sbix image.
488 if (!std::isinf(bitmapMetrics.width) && !std::isinf(bitmapMetrics.height)) {
489 SkRect modifiedBounds =
491 0,
492 bitmapMetrics.width * fontUnitsToSize / imageToSize,
493 bitmapMetrics.height * fontUnitsToSize / imageToSize);
494 if (modifiedBounds.isEmpty()) {
495 bounds = SkRect::MakeEmpty();
496 } else {
497 bounds = modifiedBounds;
498 }
499 }
500
501 // The offset from origin is given in font units, so requires a
502 // different scale factor than the scaling of the image.
503 matrix.preTranslate(bitmapMetrics.bearing_x * fontUnitsToSize,
504 -bitmapMetrics.bearing_y * fontUnitsToSize);
505
506 matrix.preScale(imageToSize, imageToSize);
507 // For sbix bitmap glyphs, the origin is the bottom left of the image.
508 float heightAdjustment =
509 bitmapMetrics.placement_origin_bottom_left ? bounds.height() : 0;
510 matrix.preTranslate(0, -heightAdjustment);
511
512 if (this->isSubpixel()) {
515 }
516 matrix.mapRect(&bounds);
517 mx.bounds = SkRect::Make(bounds.roundOut());
518 } else {
519 // TODO: Retrieve from read_fonts and Skrifa - TrueType bbox or from path with
520 // hinting?
522 mx.computeFromPath = true;
523 }
524 return mx;
525 }
526
527 void generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
529 SkBitmap dstBitmap;
530 dstBitmap.setInfo(
532 glyph.width(), glyph.height(), kN32_SkColorType, kPremul_SkAlphaType),
533 glyph.rowBytes());
534 dstBitmap.setPixels(imageBuffer);
535
536 SkCanvas canvas(dstBitmap);
537
538 canvas.translate(-glyph.left(), -glyph.top());
539
541 SkMatrix remainingMatrix;
544 return;
545 }
546
547 rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
548 fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), scale.fY);
549 rust::cxxbridge1::Slice<const uint8_t> png_data = fontations_ffi::png_data(*bitmap_glyph);
550 SkASSERT(png_data.size());
551
552 std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
553 SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
554
555 if (!codec) {
556 return;
557 }
558
559 auto [glyph_image, result] = codec->getImage();
561 return;
562 }
563
565 canvas.concat(remainingMatrix);
566
567 if (this->isSubpixel()) {
568 canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
570 }
571 const fontations_ffi::BitmapMetrics bitmapMetrics =
572 fontations_ffi::bitmap_metrics(*bitmap_glyph);
573
574 // We need two different scale factors here, one for font units to size,
575 // one for scaling the embedded PNG, see generateMetrics() for details.
576 SkScalar imageScaleFactor = scale.fY / bitmapMetrics.ppem_y;
577
578 float fontUnitsToSize = scale.fY / fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
579 canvas.translate(bitmapMetrics.bearing_x * fontUnitsToSize,
580 -bitmapMetrics.bearing_y * fontUnitsToSize);
581 canvas.scale(imageScaleFactor, imageScaleFactor);
582 canvas.translate(bitmapMetrics.inner_bearing_x, -bitmapMetrics.inner_bearing_y);
583
584 float heightAdjustment =
585 bitmapMetrics.placement_origin_bottom_left ? glyph_image->height() : 0;
586
587 canvas.translate(0, -heightAdjustment);
588
590 canvas.drawImage(glyph_image, 0, 0, sampling);
591 }
592
593 void generateImage(const SkGlyph& glyph, void* imageBuffer) override {
596 const SkPath* devPath = glyph.path();
597 SkASSERT_RELEASE(devPath);
598 SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer),
599 glyph.iRect(),
600 glyph.rowBytes(),
601 glyph.maskFormat());
606 const bool hairline = glyph.pathIsHairline();
607 GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline);
608
611 SkBitmap dstBitmap;
612 dstBitmap.setInfo(
614 glyph.width(), glyph.height(), kN32_SkColorType, kPremul_SkAlphaType),
615 glyph.rowBytes());
616 dstBitmap.setPixels(imageBuffer);
617
618 SkCanvas canvas(dstBitmap);
619 if constexpr (kSkShowTextBlitCoverage) {
620 canvas.clear(0x33FF0000);
621 } else {
623 }
624 canvas.translate(-glyph.left(), -glyph.top());
625
626 drawCOLRGlyph(glyph, fRec.fForegroundColor, &canvas);
627 } else if (format == ScalerContextBits::BITMAP) {
628 generatePngImage(glyph, imageBuffer);
629 } else {
630 SK_ABORT("Bad format");
631 }
632 }
633
634 bool generatePath(const SkGlyph& glyph, SkPath* path) override {
636
638 SkMatrix remainingMatrix;
641 return false;
642 }
643 bool result = generateYScalePathForGlyphId(glyph.getGlyphID(), path, scale.y());
644 if (!result) {
645 return false;
646 }
647
648 *path = path->makeTransform(remainingMatrix);
649 return true;
650 }
651
652 bool drawCOLRGlyph(const SkGlyph& glyph, SkColor foregroundColor, SkCanvas* canvas) {
653 uint16_t upem = fontations_ffi::units_per_em_or_zero(fBridgeFontRef);
654 if (upem == 0) {
655 return false;
656 }
657
658 SkMatrix scalerMatrix;
659 fRec.getSingleMatrix(&scalerMatrix);
660 SkAutoCanvasRestore autoRestore(canvas, true /* doSave */);
661
662 // Scale down so that COLR operations can happen in glyph coordinates.
663 SkMatrix upemToPpem = SkMatrix::Scale(1.f / upem, 1.f / upem);
664 scalerMatrix.preConcat(upemToPpem);
665 canvas->concat(scalerMatrix);
666 SkPaint defaultPaint;
667 defaultPaint.setColor(SK_ColorRED);
668 sk_fontations::ColorPainter colorPainter(*this, *canvas, fPalette, foregroundColor,
670 bool result = fontations_ffi::draw_colr_glyph(
671 fBridgeFontRef, fBridgeNormalizedCoords, glyph.getGlyphID(), colorPainter);
672 return result;
673 }
674
676 struct GlyphDrawable : public SkDrawable {
678 SkGlyph fGlyph;
679 GlyphDrawable(SkFontationsScalerContext* self, const SkGlyph& glyph)
680 : fSelf(self), fGlyph(glyph) {}
681 SkRect onGetBounds() override { return fGlyph.rect(); }
682 size_t onApproximateBytesUsed() override { return sizeof(GlyphDrawable); }
683 void maybeShowTextBlitCoverage(SkCanvas* canvas) {
684 if constexpr (kSkShowTextBlitCoverage) {
686 paint.setColor(0x3300FF00);
687 paint.setStyle(SkPaint::kFill_Style);
688 canvas->drawRect(this->onGetBounds(), paint);
689 }
690 }
691 };
692 struct ColrGlyphDrawable : public GlyphDrawable {
693 using GlyphDrawable::GlyphDrawable;
694 void onDraw(SkCanvas* canvas) override {
695 this->maybeShowTextBlitCoverage(canvas);
696 fSelf->drawCOLRGlyph(fGlyph, fSelf->fRec.fForegroundColor, canvas);
697 }
698 };
701 return sk_sp<SkDrawable>(new ColrGlyphDrawable(this, glyph));
702 }
703 return nullptr;
704 }
705
706 void generateFontMetrics(SkFontMetrics* out_metrics) override {
708 SkMatrix remainingMatrix;
711 fontations_ffi::Metrics metrics =
712 fontations_ffi::get_skia_metrics(fBridgeFontRef, scale.fY, fBridgeNormalizedCoords);
713 out_metrics->fTop = -metrics.top;
714 out_metrics->fAscent = -metrics.ascent;
715 out_metrics->fDescent = -metrics.descent;
716 out_metrics->fBottom = -metrics.bottom;
717 out_metrics->fLeading = metrics.leading;
718 out_metrics->fAvgCharWidth = metrics.avg_char_width;
719 out_metrics->fMaxCharWidth = metrics.max_char_width;
720 out_metrics->fXMin = metrics.x_min;
721 out_metrics->fXMax = metrics.x_max;
722 out_metrics->fXHeight = -metrics.x_height;
723 out_metrics->fCapHeight = -metrics.cap_height;
724 out_metrics->fFlags = 0;
725 if (fontations_ffi::table_data(fBridgeFontRef,
726 SkSetFourByteTag('f', 'v', 'a', 'r'),
727 0,
728 rust::Slice<uint8_t>())) {
730 }
731 auto setMetric = [](float& dstMetric, const float srcMetric,
733 {
734 if (std::isnan(srcMetric)) {
735 dstMetric = 0;
736 } else {
737 dstMetric = srcMetric;
738 flags |= flag;
739 }
740 };
741 setMetric(out_metrics->fUnderlinePosition, -metrics.underline_position,
743 setMetric(out_metrics->fUnderlineThickness, metrics.underline_thickness,
745
746 setMetric(out_metrics->fStrikeoutPosition, -metrics.strikeout_position,
748 setMetric(out_metrics->fStrikeoutThickness, metrics.strikeout_thickness,
750 }
751
752private:
753 SkMatrix fMatrix;
754 sk_sp<SkData> fFontData = nullptr;
755 const fontations_ffi::BridgeFontRef& fBridgeFontRef;
756 const fontations_ffi::BridgeNormalizedCoords& fBridgeNormalizedCoords;
757 const fontations_ffi::BridgeOutlineCollection& fOutlines;
758 const SkSpan<SkColor> fPalette;
760};
761
762std::unique_ptr<SkStreamAsset> SkTypeface_Fontations::onOpenStream(int* ttcIndex) const {
763 *ttcIndex = fTtcIndex;
764 return std::make_unique<SkMemoryStream>(fFontData);
765}
766
768 // Matching DWrite implementation, return self if ttc index mismatches.
769 if (fTtcIndex != SkTo<uint32_t>(args.getCollectionIndex())) {
770 return sk_ref_sp(this);
771 }
772
773 int numAxes = onGetVariationDesignPosition(nullptr, 0);
774 auto fusedDesignPosition =
775 std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>(numAxes);
776 int retrievedAxes = onGetVariationDesignPosition(fusedDesignPosition.get(), numAxes);
777 if (numAxes != retrievedAxes) {
778 return nullptr;
779 }
780
781 // We know the internally retrieved axes are normalized, contain a value for every possible
782 // axis, other axes do not exist, so we only need to override any of those.
783 for (int i = 0; i < numAxes; ++i) {
784 const SkFontArguments::VariationPosition& argPosition = args.getVariationDesignPosition();
785 for (int j = 0; j < argPosition.coordinateCount; ++j) {
786 if (fusedDesignPosition[i].axis == argPosition.coordinates[j].axis) {
787 fusedDesignPosition[i].value = argPosition.coordinates[j].value;
788 }
789 }
790 }
791
792 SkFontArguments fusedArgs;
793 fusedArgs.setVariationDesignPosition({fusedDesignPosition.get(), SkToInt(numAxes)});
794 fusedArgs.setPalette(args.getPalette());
795
796 rust::cxxbridge1::Box<fontations_ffi::BridgeNormalizedCoords> normalized_args =
797 make_normalized_coords(*fBridgeFontRef, fusedArgs.getVariationDesignPosition());
798
799 if (!fontations_ffi::normalized_coords_equal(*normalized_args, *fBridgeNormalizedCoords)) {
800 return MakeFromData(fFontData, fusedArgs);
801 }
802
803 // TODO(crbug.com/skia/330149870): Palette differences are not fused, see DWrite backend impl.
804 rust::Slice<const fontations_ffi::PaletteOverride> argPaletteOverrides(
805 reinterpret_cast<const fontations_ffi::PaletteOverride*>(args.getPalette().overrides),
806 args.getPalette().overrideCount);
807 rust::Vec<uint32_t> newPalette =
808 resolve_palette(*fBridgeFontRef, args.getPalette().index, argPaletteOverrides);
809
810 if (fPalette.size() != newPalette.size() ||
811 memcmp(fPalette.data(), newPalette.data(), fPalette.size() * sizeof(fPalette[0]))) {
812 return MakeFromData(fFontData, fusedArgs);
813 }
814
815 return sk_ref_sp(this);
816}
817
818std::unique_ptr<SkScalerContext> SkTypeface_Fontations::onCreateScalerContext(
819 const SkScalerContextEffects& effects, const SkDescriptor* desc) const {
820 return std::make_unique<SkFontationsScalerContext>(
821 sk_ref_sp(const_cast<SkTypeface_Fontations*>(this)), effects, desc);
822}
823
824std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Fontations::onGetAdvancedMetrics() const {
825 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
826
827 if (!fontations_ffi::is_embeddable(*fBridgeFontRef)) {
829 }
830
831 if (!fontations_ffi::is_subsettable(*fBridgeFontRef)) {
833 }
834
835 if (fontations_ffi::table_data(
836 *fBridgeFontRef, SkSetFourByteTag('f', 'v', 'a', 'r'), 0, rust::Slice<uint8_t>())) {
838 }
839
840 // Metrics information.
841 fontations_ffi::Metrics metrics =
842 fontations_ffi::get_unscaled_metrics(*fBridgeFontRef, *fBridgeNormalizedCoords);
843 info->fAscent = metrics.ascent;
844 info->fDescent = metrics.descent;
845 info->fCapHeight = metrics.cap_height;
846
847 info->fBBox = SkIRect::MakeLTRB((int32_t)metrics.x_min,
848 (int32_t)metrics.top,
849 (int32_t)metrics.x_max,
850 (int32_t)metrics.bottom);
851
852 // Style information.
853 if (fontations_ffi::is_fixed_pitch(*fBridgeFontRef)) {
855 }
856
857 fontations_ffi::BridgeFontStyle fontStyle;
858 if (fontations_ffi::get_font_style(*fBridgeFontRef, fontStyle)) {
861 }
862 }
863
864 if (fontations_ffi::is_serif_style(*fBridgeFontRef)) {
866 } else if (fontations_ffi::is_script_style(*fBridgeFontRef)) {
868 }
869
870 info->fItalicAngle = fontations_ffi::italic_angle(*fBridgeFontRef);
871
872 return info;
873}
874
876 SkString familyName;
877 onGetFamilyName(&familyName);
878 desc->setFamilyName(familyName.c_str());
879 desc->setStyle(this->fontStyle());
880 desc->setFactoryId(FactoryId);
881 *serialize = true;
882}
883
885 size_t offset,
886 size_t length,
887 void* data) const {
888 rust::Slice<uint8_t> dataSlice;
889 if (data) {
890 dataSlice = rust::Slice<uint8_t>(reinterpret_cast<uint8_t*>(data), length);
891 }
892 size_t copied = fontations_ffi::table_data(*fBridgeFontRef, tag, offset, dataSlice);
893 // If data is nullptr, the Rust side doesn't see a length limit.
894 return std::min(copied, length);
895}
896
898 uint16_t numTables = fontations_ffi::table_tags(*fBridgeFontRef, rust::Slice<uint32_t>());
899 if (!tags) {
900 return numTables;
901 }
902 rust::Slice<uint32_t> copyToTags(tags, numTables);
903 return fontations_ffi::table_tags(*fBridgeFontRef, copyToTags);
904}
905
907 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const {
908 rust::Slice<fontations_ffi::SkiaDesignCoordinate> copyToCoordinates;
909 if (coordinates) {
910 copyToCoordinates = rust::Slice<fontations_ffi::SkiaDesignCoordinate>(
911 reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(coordinates),
912 coordinateCount);
913 }
914 return fontations_ffi::variation_position(*fBridgeNormalizedCoords, copyToCoordinates);
915}
916
918 SkFontParameters::Variation::Axis parameters[], int parameterCount) const {
919 sk_fontations::AxisWrapper axisWrapper(parameters, parameterCount);
920 return fontations_ffi::populate_axes(*fBridgeFontRef, axisWrapper);
921}
922
923namespace sk_fontations {
924
925namespace {
926
927const uint16_t kForegroundColorPaletteIndex = 0xFFFF;
928
929void populateStopsAndColors(std::vector<SkScalar>& dest_stops,
930 std::vector<SkColor4f>& dest_colors,
931 const SkSpan<SkColor>& palette,
932 SkColor foregroundColor,
933 fontations_ffi::BridgeColorStops& color_stops) {
934 SkASSERT(dest_stops.size() == 0);
935 SkASSERT(dest_colors.size() == 0);
936 size_t num_color_stops = fontations_ffi::num_color_stops(color_stops);
937 dest_stops.reserve(num_color_stops);
938 dest_colors.reserve(num_color_stops);
939
940 fontations_ffi::ColorStop color_stop;
941 while (fontations_ffi::next_color_stop(color_stops, color_stop)) {
942 dest_stops.push_back(color_stop.stop);
943 SkColor4f dest_color;
944 if (color_stop.palette_index == kForegroundColorPaletteIndex) {
945 dest_color = SkColor4f::FromColor(foregroundColor);
946 } else {
947 dest_color = SkColor4f::FromColor(palette[color_stop.palette_index]);
948 }
949 dest_color.fA *= color_stop.alpha;
950 dest_colors.push_back(dest_color);
951 }
952}
953
954SkColor4f lerpSkColor(SkColor4f c0, SkColor4f c1, float t) {
955 // Due to the floating point calculation in the caller, when interpolating between very
956 // narrow stops, we may get values outside the interpolation range, guard against these.
957 if (t < 0) {
958 return c0;
959 }
960 if (t > 1) {
961 return c1;
962 }
963
964 const auto c0_4f = skvx::float4::Load(c0.vec());
965 const auto c1_4f = skvx::float4::Load(c1.vec());
966 const auto c_4f = c0_4f + (c1_4f - c0_4f) * t;
967
968 SkColor4f l;
969 c_4f.store(l.vec());
970 return l;
971}
972
973enum TruncateStops { TruncateStart, TruncateEnd };
974
975// Truncate a vector of color stops at a previously computed stop position and insert at that
976// position the color interpolated between the surrounding stops.
977void truncateToStopInterpolating(SkScalar zeroRadiusStop,
978 std::vector<SkColor4f>& colors,
979 std::vector<SkScalar>& stops,
980 TruncateStops truncateStops) {
981 if (stops.size() <= 1u || zeroRadiusStop < stops.front() || stops.back() < zeroRadiusStop) {
982 return;
983 }
984
985 size_t afterIndex =
986 (truncateStops == TruncateStart)
987 ? std::lower_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin()
988 : std::upper_bound(stops.begin(), stops.end(), zeroRadiusStop) - stops.begin();
989
990 const float t =
991 (zeroRadiusStop - stops[afterIndex - 1]) / (stops[afterIndex] - stops[afterIndex - 1]);
992 SkColor4f lerpColor = lerpSkColor(colors[afterIndex - 1], colors[afterIndex], t);
993
994 if (truncateStops == TruncateStart) {
995 stops.erase(stops.begin(), stops.begin() + afterIndex);
996 colors.erase(colors.begin(), colors.begin() + afterIndex);
997 stops.insert(stops.begin(), 0);
998 colors.insert(colors.begin(), lerpColor);
999 } else {
1000 stops.erase(stops.begin() + afterIndex, stops.end());
1001 colors.erase(colors.begin() + afterIndex, colors.end());
1002 stops.insert(stops.end(), 1);
1003 colors.insert(colors.end(), lerpColor);
1004 }
1005}
1006
1007// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite
1008inline SkBlendMode ToSkBlendMode(uint16_t colrV1CompositeMode) {
1009 switch (colrV1CompositeMode) {
1010 case 0:
1011 return SkBlendMode::kClear;
1012 case 1:
1013 return SkBlendMode::kSrc;
1014 case 2:
1015 return SkBlendMode::kDst;
1016 case 3:
1017 return SkBlendMode::kSrcOver;
1018 case 4:
1019 return SkBlendMode::kDstOver;
1020 case 5:
1021 return SkBlendMode::kSrcIn;
1022 case 6:
1023 return SkBlendMode::kDstIn;
1024 case 7:
1025 return SkBlendMode::kSrcOut;
1026 case 8:
1027 return SkBlendMode::kDstOut;
1028 case 9:
1029 return SkBlendMode::kSrcATop;
1030 case 10:
1031 return SkBlendMode::kDstATop;
1032 case 11:
1033 return SkBlendMode::kXor;
1034 case 12:
1035 return SkBlendMode::kPlus;
1036 case 13:
1037 return SkBlendMode::kScreen;
1038 case 14:
1039 return SkBlendMode::kOverlay;
1040 case 15:
1041 return SkBlendMode::kDarken;
1042 case 16:
1043 return SkBlendMode::kLighten;
1044 case 17:
1046 case 18:
1048 case 19:
1050 case 20:
1052 case 21:
1054 case 22:
1056 case 23:
1058 case 24:
1059 return SkBlendMode::kHue;
1060 case 25:
1062 case 26:
1063 return SkBlendMode::kColor;
1064 case 27:
1066 default:
1067 return SkBlendMode::kDst;
1068 }
1069}
1070
1071inline SkTileMode ToSkTileMode(uint8_t extendMode) {
1072 switch (extendMode) {
1073 case 1:
1074 return SkTileMode::kRepeat;
1075 case 2:
1076 return SkTileMode::kMirror;
1077 default:
1078 return SkTileMode::kClamp;
1079 }
1080}
1081} // namespace
1082
1084 SkCanvas& canvas,
1085 SkSpan<SkColor> palette,
1086 SkColor foregroundColor,
1087 bool antialias,
1088 uint16_t upem)
1089 : fScalerContext(scaler_context)
1090 , fCanvas(canvas)
1091 , fPalette(palette)
1092 , fForegroundColor(foregroundColor)
1093 , fAntialias(antialias)
1094 , fUpem(upem) {}
1095
1096void ColorPainter::push_transform(const fontations_ffi::Transform& transform_arg) {
1097 fCanvas.save();
1098 fCanvas.concat(SkMatrixFromFontationsTransform(transform_arg));
1099}
1100
1102
1103void ColorPainter::push_clip_glyph(uint16_t glyph_id) {
1104 fCanvas.save();
1105 SkPath path;
1106 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1107 fCanvas.clipPath(path, fAntialias);
1108}
1109
1110void ColorPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) {
1111 fCanvas.save();
1112 SkRect clipRect = SkRect::MakeLTRB(x_min, -y_min, x_max, -y_max);
1113 fCanvas.clipRect(clipRect, fAntialias);
1114}
1115
1116void ColorPainter::pop_clip() { fCanvas.restore(); }
1117
1118void ColorPainter::configure_solid_paint(uint16_t palette_index, float alpha, SkPaint& paint) {
1119 paint.setAntiAlias(fAntialias);
1121 if (palette_index == kForegroundColorPaletteIndex) {
1122 color = SkColor4f::FromColor(fForegroundColor);
1123 } else {
1124 color = SkColor4f::FromColor(fPalette[palette_index]);
1125 }
1126 color.fA *= alpha;
1127 paint.setShader(nullptr);
1128 paint.setColor(color);
1129}
1130
1131void ColorPainter::fill_solid(uint16_t palette_index, float alpha) {
1132 SkPaint paint;
1133 configure_solid_paint(palette_index, alpha, paint);
1134 fCanvas.drawPaint(paint);
1135}
1136
1137void ColorPainter::fill_glyph_solid(uint16_t glyph_id, uint16_t palette_index, float alpha) {
1138 SkPath path;
1139 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1140
1141 SkPaint paint;
1142 configure_solid_paint(palette_index, alpha, paint);
1143 fCanvas.drawPath(path, paint);
1144}
1145
1146void ColorPainter::configure_linear_paint(const fontations_ffi::FillLinearParams& linear_params,
1147 fontations_ffi::BridgeColorStops& bridge_stops,
1148 uint8_t extend_mode,
1149 SkPaint& paint,
1150 SkMatrix* paintTransform) {
1151 paint.setAntiAlias(fAntialias);
1152
1153 std::vector<SkScalar> stops;
1154 std::vector<SkColor4f> colors;
1155
1156 populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1157
1158 if (stops.size() == 1) {
1159 paint.setColor(colors[0]);
1160 return;
1161 }
1162
1163 SkPoint linePositions[2] = {
1164 SkPoint::Make(SkFloatToScalar(linear_params.x0), -SkFloatToScalar(linear_params.y0)),
1165 SkPoint::Make(SkFloatToScalar(linear_params.x1), -SkFloatToScalar(linear_params.y1))};
1166 SkTileMode tileMode = ToSkTileMode(extend_mode);
1167
1169 linePositions,
1170 colors.data(),
1172 stops.data(),
1173 stops.size(),
1174 tileMode,
1175 SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1176 SkGradientShader::Interpolation::ColorSpace::kSRGB,
1177 SkGradientShader::Interpolation::HueMethod::kShorter},
1178 paintTransform));
1179
1180 SkASSERT(shader);
1181 // An opaque color is needed to ensure the gradient is not modulated by alpha.
1182 paint.setColor(SK_ColorBLACK);
1183 paint.setShader(shader);
1184}
1185
1186void ColorPainter::fill_linear(const fontations_ffi::FillLinearParams& linear_params,
1187 fontations_ffi::BridgeColorStops& bridge_stops,
1188 uint8_t extend_mode) {
1189 SkPaint paint;
1190
1191 configure_linear_paint(linear_params, bridge_stops, extend_mode, paint);
1192
1193 fCanvas.drawPaint(paint);
1194}
1195
1196void ColorPainter::fill_glyph_linear(uint16_t glyph_id,
1197 const fontations_ffi::Transform& transform,
1198 const fontations_ffi::FillLinearParams& linear_params,
1199 fontations_ffi::BridgeColorStops& bridge_stops,
1200 uint8_t extend_mode) {
1201 SkPath path;
1202 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1203
1204 SkPaint paint;
1205 SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1206 configure_linear_paint(linear_params, bridge_stops, extend_mode, paint, &paintTransform);
1207 fCanvas.drawPath(path, paint);
1208}
1209
1210void ColorPainter::configure_radial_paint(
1211 const fontations_ffi::FillRadialParams& fill_radial_params,
1212 fontations_ffi::BridgeColorStops& bridge_stops,
1213 uint8_t extend_mode,
1214 SkPaint& paint,
1215 SkMatrix* paintTransform) {
1216 paint.setAntiAlias(fAntialias);
1217
1218 SkPoint start = SkPoint::Make(fill_radial_params.x0, -fill_radial_params.y0);
1219 SkPoint end = SkPoint::Make(fill_radial_params.x1, -fill_radial_params.y1);
1220
1221 float startRadius = fill_radial_params.r0;
1222 float endRadius = fill_radial_params.r1;
1223
1224 std::vector<SkScalar> stops;
1225 std::vector<SkColor4f> colors;
1226
1227 populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1228
1229 // Draw single color if there's only one stop.
1230 if (stops.size() == 1) {
1231 paint.setColor(colors[0]);
1232 fCanvas.drawPaint(paint);
1233 return;
1234 }
1235
1236 SkTileMode tileMode = ToSkTileMode(extend_mode);
1237
1238 // For negative radii, interpolation is needed to prepare parameters suitable
1239 // for invoking the shader. Implementation below as resolution discussed in
1240 // https://github.com/googlefonts/colr-gradients-spec/issues/367.
1241 // Truncate to manually interpolated color for tile mode clamp, otherwise
1242 // calculate positive projected circles.
1243 if (startRadius < 0 || endRadius < 0) {
1244 if (startRadius == endRadius && startRadius < 0) {
1245 paint.setColor(SK_ColorTRANSPARENT);
1246 // return true;
1247 return;
1248 }
1249
1250 if (tileMode == SkTileMode::kClamp) {
1251 SkVector startToEnd = end - start;
1252 SkScalar radiusDiff = endRadius - startRadius;
1253 SkScalar zeroRadiusStop = 0.f;
1254 TruncateStops truncateSide = TruncateStart;
1255 if (startRadius < 0) {
1256 truncateSide = TruncateStart;
1257
1258 // Compute color stop position where radius is = 0. After the scaling
1259 // of stop positions to the normal 0,1 range that we have done above,
1260 // the size of the radius as a function of the color stops is: r(x) = r0
1261 // + x*(r1-r0) Solving this function for r(x) = 0, we get: x = -r0 /
1262 // (r1-r0)
1263 zeroRadiusStop = -startRadius / (endRadius - startRadius);
1264 startRadius = 0.f;
1265 SkVector startEndDiff = end - start;
1266 startEndDiff.scale(zeroRadiusStop);
1267 start = start + startEndDiff;
1268 }
1269
1270 if (endRadius < 0) {
1271 truncateSide = TruncateEnd;
1272 zeroRadiusStop = -startRadius / (endRadius - startRadius);
1273 endRadius = 0.f;
1274 SkVector startEndDiff = end - start;
1275 startEndDiff.scale(1 - zeroRadiusStop);
1276 end = end - startEndDiff;
1277 }
1278
1279 if (!(startRadius == 0 && endRadius == 0)) {
1280 truncateToStopInterpolating(zeroRadiusStop, colors, stops, truncateSide);
1281 } else {
1282 // If both radii have become negative and where clamped to 0, we need to
1283 // produce a single color cone, otherwise the shader colors the whole
1284 // plane in a single color when two radii are specified as 0.
1285 if (radiusDiff > 0) {
1286 end = start + startToEnd;
1287 endRadius = radiusDiff;
1288 colors.erase(colors.begin(), colors.end() - 1);
1289 stops.erase(stops.begin(), stops.end() - 1);
1290 } else {
1291 start -= startToEnd;
1292 startRadius = -radiusDiff;
1293 colors.erase(colors.begin() + 1, colors.end());
1294 stops.erase(stops.begin() + 1, stops.end());
1295 }
1296 }
1297 } else {
1298 if (startRadius < 0 || endRadius < 0) {
1299 auto roundIntegerMultiple = [](SkScalar factorZeroCrossing, SkTileMode tileMode) {
1300 int roundedMultiple = factorZeroCrossing > 0 ? ceilf(factorZeroCrossing)
1301 : floorf(factorZeroCrossing) - 1;
1302 if (tileMode == SkTileMode::kMirror && roundedMultiple % 2 != 0) {
1303 roundedMultiple += roundedMultiple < 0 ? -1 : 1;
1304 }
1305 return roundedMultiple;
1306 };
1307
1308 SkVector startToEnd = end - start;
1309 SkScalar radiusDiff = endRadius - startRadius;
1310 SkScalar factorZeroCrossing = (startRadius / (startRadius - endRadius));
1311 bool inRange = 0.f <= factorZeroCrossing && factorZeroCrossing <= 1.0f;
1312 SkScalar direction = inRange && radiusDiff < 0 ? -1.0f : 1.0f;
1313 SkScalar circleProjectionFactor =
1314 roundIntegerMultiple(factorZeroCrossing * direction, tileMode);
1315 startToEnd.scale(circleProjectionFactor);
1316 startRadius += circleProjectionFactor * radiusDiff;
1317 endRadius += circleProjectionFactor * radiusDiff;
1318 start += startToEnd;
1319 end += startToEnd;
1320 }
1321 }
1322 }
1323
1324 // An opaque color is needed to ensure the gradient is not modulated by alpha.
1325 paint.setColor(SK_ColorBLACK);
1326
1328 start,
1329 startRadius,
1330 end,
1331 endRadius,
1332 colors.data(),
1334 stops.data(),
1335 stops.size(),
1336 tileMode,
1337 SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1338 SkGradientShader::Interpolation::ColorSpace::kSRGB,
1339 SkGradientShader::Interpolation::HueMethod::kShorter},
1340 paintTransform));
1341}
1342
1343void ColorPainter::fill_radial(const fontations_ffi::FillRadialParams& fill_radial_params,
1344 fontations_ffi::BridgeColorStops& bridge_stops,
1345 uint8_t extend_mode) {
1346 SkPaint paint;
1347
1348 configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint);
1349
1350 fCanvas.drawPaint(paint);
1351}
1352
1353void ColorPainter::fill_glyph_radial(uint16_t glyph_id,
1354 const fontations_ffi::Transform& transform,
1355 const fontations_ffi::FillRadialParams& fill_radial_params,
1356 fontations_ffi::BridgeColorStops& bridge_stops,
1357 uint8_t extend_mode) {
1358 SkPath path;
1359 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1360
1361 SkPaint paint;
1362 SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1363 configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint, &paintTransform);
1364 fCanvas.drawPath(path, paint);
1365}
1366
1367void ColorPainter::configure_sweep_paint(const fontations_ffi::FillSweepParams& sweep_params,
1368 fontations_ffi::BridgeColorStops& bridge_stops,
1369 uint8_t extend_mode,
1370 SkPaint& paint,
1371 SkMatrix* paintTransform) {
1372 paint.setAntiAlias(fAntialias);
1373
1374 SkPoint center = SkPoint::Make(sweep_params.x0, -sweep_params.y0);
1375
1376 std::vector<SkScalar> stops;
1377 std::vector<SkColor4f> colors;
1378
1379 populateStopsAndColors(stops, colors, fPalette, fForegroundColor, bridge_stops);
1380
1381 if (stops.size() == 1) {
1382 paint.setColor(colors[0]);
1383 fCanvas.drawPaint(paint);
1384 return;
1385 }
1386
1387 // An opaque color is needed to ensure the gradient is not modulated by alpha.
1388 paint.setColor(SK_ColorBLACK);
1389 SkTileMode tileMode = ToSkTileMode(extend_mode);
1390
1391 paint.setColor(SK_ColorBLACK);
1393 center.x(),
1394 center.y(),
1395 colors.data(),
1397 stops.data(),
1398 stops.size(),
1399 tileMode,
1400 sweep_params.start_angle,
1401 sweep_params.end_angle,
1402 SkGradientShader::Interpolation{SkGradientShader::Interpolation::InPremul::kNo,
1403 SkGradientShader::Interpolation::ColorSpace::kSRGB,
1404 SkGradientShader::Interpolation::HueMethod::kShorter},
1405 paintTransform));
1406}
1407
1408void ColorPainter::fill_sweep(const fontations_ffi::FillSweepParams& sweep_params,
1409 fontations_ffi::BridgeColorStops& bridge_stops,
1410 uint8_t extend_mode) {
1411 SkPaint paint;
1412
1413 configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint);
1414
1415 fCanvas.drawPaint(paint);
1416}
1417
1418void ColorPainter::fill_glyph_sweep(uint16_t glyph_id,
1419 const fontations_ffi::Transform& transform,
1420 const fontations_ffi::FillSweepParams& sweep_params,
1421 fontations_ffi::BridgeColorStops& bridge_stops,
1422 uint8_t extend_mode) {
1423 SkPath path;
1424 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1425
1426 SkPaint paint;
1427 SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform);
1428 configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint, &paintTransform);
1429 fCanvas.drawPath(path, paint);
1430}
1431
1432void ColorPainter::push_layer(uint8_t compositeMode) {
1433 SkPaint paint;
1434 paint.setBlendMode(ToSkBlendMode(compositeMode));
1435 fCanvas.saveLayer(nullptr, &paint);
1436}
1437
1439
1441 SkMatrix initialTransfom,
1442 uint16_t upem)
1443 : fScalerContext(scaler_context)
1444 , fCurrentTransform(initialTransfom)
1445 , fUpem(upem)
1446 , fBounds(SkRect::MakeEmpty()) {}
1447
1449
1450// fontations_ffi::ColorPainter interface.
1451void BoundsPainter::push_transform(const fontations_ffi::Transform& transform_arg) {
1452 SkMatrix transform = SkMatrix::MakeAll(transform_arg.xx,
1453 -transform_arg.xy,
1454 transform_arg.dx,
1455 -transform_arg.yx,
1456 transform_arg.yy,
1457 -transform_arg.dy,
1458 0.f,
1459 0.f,
1460 1.0f);
1461 fCurrentTransform.preConcat(transform);
1462 bool invertResult = transform.invert(&fStackTopTransformInverse);
1463 SkASSERT(invertResult);
1464}
1466 fCurrentTransform.preConcat(fStackTopTransformInverse);
1467 fStackTopTransformInverse = SkMatrix();
1468}
1469
1470void BoundsPainter::push_clip_glyph(uint16_t glyph_id) {
1471 SkPath path;
1472 fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem);
1473 path.transform(fCurrentTransform);
1474 fBounds.join(path.getBounds());
1475}
1476
1477void BoundsPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) {
1478 SkRect clipRect = SkRect::MakeLTRB(x_min, -y_min, x_max, -y_max);
1479 SkPath rectPath = SkPath::Rect(clipRect);
1480 rectPath.transform(fCurrentTransform);
1481 fBounds.join(rectPath.getBounds());
1482}
1483
1484void BoundsPainter::fill_glyph_solid(uint16_t glyph_id, uint16_t, float) {
1485 push_clip_glyph(glyph_id);
1486 pop_clip();
1487}
1488
1490 const fontations_ffi::Transform&,
1491 const fontations_ffi::FillRadialParams&,
1492 fontations_ffi::BridgeColorStops&,
1493 uint8_t) {
1494 push_clip_glyph(glyph_id);
1495 pop_clip();
1496}
1498 const fontations_ffi::Transform&,
1499 const fontations_ffi::FillLinearParams&,
1500 fontations_ffi::BridgeColorStops&,
1501 uint8_t) {
1502 push_clip_glyph(glyph_id);
1503 pop_clip();
1504}
1505
1506void BoundsPainter::fill_glyph_sweep(uint16_t glyph_id,
1507 const fontations_ffi::Transform&,
1508 const fontations_ffi::FillSweepParams&,
1509 fontations_ffi::BridgeColorStops&,
1510 uint8_t) {
1511 push_clip_glyph(glyph_id);
1512 pop_clip();
1513}
1514
1515} // namespace sk_fontations
SkPath fPath
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
uint16_t glyphs[5]
int count
SkColor4f color
const SkRect fBounds
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT_RELEASE(cond)
Definition SkAssert.h:100
#define SkASSERT(cond)
Definition SkAssert.h:116
SkBlendMode
Definition SkBlendMode.h:38
@ kSrcOut
r = s * (1-da)
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kDstIn
r = d * sa
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kSrcATop
r = s*da + d*(1-sa)
@ kDstATop
r = d*sa + s*(1-da)
@ kDstOver
r = d + (1-da)*s
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDstOut
r = d * (1-sa)
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
@ kSrcIn
r = s * da
@ kClear
r = 0
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
#define SkFixedToScalar(x)
Definition SkFixed.h:124
@ kNone
glyph outlines unchanged
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
bool SK_API AsWinding(const SkPath &path, SkPath *result)
bool SK_API Simplify(const SkPath &path, SkPath *result)
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
#define SkFloatToScalar(x)
Definition SkScalar.h:62
SkTileMode
Definition SkTileMode.h:13
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
constexpr int SkToInt(S x)
Definition SkTo.h:29
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
uint32_t SkFontTableTag
Definition SkTypeface.h:41
sk_sp< SkTypeface > SkTypeface_Make_Fontations(std::unique_ptr< SkStreamAsset > fontData, const SkFontArguments &args)
int32_t SkUnichar
Definition SkTypes.h:175
uint16_t SkGlyphID
Definition SkTypes.h:179
uint32_t SkFourByteTag
Definition SkTypes.h:166
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
static SkScalar center(float pos0, float pos1)
void setPixels(void *pixels)
Definition SkBitmap.cpp:207
bool setInfo(const SkImageInfo &imageInfo, size_t rowBytes=0)
Definition SkBitmap.cpp:114
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition SkCanvas.cpp:500
void drawRect(const SkRect &rect, const SkPaint &paint)
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
void drawPaint(const SkPaint &paint)
void clear(SkColor color)
Definition SkCanvas.h:1199
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
void concat(const SkMatrix &matrix)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
@ kSuccess
Definition SkCodec.h:80
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition SkData.h:116
static sk_sp< SkData > MakeFromStream(SkStream *, size_t size)
Definition SkData.cpp:208
void setFactoryId(SkTypeface::FactoryId factoryId)
Slant slant() const
Definition SkFontStyle.h:64
int width() const
Definition SkFontStyle.h:63
int weight() const
Definition SkFontStyle.h:62
sk_sp< SkDrawable > generateDrawable(const SkGlyph &glyph) override
void generateImage(const SkGlyph &glyph, void *imageBuffer) override
bool drawCOLRGlyph(const SkGlyph &glyph, SkColor foregroundColor, SkCanvas *canvas)
void generatePngImage(const SkGlyph &glyph, void *imageBuffer)
SkFontationsScalerContext(sk_sp< SkTypeface_Fontations > face, const SkScalerContextEffects &effects, const SkDescriptor *desc)
void generateFontMetrics(SkFontMetrics *out_metrics) override
bool generateYScalePathForGlyphId(uint16_t glyphId, SkPath *path, float yScale)
GlyphMetrics generateMetrics(const SkGlyph &glyph, SkArenaAlloc *) override
bool generatePath(const SkGlyph &glyph, SkPath *path) override
int top() const
Definition SkGlyph.h:511
size_t rowBytes() const
Definition SkGlyph.cpp:233
uint16_t extraBits() const
Definition SkGlyph.h:519
SkGlyphID getGlyphID() const
Definition SkGlyph.h:429
const SkPath * path() const
Definition SkGlyph.cpp:284
SkMask::Format maskFormat() const
Definition SkGlyph.h:500
SkRect rect() const
Definition SkGlyph.h:506
bool pathIsHairline() const
Definition SkGlyph.cpp:293
int height() const
Definition SkGlyph.h:513
SkFixed getSubYFixed() const
Definition SkGlyph.h:432
SkFixed getSubXFixed() const
Definition SkGlyph.h:431
SkIRect iRect() const
Definition SkGlyph.h:505
int width() const
Definition SkGlyph.h:512
int left() const
Definition SkGlyph.h:510
static sk_sp< SkShader > MakeTwoPointConical(const SkPoint &start, SkScalar startRadius, const SkPoint &end, SkScalar endRadius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:281
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition SkMatrix.h:179
void mapXY(SkScalar x, SkScalar y, SkPoint *result) const
Definition SkMatrix.cpp:777
SkMatrix & preConcat(const SkMatrix &other)
Definition SkMatrix.cpp:674
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:315
bool isIdentity() const
Definition SkMatrix.h:223
void setColor(SkColor color)
Definition SkPaint.cpp:119
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition SkPath.cpp:3518
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition SkPath.cpp:736
const SkRect & getBounds() const
Definition SkPath.cpp:420
SkPath & cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)
Definition SkPath.cpp:789
SkPath & close()
Definition SkPath.cpp:813
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition SkPath.cpp:1647
SkScalerContextRec fRec
static void GenerateImageFromPath(SkMaskBuilder &dst, const SkPath &path, const SkMaskGamma::PreBlend &maskPreBlend, bool doBGR, bool verticalLCD, bool a8FromLCD, bool hairline)
const SkMaskGamma::PreBlend fPreBlend
SkTypeface * getTypeface() const
bool isSubpixel() const
const char * c_str() const
Definition SkString.h:133
void onGetFontDescriptor(SkFontDescriptor *, bool *) const override
int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const override
static sk_sp< SkTypeface > MakeFromStream(std::unique_ptr< SkStreamAsset >, const SkFontArguments &)
int onCountGlyphs() const override
int onGetUPEM() const override
void onCharsToGlyphs(const SkUnichar *chars, int count, SkGlyphID glyphs[]) const override
std::unique_ptr< SkAdvancedTypefaceMetrics > onGetAdvancedMetrics() const override
bool onGlyphMaskNeedsCurrentColor() const override
int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], int parameterCount) const override
sk_sp< SkTypeface > onMakeClone(const SkFontArguments &args) const override
void onFilterRec(SkScalerContextRec *) const override
int onGetTableTags(SkFontTableTag tags[]) const override
void getGlyphToUnicodeMap(SkUnichar *) const override
std::unique_ptr< SkScalerContext > onCreateScalerContext(const SkScalerContextEffects &effects, const SkDescriptor *desc) const override
void onGetFamilyName(SkString *familyName) const override
static sk_sp< SkTypeface > MakeFromData(sk_sp< SkData > fontData, const SkFontArguments &)
size_t onGetTableData(SkFontTableTag, size_t, size_t, void *) const override
std::unique_ptr< SkStreamAsset > onOpenStream(int *ttcIndex) const override
bool onGetPostScriptName(SkString *) const override
SkTypeface::LocalizedStrings * onCreateFamilyNameIterator() const override
SkFontStyle fontStyle() const
Definition SkTypeface.h:55
void serialize(SkWStream *, SerializeBehavior=SerializeBehavior::kIncludeDataIfLocal) const
size_t getTableSize(SkFontTableTag) const
SkFourByteTag FactoryId
Definition SkTypeface.h:335
SkrifaLocalizedStrings(rust::Box<::fontations_ffi::BridgeLocalizedStrings > bridge_localized_strings)
bool next(SkTypeface::LocalizedString *localized_string) override
bool populate_axis(size_t i, uint32_t axisTag, float min, float def, float max, bool hidden) override
virtual void fill_glyph_radial(uint16_t glyph_id, const fontations_ffi::Transform &, const fontations_ffi::FillRadialParams &, fontations_ffi::BridgeColorStops &, uint8_t) override
virtual void push_transform(const fontations_ffi::Transform &transform) override
virtual void push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) override
virtual void fill_glyph_linear(uint16_t glyph_id, const fontations_ffi::Transform &, const fontations_ffi::FillLinearParams &, fontations_ffi::BridgeColorStops &, uint8_t) override
virtual void fill_glyph_sweep(uint16_t glyph_id, const fontations_ffi::Transform &, const fontations_ffi::FillSweepParams &, fontations_ffi::BridgeColorStops &, uint8_t) override
virtual void fill_glyph_solid(uint16_t glyph_id, uint16_t, float) override
virtual void push_clip_glyph(uint16_t glyph_id) override
virtual void fill_radial(const fontations_ffi::FillRadialParams &fill_radial_params, fontations_ffi::BridgeColorStops &, uint8_t extend_mode) override
virtual void fill_glyph_radial(uint16_t glyph_id, const fontations_ffi::Transform &transform, const fontations_ffi::FillRadialParams &fill_radial_params, fontations_ffi::BridgeColorStops &stops, uint8_t) override
virtual void fill_glyph_linear(uint16_t glyph_id, const fontations_ffi::Transform &transform, const fontations_ffi::FillLinearParams &fill_linear_params, fontations_ffi::BridgeColorStops &stops, uint8_t) override
virtual void fill_solid(uint16_t palette_index, float alpha) override
virtual void fill_glyph_sweep(uint16_t glyph_id, const fontations_ffi::Transform &transform, const fontations_ffi::FillSweepParams &fill_sweep_params, fontations_ffi::BridgeColorStops &stops, uint8_t) override
virtual void push_layer(uint8_t compositeMode) override
virtual void pop_transform() override
virtual void fill_sweep(const fontations_ffi::FillSweepParams &fill_sweep_params, fontations_ffi::BridgeColorStops &, uint8_t extend_mode) override
virtual void fill_linear(const fontations_ffi::FillLinearParams &fill_linear_params, fontations_ffi::BridgeColorStops &, uint8_t extend_mode) override
virtual void fill_glyph_solid(uint16_t glyph_id, uint16_t palette_index, float alpha) override
virtual void push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) override
virtual void push_transform(const fontations_ffi::Transform &transform) override
virtual void push_clip_glyph(uint16_t glyph_id) override
void line_to(float x, float y) override
void move_to(float x, float y) override
void curve_to(float cx0, float cy0, float cx1, float cy1, float x, float y) override
void quad_to(float cx0, float cy0, float x, float y) override
const Paint & paint
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flag
FlutterSemanticsFlag flags
glong glong end
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition hsl.cpp:49
static float min(float r, float g, float b)
Definition hsl.cpp:48
size_t length
double y
double x
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
PODArray< SkColor > colors
Definition SkRecords.h:276
Definition ref_ptr.h:256
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
const Scalar scale
Point offset
@ kNotEmbeddable_FontFlag
May not be embedded.
@ kNotSubsettable_FontFlag
May not be subset.
@ kVariable_FontFlag
May be true for Type1, CFF, or TrueType fonts.
SkFontArguments & setVariationDesignPosition(VariationPosition position)
SkFontArguments & setPalette(Palette palette)
VariationPosition getVariationDesignPosition() const
SkScalar fTop
greatest extent above origin of any glyph bounding box, typically negative; deprecated with variable ...
SkScalar fLeading
distance to add between lines, typically positive or zero
SkScalar fAvgCharWidth
average character width, zero if unknown
SkScalar fStrikeoutPosition
distance from baseline to bottom of stroke, typically negative
SkScalar fStrikeoutThickness
strikeout thickness
SkScalar fMaxCharWidth
maximum character width, zero if unknown
SkScalar fBottom
greatest extent below origin of any glyph bounding box, typically positive; deprecated with variable ...
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fXHeight
height of lower-case 'x', zero if unknown, typically negative
SkScalar fUnderlineThickness
underline thickness
@ kStrikeoutPositionIsValid_Flag
set if fStrikeoutPosition is valid
@ kStrikeoutThicknessIsValid_Flag
set if fStrikeoutThickness is valid
@ kUnderlinePositionIsValid_Flag
set if fUnderlinePosition is valid
@ kUnderlineThicknessIsValid_Flag
set if fUnderlineThickness is valid
@ kBoundsInvalid_Flag
set if fTop, fBottom, fXMin, fXMax invalid
SkScalar fDescent
distance to reserve below baseline, typically positive
SkScalar fCapHeight
height of an upper-case letter, zero if unknown, typically negative
SkScalar fXMin
greatest extent to left of origin of any glyph bounding box, typically negative; deprecated with vari...
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
SkScalar fXMax
greatest extent to right of origin of any glyph bounding box, typically positive; deprecated with var...
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition SkRect.h:91
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
@ kARGB32_Format
SkPMColor.
Definition SkMask.h:30
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27
const Format fFormat
Definition SkMask.h:44
static constexpr SkPoint Make(float x, float y)
void scale(float scale, SkPoint *dst) const
Definition SkPoint.cpp:17
static SkRGBA4f FromColor(SkColor color)
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
bool isEmpty() const
Definition SkRect.h:693
void join(const SkRect &r)
Definition SkRect.cpp:126
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
void getSingleMatrix(SkMatrix *) const
SkMask::Format fMaskFormat
bool computeMatrices(PreMatrixScale preMatrixScale, SkVector *scale, SkMatrix *remaining, SkMatrix *remainingWithoutRotation=nullptr, SkMatrix *remainingRotation=nullptr, SkMatrix *total=nullptr)
void setHinting(SkFontHinting)
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109