Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkScalerContext_win_dw.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 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
10#if defined(SK_BUILD_FOR_WIN)
11
12#undef GetGlyphIndices
13
18#include "include/core/SkData.h"
24#include "include/core/SkPath.h"
29#include "src/base/SkEndian.h"
32#include "src/core/SkDraw.h"
33#include "src/core/SkGlyph.h"
48
49#include <dwrite.h>
50#include <dwrite_1.h>
51#include <dwrite_3.h>
52
53namespace {
54static inline const constexpr bool kSkShowTextBlitCoverage = false;
55
56/* Note:
57 * In versions 8 and 8.1 of Windows, some calls in DWrite are not thread safe.
58 * The mutex returned from maybe_dw_mutex protects the calls that are
59 * problematic.
60 */
61static SkSharedMutex* maybe_dw_mutex(DWriteFontTypeface& typeface) {
62 static SkSharedMutex mutex;
63 return typeface.fDWriteFontFace4 ? nullptr : &mutex;
64}
65
66class SK_SCOPED_CAPABILITY Exclusive {
67public:
68 explicit Exclusive(SkSharedMutex* maybe_lock) SK_ACQUIRE(*maybe_lock)
69 : fLock(maybe_lock) {
70 if (fLock) {
71 fLock->acquire();
72 }
73 }
74 ~Exclusive() SK_RELEASE_CAPABILITY() {
75 if (fLock) {
76 fLock->release();
77 }
78 }
79
80private:
81 SkSharedMutex* fLock;
82};
83class SK_SCOPED_CAPABILITY Shared {
84public:
85 explicit Shared(SkSharedMutex* maybe_lock) SK_ACQUIRE_SHARED(*maybe_lock)
86 : fLock(maybe_lock) {
87 if (fLock) {
88 fLock->acquireShared();
89 }
90 }
91
92 // You would think this should be SK_RELEASE_SHARED_CAPABILITY, but SK_SCOPED_CAPABILITY
93 // doesn't fully understand the difference between shared and exclusive.
94 // Please review https://reviews.llvm.org/D52578 for more information.
95 ~Shared() SK_RELEASE_CAPABILITY() {
96 if (fLock) {
97 fLock->releaseShared();
98 }
99 }
100
101private:
102 SkSharedMutex* fLock;
103};
104
105static bool isLCD(const SkScalerContextRec& rec) {
107}
108
109static bool is_hinted(DWriteFontTypeface* typeface) {
110 Exclusive l(maybe_dw_mutex(*typeface));
112 if (!maxp.fExists) {
113 return false;
114 }
115 if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
116 return false;
117 }
118 if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
119 return false;
120 }
121 return (0 != maxp->version.tt.maxSizeOfInstructions);
122}
123
124/** A GaspRange is inclusive, [min, max]. */
125struct GaspRange {
127 GaspRange(int min, int max, int version, Behavior flags)
128 : fMin(min), fMax(max), fVersion(version), fFlags(flags) { }
129 int fMin;
130 int fMax;
131 int fVersion;
132 Behavior fFlags;
133};
134
135bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) {
137 if (!gasp.fExists) {
138 return false;
139 }
140 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
141 return false;
142 }
143 if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
145 {
146 return false;
147 }
148
149 uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
150 if (numRanges > 1024 ||
151 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
153 {
154 return false;
155 }
156
158 SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
159 int minPPEM = -1;
160 for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
161 int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
162 if (minPPEM < size && size <= maxPPEM) {
163 range->fMin = minPPEM + 1;
164 range->fMax = maxPPEM;
165 range->fVersion = SkEndian_SwapBE16(gasp->version);
166 range->fFlags = rangeTable->flags;
167 return true;
168 }
169 minPPEM = maxPPEM;
170 }
171 return false;
172}
173/** If the rendering mode for the specified 'size' is gridfit, then place
174 * the gridfit range into 'range'. Otherwise, leave 'range' alone.
175 */
176static bool is_gridfit_only(GaspRange::Behavior flags) {
177 return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
178}
179
180static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) {
181 Exclusive l(maybe_dw_mutex(*typeface));
182 {
184 if (!eblc.fExists) {
185 return false;
186 }
187 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
188 return false;
189 }
191 return false;
192 }
193
194 uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
195 if (numSizes > 1024 ||
196 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
198 {
199 return false;
200 }
201
203 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
204 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
205 if (sizeTable->ppemX == sizeTable->ppemY &&
206 range.fMin <= sizeTable->ppemX && sizeTable->ppemX <= range.fMax)
207 {
208 // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
209 // to determine the actual number of glyphs with bitmaps.
210
211 // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
212
213 // TODO: Ensure that the bitmaps are bi-level?
214 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
215 return true;
216 }
217 }
218 }
219 }
220
221 {
223 if (!ebsc.fExists) {
224 return false;
225 }
226 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
227 return false;
228 }
230 return false;
231 }
232
233 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
234 if (numSizes > 1024 ||
235 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
237 {
238 return false;
239 }
240
242 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
243 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
244 if (scaleTable->ppemX == scaleTable->ppemY &&
245 range.fMin <= scaleTable->ppemX && scaleTable->ppemX <= range.fMax) {
246 // EBSC tables are normally only found in bitmap only fonts.
247 return true;
248 }
249 }
250 }
251
252 return false;
253}
254
255static bool both_zero(SkScalar a, SkScalar b) {
256 return 0 == a && 0 == b;
257}
258
259// returns false if there is any non-90-rotation or skew
260static bool is_axis_aligned(const SkScalerContextRec& rec) {
261 return 0 == rec.fPreSkewX &&
262 (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
263 both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
264}
265
266} //namespace
267
269 const SkScalerContextEffects& effects,
270 const SkDescriptor* desc)
271 : SkScalerContext(std::move(typefaceRef), effects, desc)
272{
273 DWriteFontTypeface* typeface = this->getDWriteTypeface();
274 fGlyphCount = typeface->fDWriteFontFace->GetGlyphCount();
275
276 // In general, all glyphs should use NATURAL_SYMMETRIC
277 // except when bi-level rendering is requested or there are embedded
278 // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
279 //
280 // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
281 // this. As a result, determine the actual size of the text and then see if
282 // there are any embedded bi-level bitmaps of that size. If there are, then
283 // force bitmaps by requesting bi-level rendering.
284 //
285 // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
286 // square pixels and only uses ppemY. Therefore the transform must track any
287 // non-uniform x-scale.
288 //
289 // Also, rotated glyphs should have the same absolute advance widths as
290 // horizontal glyphs and the subpixel flag should not affect glyph shapes.
291
293 fRec.computeMatrices(SkScalerContextRec::PreMatrixScale::kVertical, &scale, &fSkXform);
294
295 fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
296 fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
297 fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
298 fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
299 fXform.dx = 0;
300 fXform.dy = 0;
301
302 // realTextSize is the actual device size we want (as opposed to the size the user requested).
303 // gdiTextSize is the size we request when GDI compatible.
304 // If the scale is negative, this means the matrix will do the flip anyway.
305 const SkScalar realTextSize = scale.fY;
306 // Due to floating point math, the lower bits are suspect. Round carefully.
307 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
308 if (gdiTextSize == 0) {
309 gdiTextSize = SK_Scalar1;
310 }
311
312 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
313 bool treatLikeBitmap = false;
314 bool axisAlignedBitmap = false;
315 if (bitmapRequested) {
316 // When embedded bitmaps are requested, treat the entire range like
317 // a bitmap strike if the range is gridfit only and contains a bitmap.
318 int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
319 GaspRange range(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
320 if (get_gasp_range(typeface, bitmapPPEM, &range)) {
321 if (!is_gridfit_only(range.fFlags)) {
322 range = GaspRange(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
323 }
324 }
325 treatLikeBitmap = has_bitmap_strike(typeface, range);
326
327 axisAlignedBitmap = is_axis_aligned(fRec);
328 }
329
330 GaspRange range(0, 0xFFFF, 0, GaspRange::Behavior());
331
332 // If the user requested aliased, do so with aliased compatible metrics.
333 if (SkMask::kBW_Format == fRec.fMaskFormat) {
334 fTextSizeRender = gdiTextSize;
335 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
336 fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
337 fTextSizeMeasure = gdiTextSize;
338 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
339
340 // If we can use a bitmap, use gdi classic rendering and measurement.
341 // This will not always provide a bitmap, but matches expected behavior.
342 } else if (treatLikeBitmap && axisAlignedBitmap) {
343 fTextSizeRender = gdiTextSize;
344 fRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
345 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
346 fTextSizeMeasure = gdiTextSize;
347 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
348
349 // If rotated but the horizontal text could have used a bitmap,
350 // render high quality rotated glyphs but measure using bitmap metrics.
351 } else if (treatLikeBitmap) {
352 fTextSizeRender = gdiTextSize;
353 fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
354 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
355 fTextSizeMeasure = gdiTextSize;
356 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
357
358 // If the font has a gasp table version 1, use it to determine symmetric rendering.
359 } else if (get_gasp_range(typeface, SkScalarRoundToInt(gdiTextSize), &range) &&
360 range.fVersion >= 1)
361 {
362 fTextSizeRender = realTextSize;
363 fRenderingMode = range.fFlags.field.SymmetricSmoothing
364 ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
365 : DWRITE_RENDERING_MODE_NATURAL;
366 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
367 fTextSizeMeasure = realTextSize;
368 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
369
370 // If the requested size is above 20px or there are no bytecode hints, use symmetric rendering.
371 } else if (realTextSize > SkIntToScalar(20) || !is_hinted(typeface)) {
372 fTextSizeRender = realTextSize;
373 fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
374 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
375 fTextSizeMeasure = realTextSize;
376 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
377
378 // Fonts with hints, no gasp or gasp version 0, and below 20px get non-symmetric rendering.
379 // Often such fonts have hints which were only tested with GDI ClearType classic.
380 // Some of these fonts rely on drop out control in the y direction in order to be legible.
381 // Tenor Sans
382 // https://fonts.google.com/specimen/Tenor+Sans
383 // Gill Sans W04
384 // https://cdn.leagueoflegends.com/lolkit/1.1.9/resources/fonts/gill-sans-w04-book.woff
385 // https://na.leagueoflegends.com/en/news/game-updates/patch/patch-410-notes
386 // See https://crbug.com/385897
387 } else {
388 fTextSizeRender = gdiTextSize;
389 fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
390 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
391 fTextSizeMeasure = realTextSize;
392 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
393 }
394
395 // DirectWrite2 allows for grayscale hinting.
396 fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE;
397 if (typeface->fFactory2 && typeface->fDWriteFontFace2 &&
398 SkMask::kA8_Format == fRec.fMaskFormat &&
400 {
401 // DWRITE_TEXTURE_ALIASED_1x1 is now misnamed, it must also be used with grayscale.
402 fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
403 fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE;
404 }
405
406 // DirectWrite2 allows hinting to be disabled.
407 fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED;
408 if (fRec.getHinting() == SkFontHinting::kNone) {
409 fGridFitMode = DWRITE_GRID_FIT_MODE_DISABLED;
410 if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
411 fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
412 }
413 }
414
415 if (this->isLinearMetrics()) {
416 fTextSizeMeasure = realTextSize;
417 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
418 }
419
420 // The GDI measuring modes don't seem to work well with CBDT fonts (DWrite.dll 10.0.18362.836).
421 if (fMeasuringMode != DWRITE_MEASURING_MODE_NATURAL) {
422 constexpr UINT32 CBDTTag = DWRITE_MAKE_OPENTYPE_TAG('C','B','D','T');
423 AutoDWriteTable CBDT(typeface->fDWriteFontFace.get(), CBDTTag);
424 if (CBDT.fExists) {
425 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
426 }
427 }
428}
429
431}
432
433#if DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
434
435namespace {
436SkColor4f sk_color_from(DWRITE_COLOR_F const& color) {
437 // DWRITE_COLOR_F and SkColor4f are laid out the same and this should be a no-op.
438 return SkColor4f{ color.r, color.g, color.b, color.a };
439}
440DWRITE_COLOR_F dw_color_from(SkColor4f const& color) {
441 // DWRITE_COLOR_F and SkColor4f are laid out the same and this should be a no-op.
442 // Avoid brace initialization as DWRITE_COLOR_F can be defined as four floats (dxgitype.h,
443 // d3d9types.h) or four unions of two floats (dwrite_2.h, d3dtypes.h). The type changed in
444 // Direct3D 10, but the change does not appear to be documented.
445 DWRITE_COLOR_F dwColor;
446 dwColor.r = color.fR;
447 dwColor.g = color.fG;
448 dwColor.b = color.fB;
449 dwColor.a = color.fA;
450 return dwColor;
451}
452
453SkRect sk_rect_from(D2D_RECT_F const& rect) {
454 // D2D_RECT_F and SkRect are both y-down and laid the same so this should be a no-op.
455 return SkRect{ rect.left, rect.top, rect.right, rect.bottom };
456}
457constexpr bool D2D_RECT_F_is_empty(const D2D_RECT_F& r) {
458 return r.right <= r.left || r.bottom <= r.top;
459}
460
461SkMatrix sk_matrix_from(DWRITE_MATRIX const& m) {
462 // DWRITE_MATRIX and SkMatrix are y-down. However DWRITE_MATRIX is affine only.
463 return SkMatrix::MakeAll(
464 m.m11, m.m21, m.dx,
465 m.m12, m.m22, m.dy,
466 0, 0, 1);
467}
468
469SkTileMode sk_tile_mode_from(D2D1_EXTEND_MODE extendMode) {
470 switch (extendMode) {
471 case D2D1_EXTEND_MODE_CLAMP:
472 return SkTileMode::kClamp;
473 case D2D1_EXTEND_MODE_WRAP:
474 return SkTileMode::kRepeat;
475 case D2D1_EXTEND_MODE_MIRROR:
476 return SkTileMode::kMirror;
477 default:
478 return SkTileMode::kClamp;
479 }
480}
481
482SkBlendMode sk_blend_mode_from(DWRITE_COLOR_COMPOSITE_MODE compositeMode) {
483 switch (compositeMode) {
484 case DWRITE_COLOR_COMPOSITE_CLEAR:
485 return SkBlendMode::kClear;
486 case DWRITE_COLOR_COMPOSITE_SRC:
487 return SkBlendMode::kSrc;
488 case DWRITE_COLOR_COMPOSITE_DEST:
489 return SkBlendMode::kDst;
490 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
492 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
494 case DWRITE_COLOR_COMPOSITE_SRC_IN:
495 return SkBlendMode::kSrcIn;
496 case DWRITE_COLOR_COMPOSITE_DEST_IN:
497 return SkBlendMode::kDstIn;
498 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
500 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
502 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
504 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
506 case DWRITE_COLOR_COMPOSITE_XOR:
507 return SkBlendMode::kXor;
508 case DWRITE_COLOR_COMPOSITE_PLUS:
509 return SkBlendMode::kPlus;
510
511 case DWRITE_COLOR_COMPOSITE_SCREEN:
513 case DWRITE_COLOR_COMPOSITE_OVERLAY:
515 case DWRITE_COLOR_COMPOSITE_DARKEN:
517 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
519 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
521 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
523 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
525 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
527 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
529 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
531 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
533
534 case DWRITE_COLOR_COMPOSITE_HSL_HUE:
535 return SkBlendMode::kHue;
536 case DWRITE_COLOR_COMPOSITE_HSL_SATURATION:
538 case DWRITE_COLOR_COMPOSITE_HSL_COLOR:
539 return SkBlendMode::kColor;
540 case DWRITE_COLOR_COMPOSITE_HSL_LUMINOSITY:
542 default:
543 return SkBlendMode::kDst;
544 }
545}
546
547inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
549 if (!length) {
550 return SkPoint();
551 }
552 SkPoint bNormalized = b;
553 bNormalized.normalize();
554 bNormalized.scale(SkPoint::DotProduct(a, b) / length);
555 return bNormalized;
556}
557
558// This linear interpolation is used for calculating a truncated color line in special edge cases.
559// This interpolation needs to be kept in sync with what the gradient shader would normally do when
560// truncating and drawing color lines. When drawing into N32 surfaces, this is expected to be true.
561// If that changes, or if we support other color spaces in CPAL tables at some point, this needs to
562// be looked at.
563D2D1_COLOR_F lerpSkColor(D2D1_COLOR_F c0, D2D1_COLOR_F c1, float t) {
564 // Due to the floating point calculation in the caller, when interpolating between very narrow
565 // stops, we may get values outside the interpolation range, guard against these.
566 if (t < 0) {
567 return c0;
568 }
569 if (t > 1) {
570 return c1;
571 }
572 const auto c0_4f = skvx::float4(c0.r, c0.g, c0.b, c0.a),
573 c1_4f = skvx::float4(c1.r, c1.g, c1.b, c1.a),
574 c_4f = c0_4f + (c1_4f - c0_4f) * t;
575 D2D1_COLOR_F r;
576 c_4f.store(&r);
577 return r;
578}
579
580enum TruncateStops {
581 TruncateStart,
582 TruncateEnd,
583};
584// Truncate a vector of color stops at a previously computed stop position and insert at that
585// position the color interpolated between the surrounding stops.
586void truncateToStopInterpolating(SkScalar zeroRadiusStop,
587 std::vector<D2D1_GRADIENT_STOP>& stops,
588 TruncateStops truncateStops) {
589 if (stops.size() <= 1u ||
590 zeroRadiusStop < stops.front().position || stops.back().position < zeroRadiusStop) {
591 return;
592 }
593
594 auto lcmp = [](D2D1_GRADIENT_STOP const& stop, SkScalar position) {
595 return stop.position < position;
596 };
597 auto ucmp = [](SkScalar position, D2D1_GRADIENT_STOP const& stop) {
598 return position < stop.position;
599 };
600 size_t afterIndex = (truncateStops == TruncateStart)
601 ? std::lower_bound(stops.begin(), stops.end(), zeroRadiusStop, lcmp) - stops.begin()
602 : std::upper_bound(stops.begin(), stops.end(), zeroRadiusStop, ucmp) - stops.begin();
603
604 const float t = (zeroRadiusStop - stops[afterIndex - 1].position) /
605 (stops[afterIndex].position - stops[afterIndex - 1].position);
606 D2D1_COLOR_F lerpColor = lerpSkColor(stops[afterIndex - 1].color, stops[afterIndex].color, t);
607
608 if (truncateStops == TruncateStart) {
609 stops.erase(stops.begin(), stops.begin() + afterIndex);
610 stops.insert(stops.begin(), { 0, lerpColor });
611 } else {
612 stops.erase(stops.begin() + afterIndex, stops.end());
613 stops.insert(stops.end(), { 1, lerpColor });
614 }
615}
616} // namespace
617
618bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas,
619 IDWritePaintReader& reader,
620 DWRITE_PAINT_ELEMENT const & element)
621{
622 // Helper to draw the specified number of children.
623 auto drawChildren = [&](uint32_t childCount) -> bool {
624 if (childCount != 0) {
625 DWRITE_PAINT_ELEMENT childElement;
626 HRB(reader.MoveToFirstChild(&childElement));
627 this->drawColorV1Paint(canvas, reader, childElement);
628
629 for (uint32_t i = 1; i < childCount; i++) {
630 HRB(reader.MoveToNextSibling(&childElement));
631 this->drawColorV1Paint(canvas, reader, childElement);
632 }
633
634 HRB(reader.MoveToParent());
635 }
636 return true;
637 };
638
639 SkAutoCanvasRestore restoreCanvas(&canvas, true);
640 switch (element.paintType) {
641 case DWRITE_PAINT_TYPE_NONE:
642 return true;
643
644 case DWRITE_PAINT_TYPE_LAYERS: {
645 // A layers paint element has a variable number of children.
646 return drawChildren(element.paint.layers.childCount);
647 }
648
649 case DWRITE_PAINT_TYPE_SOLID_GLYPH: {
650 // A solid glyph paint element has no children.
651 // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes
652 auto const& solidGlyph = element.paint.solidGlyph;
653
654 SkPath path;
655 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
656 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
657 "Could not create geometry to path converter.");
658 UINT16 glyphId = SkTo<UINT16>(solidGlyph.glyphIndex);
659 {
660 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
661 HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
662 SkScalarToFloat(fTextSizeRender),
663 &glyphId,
664 nullptr, //advances
665 nullptr, //offsets
666 1, //num glyphs
667 FALSE, //sideways
668 FALSE, //rtl
669 geometryToPath.get()),
670 "Could not create glyph outline.");
671 }
672
673 path.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
674 SkPaint skPaint;
675 skPaint.setColor4f(sk_color_from(solidGlyph.color.value));
676 skPaint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
677 canvas.drawPath(path, skPaint);
678 return true;
679 }
680
681 case DWRITE_PAINT_TYPE_SOLID: {
682 // A solid paint element has no children.
683 // value, paletteEntryIndex, alphaMultiplier, colorAttributes
684 SkPaint skPaint;
685 skPaint.setColor4f(sk_color_from(element.paint.solid.value));
686 canvas.drawPaint(skPaint);
687 return true;
688 }
689
690 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT: {
691 auto const& linearGradient = element.paint.linearGradient;
692 // A linear gradient paint element has no children.
693 // x0, y0, x1, y1, x2, y2, extendMode, gradientStopCount, [colorStops]
694
695 if (linearGradient.gradientStopCount == 0) {
696 return true;
697 }
698 std::vector<D2D1_GRADIENT_STOP> stops;
699 stops.resize(linearGradient.gradientStopCount);
700
701 // If success stops will be ordered.
702 HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
703 "Could not get linear gradient stops.");
704 SkPaint skPaint;
705 if (stops.size() == 1) {
706 skPaint.setColor4f(sk_color_from(stops[0].color));
707 canvas.drawPaint(skPaint);
708 return true;
709 }
710 SkPoint linePositions[2] = { {linearGradient.x0, linearGradient.y0},
711 {linearGradient.x1, linearGradient.y1} };
712 SkPoint p0 = linePositions[0];
713 SkPoint p1 = linePositions[1];
714 SkPoint p2 = SkPoint::Make(linearGradient.x2, linearGradient.y2);
715
716 // If p0p1 or p0p2 are degenerate probably nothing should be drawn.
717 // If p0p1 and p0p2 are parallel then one side is the first color and the other side is
718 // the last color, depending on the direction.
719 // For now, just use the first color.
720 if (p1 == p0 || p2 == p0 || !SkPoint::CrossProduct(p1 - p0, p2 - p0)) {
721 skPaint.setColor4f(sk_color_from(stops[0].color));
722 canvas.drawPaint(skPaint);
723 return true;
724 }
725
726 // Follow implementation note in nanoemoji:
727 // https://github.com/googlefonts/nanoemoji/blob/0ac6e7bb4d8202db692574d8530a9b643f1b3b3c/src/nanoemoji/svg.py#L188
728 // to compute a new gradient end point P3 as the orthogonal
729 // projection of the vector from p0 to p1 onto a line perpendicular
730 // to line p0p2 and passing through p0.
731 SkVector perpendicularToP2P0 = (p2 - p0);
732 perpendicularToP2P0 = SkPoint::Make( perpendicularToP2P0.y(),
733 -perpendicularToP2P0.x());
734 SkVector p3 = p0 + SkVectorProjection((p1 - p0), perpendicularToP2P0);
735 linePositions[1] = p3;
736
737 // Project/scale points according to stop extrema along p0p3 line,
738 // p3 being the result of the projection above, then scale stops to
739 // to [0, 1] range so that repeat modes work. The Skia linear
740 // gradient shader performs the repeat modes over the 0 to 1 range,
741 // that's why we need to scale the stops to within that range.
742 SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(linearGradient.extendMode));
743 SkScalar colorStopRange = stops.back().position - stops.front().position;
744 // If the color stops are all at the same offset position, repeat and reflect modes
745 // become meaningless.
746 if (colorStopRange == 0.f) {
747 if (tileMode != SkTileMode::kClamp) {
748 //skPaint.setColor(SK_ColorTRANSPARENT);
749 return true;
750 } else {
751 // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
752 // of circles for an originally 0-length color stop range. Adding this stop will
753 // paint the equivalent gradient, because: All font specified color stops are in the
754 // same spot, mode is pad, so everything before this spot is painted with the first
755 // color, everything after this spot is painted with the last color. Not adding this
756 // stop will skip the projection and result in specifying non-normalized color stops
757 // to the shader.
758 stops.push_back({ stops.back().position + 1.0f, stops.back().color });
759 colorStopRange = 1.0f;
760 }
761 }
762 SkASSERT(colorStopRange != 0.f);
763
764 // If the colorStopRange is 0 at this point, the default behavior of the shader is to
765 // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
766 // and repeat the outer color stops at 0 and 1 if the color stops are inside the
767 // range. That will result in the correct rendering.
768 if ((colorStopRange != 1 || stops.front().position != 0.f)) {
769 SkVector p0p3 = p3 - p0;
770 SkVector p0Offset = p0p3;
771 p0Offset.scale(stops.front().position);
772 SkVector p1Offset = p0p3;
773 p1Offset.scale(stops.back().position);
774
775 linePositions[0] = p0 + p0Offset;
776 linePositions[1] = p0 + p1Offset;
777
778 SkScalar scaleFactor = 1 / colorStopRange;
779 SkScalar startOffset = stops.front().position;
780 for (D2D1_GRADIENT_STOP& stop : stops) {
781 stop.position = (stop.position - startOffset) * scaleFactor;
782 }
783 }
784
785 std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
786 std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
787 for (size_t i = 0; i < stops.size(); ++i) {
788 skColors[i] = sk_color_from(stops[i].color);
789 skStops[i] = stops[i].position;
790 }
791
793 linePositions,
794 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
795 tileMode,
797 SkGradientShader::Interpolation::InPremul::kNo,
798 SkGradientShader::Interpolation::ColorSpace::kSRGB,
799 SkGradientShader::Interpolation::HueMethod::kShorter
800 },
801 nullptr));
802
803 SkASSERT(shader);
804 // An opaque color is needed to ensure the gradient is not modulated by alpha.
805 skPaint.setColor(SK_ColorBLACK);
806 skPaint.setShader(shader);
807 canvas.drawPaint(skPaint);
808 return true;
809 }
810
811 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT: {
812 auto const& radialGradient = element.paint.radialGradient;
813 // A radial gradient paint element has no children.
814 // x0, y0, radius0, x1, y1, radius1, extendMode, gradientStopCount, [colorsStops]
815
816 SkPoint start = SkPoint::Make(radialGradient.x0, radialGradient.y0);
817 SkScalar startRadius = radialGradient.radius0;
818 SkPoint end = SkPoint::Make(radialGradient.x1, radialGradient.y1);
819 SkScalar endRadius = radialGradient.radius1;
820
821 if (radialGradient.gradientStopCount == 0) {
822 return true;
823 }
824 std::vector<D2D1_GRADIENT_STOP> stops;
825 stops.resize(radialGradient.gradientStopCount);
826
827 // If success stops will be ordered.
828 HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
829 "Could not get radial gradient stops.");
830 SkPaint skPaint;
831 if (stops.size() == 1) {
832 skPaint.setColor4f(sk_color_from(stops[0].color));
833 canvas.drawPaint(skPaint);
834 return true;
835 }
836
837 SkScalar colorStopRange = stops.back().position - stops.front().position;
838 SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(radialGradient.extendMode));
839
840 if (colorStopRange == 0.f) {
841 if (tileMode != SkTileMode::kClamp) {
842 //skPaint.setColor(SK_ColorTRANSPARENT);
843 return true;
844 } else {
845 // Insert duplicated fake color stop in pad case at +1.0f to enable the projection
846 // of circles for an originally 0-length color stop range. Adding this stop will
847 // paint the equivalent gradient, because: All font specified color stops are in the
848 // same spot, mode is pad, so everything before this spot is painted with the first
849 // color, everything after this spot is painted with the last color. Not adding this
850 // stop will skip the projection and result in specifying non-normalized color stops
851 // to the shader.
852 stops.push_back({ stops.back().position + 1.0f, stops.back().color });
853 colorStopRange = 1.0f;
854 }
855 }
856 SkASSERT(colorStopRange != 0.f);
857
858 // If the colorStopRange is 0 at this point, the default behavior of the shader is to
859 // clamp to 1 color stops that are above 1, clamp to 0 for color stops that are below 0,
860 // and repeat the outer color stops at 0 and 1 if the color stops are inside the
861 // range. That will result in the correct rendering.
862 if (colorStopRange != 1 || stops.front().position != 0.f) {
863 // For the Skia two-point caonical shader to understand the
864 // COLRv1 color stops we need to scale stops to 0 to 1 range and
865 // interpolate new centers and radii. Otherwise the shader
866 // clamps stops outside the range to 0 and 1 (larger interval)
867 // or repeats the outer stops at 0 and 1 if the (smaller
868 // interval).
869 SkVector startToEnd = end - start;
870 SkScalar radiusDiff = endRadius - startRadius;
871 SkScalar scaleFactor = 1 / colorStopRange;
872 SkScalar stopsStartOffset = stops.front().position;
873
874 SkVector startOffset = startToEnd;
875 startOffset.scale(stops.front().position);
876 SkVector endOffset = startToEnd;
877 endOffset.scale(stops.back().position);
878
879 // The order of the following computations is important in order to avoid
880 // overwriting start or startRadius before the second reassignment.
881 end = start + endOffset;
882 start = start + startOffset;
883 endRadius = startRadius + radiusDiff * stops.back().position;
884 startRadius = startRadius + radiusDiff * stops.front().position;
885
886 for (auto& stop : stops) {
887 stop.position = (stop.position - stopsStartOffset) * scaleFactor;
888 }
889 }
890
891 // For negative radii, interpolation is needed to prepare parameters suitable
892 // for invoking the shader. Implementation below as resolution discussed in
893 // https://github.com/googlefonts/colr-gradients-spec/issues/367.
894 // Truncate to manually interpolated color for tile mode clamp, otherwise
895 // calculate positive projected circles.
896 if (startRadius < 0 || endRadius < 0) {
897 if (startRadius == endRadius && startRadius < 0) {
898 //skPaint.setColor(SK_ColorTRANSPARENT);
899 return true;
900 }
901
902 if (tileMode == SkTileMode::kClamp) {
903 SkVector startToEnd = end - start;
904 SkScalar radiusDiff = endRadius - startRadius;
905 SkScalar zeroRadiusStop = 0.f;
906 TruncateStops truncateSide = TruncateStart;
907 if (startRadius < 0) {
908 truncateSide = TruncateStart;
909
910 // Compute color stop position where radius is = 0. After the scaling
911 // of stop positions to the normal 0,1 range that we have done above,
912 // the size of the radius as a function of the color stops is: r(x) = r0
913 // + x*(r1-r0) Solving this function for r(x) = 0, we get: x = -r0 /
914 // (r1-r0)
915 zeroRadiusStop = -startRadius / (endRadius - startRadius);
916 startRadius = 0.f;
917 SkVector startEndDiff = end - start;
918 startEndDiff.scale(zeroRadiusStop);
919 start = start + startEndDiff;
920 }
921
922 if (endRadius < 0) {
923 truncateSide = TruncateEnd;
924 zeroRadiusStop = -startRadius / (endRadius - startRadius);
925 endRadius = 0.f;
926 SkVector startEndDiff = end - start;
927 startEndDiff.scale(1 - zeroRadiusStop);
928 end = end - startEndDiff;
929 }
930
931 if (!(startRadius == 0 && endRadius == 0)) {
932 truncateToStopInterpolating(zeroRadiusStop, stops, truncateSide);
933 } else {
934 // If both radii have become negative and where clamped to 0, we need to
935 // produce a single color cone, otherwise the shader colors the whole
936 // plane in a single color when two radii are specified as 0.
937 if (radiusDiff > 0) {
938 end = start + startToEnd;
939 endRadius = radiusDiff;
940 stops.erase(stops.begin(), stops.end() - 1);
941 } else {
942 start -= startToEnd;
943 startRadius = -radiusDiff;
944 stops.erase(stops.begin() + 1, stops.end());
945 }
946 }
947 } else {
948 if (startRadius < 0 || endRadius < 0) {
949 auto roundIntegerMultiple = [](SkScalar factorZeroCrossing,
950 SkTileMode tileMode) {
951 int roundedMultiple = factorZeroCrossing > 0
952 ? ceilf(factorZeroCrossing)
953 : floorf(factorZeroCrossing) - 1;
954 if (tileMode == SkTileMode::kMirror && roundedMultiple % 2 != 0) {
955 roundedMultiple += roundedMultiple < 0 ? -1 : 1;
956 }
957 return roundedMultiple;
958 };
959
960 SkVector startToEnd = end - start;
961 SkScalar radiusDiff = endRadius - startRadius;
962 SkScalar factorZeroCrossing = (startRadius / (startRadius - endRadius));
963 bool inRange = 0.f <= factorZeroCrossing && factorZeroCrossing <= 1.0f;
964 SkScalar direction = inRange && radiusDiff < 0 ? -1.0f : 1.0f;
965 SkScalar circleProjectionFactor =
966 roundIntegerMultiple(factorZeroCrossing * direction, tileMode);
967 startToEnd.scale(circleProjectionFactor);
968 startRadius += circleProjectionFactor * radiusDiff;
969 endRadius += circleProjectionFactor * radiusDiff;
970 start += startToEnd;
971 end += startToEnd;
972 }
973 }
974 }
975
976 std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
977 std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
978 for (size_t i = 0; i < stops.size(); ++i) {
979 skColors[i] = sk_color_from(stops[i].color);
980 skStops[i] = stops[i].position;
981 }
982
983 // An opaque color is needed to ensure the gradient is not modulated by alpha.
984 skPaint.setColor(SK_ColorBLACK);
986 start, startRadius, end, endRadius,
987 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
988 tileMode,
990 SkGradientShader::Interpolation::InPremul::kNo,
991 SkGradientShader::Interpolation::ColorSpace::kSRGB,
992 SkGradientShader::Interpolation::HueMethod::kShorter
993 },
994 nullptr));
995 canvas.drawPaint(skPaint);
996 return true;
997 }
998
999 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT: {
1000 auto const& sweepGradient = element.paint.sweepGradient;
1001 // A sweep gradient paint element has no children.
1002 // centerX, centerY, startAngle, endAngle, extendMode, gradientStopCount, [colorStops]
1003
1004 if (sweepGradient.gradientStopCount == 0) {
1005 return true;
1006 }
1007 std::vector<D2D1_GRADIENT_STOP> stops;
1008 stops.resize(sweepGradient.gradientStopCount);
1009
1010 // If success stops will be ordered.
1011 HRBM(reader.GetGradientStops(0, stops.size(), stops.data()),
1012 "Could not get sweep gradient stops");
1013 SkPaint skPaint;
1014 if (stops.size() == 1) {
1015 skPaint.setColor4f(sk_color_from(stops[0].color));
1016 canvas.drawPaint(skPaint);
1017 return true;
1018 }
1019
1020 SkPoint center = SkPoint::Make(sweepGradient.centerX, sweepGradient.centerY);
1021
1022 SkScalar startAngle = sweepGradient.startAngle;
1023 SkScalar endAngle = sweepGradient.endAngle;
1024 // OpenType 1.9.1 adds a shift to the angle to ease specification of a 0 to 360
1025 // degree sweep. This appears to already be applied by DW.
1026 //startAngle += 180.0f;
1027 //endAngle += 180.0f;
1028
1029 // An opaque color is needed to ensure the gradient is not modulated by alpha.
1030 skPaint.setColor(SK_ColorBLACK);
1031
1032 // New (Var)SweepGradient implementation compliant with OpenType 1.9.1 from here.
1033
1034 // The shader expects stops from 0 to 1, so we need to account for
1035 // minimum and maximum stop positions being different from 0 and
1036 // 1. We do that by scaling minimum and maximum stop positions to
1037 // the 0 to 1 interval and scaling the angles inverse proportionally.
1038
1039 // 1) Scale angles to their equivalent positions if stops were from 0 to 1.
1040
1041 SkScalar sectorAngle = endAngle - startAngle;
1042 SkTileMode tileMode = sk_tile_mode_from(SkTo<D2D1_EXTEND_MODE>(sweepGradient.extendMode));
1043 if (sectorAngle == 0 && tileMode != SkTileMode::kClamp) {
1044 // "If the ColorLine's extend mode is reflect or repeat and start and end angle
1045 // are equal, nothing is drawn.".
1046 //skPaint.setColor(SK_ColorTRANSPARENT);
1047 return true;
1048 }
1049
1050 SkScalar startAngleScaled = startAngle + sectorAngle * stops.front().position;
1051 SkScalar endAngleScaled = startAngle + sectorAngle * stops.back().position;
1052
1053 // 2) Scale stops accordingly to 0 to 1 range.
1054
1055 float colorStopRange = stops.back().position - stops.front().position;
1056 if (colorStopRange == 0.f) {
1057 if (tileMode != SkTileMode::kClamp) {
1058 //skPaint.setColor(SK_ColorTRANSPARENT);
1059 return true;
1060 } else {
1061 // Insert duplicated fake color stop in pad case at +1.0f to feed the shader correct
1062 // values and enable painting a pad sweep gradient with two colors. Adding this stop
1063 // will paint the equivalent gradient, because: All font specified color stops are
1064 // in the same spot, mode is pad, so everything before this spot is painted with the
1065 // first color, everything after this spot is painted with the last color. Not
1066 // adding this stop will skip the projection and result in specifying non-normalized
1067 // color stops to the shader.
1068 stops.push_back({ stops.back().position + 1.0f, stops.back().color });
1069 colorStopRange = 1.0f;
1070 }
1071 }
1072
1073 SkScalar scaleFactor = 1 / colorStopRange;
1074 SkScalar startOffset = stops.front().position;
1075
1076 for (D2D1_GRADIENT_STOP& stop : stops) {
1077 stop.position = (stop.position - startOffset) * scaleFactor;
1078 }
1079
1080 /* https://docs.microsoft.com/en-us/typography/opentype/spec/colr#sweep-gradients
1081 * "The angles are expressed in counter-clockwise degrees from
1082 * the direction of the positive x-axis on the design
1083 * grid. [...] The color line progresses from the start angle
1084 * to the end angle in the counter-clockwise direction;" -
1085 * Convert angles and stops from counter-clockwise to clockwise
1086 * for the shader if the gradient is not already reversed due to
1087 * start angle being larger than end angle. */
1088 startAngleScaled = 360.f - startAngleScaled;
1089 endAngleScaled = 360.f - endAngleScaled;
1090 if (startAngleScaled >= endAngleScaled) {
1091 std::swap(startAngleScaled, endAngleScaled);
1092 std::reverse(stops.begin(), stops.end());
1093 for (auto& stop : stops) {
1094 stop.position = 1.0f - stop.position;
1095 }
1096 }
1097
1098 std::unique_ptr<SkColor4f[]> skColors(new SkColor4f[stops.size()]);
1099 std::unique_ptr<SkScalar[]> skStops(new SkScalar[stops.size()]);
1100 for (size_t i = 0; i < stops.size(); ++i) {
1101 skColors[i] = sk_color_from(stops[i].color);
1102 skStops[i] = stops[i].position;
1103 }
1104
1106 center.x(), center.y(),
1107 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
1108 tileMode,
1109 startAngleScaled, endAngleScaled,
1111 SkGradientShader::Interpolation::InPremul::kNo,
1112 SkGradientShader::Interpolation::ColorSpace::kSRGB,
1113 SkGradientShader::Interpolation::HueMethod::kShorter
1114 },
1115 nullptr));
1116 canvas.drawPaint(skPaint);
1117 return true;
1118 }
1119
1120 case DWRITE_PAINT_TYPE_GLYPH: {
1121 // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex.
1122 SkPath path;
1123 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1124 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
1125 "Could not create geometry to path converter.");
1126 UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex);
1127 {
1128 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
1129 HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
1130 SkScalarToFloat(fTextSizeRender),
1131 &glyphId,
1132 nullptr, //advances
1133 nullptr, //offsets
1134 1, //num glyphs
1135 FALSE, //sideways
1136 FALSE, //rtl
1137 geometryToPath.get()),
1138 "Could not create glyph outline.");
1139 }
1140
1141 path.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
1142 canvas.clipPath(path, fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
1143
1144 drawChildren(1);
1145 return true;
1146 }
1147
1148 case DWRITE_PAINT_TYPE_COLOR_GLYPH: {
1149 auto const& colorGlyph = element.paint.colorGlyph;
1150 // A color glyph paint element has one child, the root of the paint tree for glyphIndex.
1151 // glyphIndex, clipBox
1152 if (D2D_RECT_F_is_empty(colorGlyph.clipBox)) {
1153 // Does not have a clip box
1154 } else {
1155 SkRect r = sk_rect_from(colorGlyph.clipBox);
1156 canvas.clipRect(r, fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
1157 }
1158
1159 drawChildren(1);
1160 return true;
1161 }
1162
1163 case DWRITE_PAINT_TYPE_TRANSFORM: {
1164 // A transform paint element always has one child, the transformed content.
1165 canvas.concat(sk_matrix_from(element.paint.transform));
1166 drawChildren(1);
1167 return true;
1168 }
1169
1170 case DWRITE_PAINT_TYPE_COMPOSITE: {
1171 // A composite paint element has two children, the source and destination of the operation.
1172
1173 SkPaint blendModePaint;
1174 blendModePaint.setBlendMode(sk_blend_mode_from(element.paint.composite.mode));
1175
1176 SkAutoCanvasRestore acr(&canvas, false);
1177
1178 // Need to visit the second child first and do savelayers, so manually handle children.
1179 DWRITE_PAINT_ELEMENT sourceElement;
1180 DWRITE_PAINT_ELEMENT backdropElement;
1181
1182 HRBM(reader.MoveToFirstChild(&sourceElement), "Could not move to child.");
1183 HRBM(reader.MoveToNextSibling(&backdropElement), "Could not move to sibiling.");
1184 canvas.saveLayer(nullptr, nullptr);
1185 this->drawColorV1Paint(canvas, reader, backdropElement);
1186
1187 HRBM(reader.MoveToParent(), "Could not move to parent.");
1188 HRBM(reader.MoveToFirstChild(&sourceElement), "Could not move to child.");
1189 canvas.saveLayer(nullptr, &blendModePaint);
1190 this->drawColorV1Paint(canvas, reader, sourceElement);
1191
1192 HRBM(reader.MoveToParent(), "Could not move to parent.");
1193
1194 return true;
1195 }
1196
1197 default:
1198 return false;
1199 }
1200}
1201
1202bool SkScalerContext_DW::drawColorV1Image(const SkGlyph& glyph, SkCanvas& canvas) {
1203 DWriteFontTypeface* typeface = this->getDWriteTypeface();
1204 IDWriteFontFace7* fontFace = typeface->fDWriteFontFace7/*.get()*/;
1205 if (!fontFace) {
1206 return false;
1207 }
1208 UINT32 glyphIndex = glyph.getGlyphID();
1209
1210 SkTScopedComPtr<IDWritePaintReader> paintReader;
1211 HRBM(fontFace->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1212 DWRITE_PAINT_FEATURE_LEVEL_COLR_V1,
1213 &paintReader),
1214 "Could not create paint reader.");
1215
1216 DWRITE_PAINT_ELEMENT paintElement;
1217 D2D_RECT_F clipBox;
1218 DWRITE_PAINT_ATTRIBUTES attributes;
1219 HRBM(paintReader->SetCurrentGlyph(glyphIndex, &paintElement, &clipBox, &attributes),
1220 "Could not set current glyph.");
1221
1222 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1223 // Does not have paint layers, try another format.
1224 return false;
1225 }
1226
1227 // All coordinates (including top level clip) are reported in "em"s (1 == em).
1228 // Size up all em units to the current size and transform.
1229 // Get glyph paths at render size, divide out the render size to get em units.
1230
1231 SkMatrix matrix = fSkXform;
1232 SkScalar scale = fTextSizeRender;
1233 matrix.preScale(scale, scale);
1234 if (this->isSubpixel()) {
1236 SkFixedToScalar(glyph.getSubYFixed()));
1237 }
1238 canvas.concat(matrix);
1239
1240 if (D2D_RECT_F_is_empty(clipBox)) {
1241 // Does not have a clip box
1242 } else {
1243 canvas.clipRect(sk_rect_from(clipBox));
1244 }
1245
1246 // The DirectWrite interface returns resolved colors if these are provided.
1247 // Indexes and alphas are reported but there is no reason to duplicate the color calculation.
1248 paintReader->SetTextColor(dw_color_from(SkColor4f::FromColor(fRec.fForegroundColor)));
1249 paintReader->SetCustomColorPalette(typeface->fDWPalette.get(), typeface->fPaletteEntryCount);
1250
1251 return this->drawColorV1Paint(canvas, *paintReader, paintElement);
1252}
1253
1254bool SkScalerContext_DW::generateColorV1Image(const SkGlyph& glyph, void* imageBuffer) {
1256
1257 SkBitmap dstBitmap;
1258 // TODO: mark this as sRGB when the blits will be sRGB.
1259 dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
1260 kN32_SkColorType, kPremul_SkAlphaType),
1261 glyph.rowBytes());
1262 dstBitmap.setPixels(imageBuffer);
1263
1264 SkCanvas canvas(dstBitmap);
1265 if constexpr (kSkShowTextBlitCoverage) {
1266 canvas.clear(0x33FF0000);
1267 } else {
1268 canvas.clear(SK_ColorTRANSPARENT);
1269 }
1270 canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
1271
1272 return this->drawColorV1Image(glyph, canvas);
1273}
1274
1275bool SkScalerContext_DW::generateColorV1PaintBounds(
1276 SkMatrix* ctm, SkRect* bounds,
1277 IDWritePaintReader& reader, DWRITE_PAINT_ELEMENT const & element)
1278{
1279 // Helper to iterate over the specified number of children.
1280 auto boundChildren = [&](UINT32 childCount) -> bool {
1281 if (childCount == 0) {
1282 return true;
1283 }
1284 DWRITE_PAINT_ELEMENT childElement;
1285 HRB(reader.MoveToFirstChild(&childElement));
1286 this->generateColorV1PaintBounds(ctm, bounds, reader, childElement);
1287
1288 for (uint32_t i = 1; i < childCount; ++i) {
1289 HRB(reader.MoveToNextSibling(&childElement));
1290 this->generateColorV1PaintBounds(ctm, bounds, reader, childElement);
1291 }
1292
1293 HRB(reader.MoveToParent());
1294 return true;
1295 };
1296
1297 SkMatrix restoreMatrix = *ctm;
1298 SK_AT_SCOPE_EXIT(*ctm = restoreMatrix);
1299
1300 switch (element.paintType) {
1301 case DWRITE_PAINT_TYPE_NONE:
1302 return false;
1303
1304 case DWRITE_PAINT_TYPE_LAYERS: {
1305 // A layers paint element has a variable number of children.
1306 return boundChildren(element.paint.layers.childCount);
1307 }
1308
1309 case DWRITE_PAINT_TYPE_SOLID_GLYPH: {
1310 // A solid glyph paint element has no children.
1311 // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes
1312
1313 SkPath path;
1314 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1315 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
1316 "Could not create geometry to path converter.");
1317 UINT16 glyphId = SkTo<UINT16>(element.paint.solidGlyph.glyphIndex);
1318 {
1319 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
1320 HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
1321 SkScalarToFloat(fTextSizeRender),
1322 &glyphId,
1323 nullptr, //advances
1324 nullptr, //offsets
1325 1, //num glyphs
1326 FALSE, //sideways
1327 FALSE, //rtl
1328 geometryToPath.get()),
1329 "Could not create glyph outline.");
1330 }
1331
1332 SkMatrix t = *ctm;
1333 t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
1334 path.transform(t);
1335 bounds->join(path.getBounds());
1336 return true;
1337 }
1338
1339 case DWRITE_PAINT_TYPE_SOLID: {
1340 return true;
1341 }
1342
1343 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT: {
1344 return true;
1345 }
1346
1347 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT: {
1348 return true;
1349 }
1350
1351 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT: {
1352 return true;
1353 }
1354
1355 case DWRITE_PAINT_TYPE_GLYPH: {
1356 // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex.
1357 SkPath path;
1358 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1359 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
1360 "Could not create geometry to path converter.");
1361 UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex);
1362 {
1363 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
1364 HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
1365 SkScalarToFloat(fTextSizeRender),
1366 &glyphId,
1367 nullptr, //advances
1368 nullptr, //offsets
1369 1, //num glyphs
1370 FALSE, //sideways
1371 FALSE, //rtl
1372 geometryToPath.get()),
1373 "Could not create glyph outline.");
1374 }
1375
1376 SkMatrix t = *ctm;
1377 t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender));
1378 path.transform(t);
1379 bounds->join(path.getBounds());
1380 return true;
1381 }
1382
1383 case DWRITE_PAINT_TYPE_COLOR_GLYPH: {
1384 // A color glyph paint element has one child, which is the root
1385 // of the paint tree for the glyph specified by glyphIndex.
1386 auto const& colorGlyph = element.paint.colorGlyph;
1387 if (D2D_RECT_F_is_empty(colorGlyph.clipBox)) {
1388 // Does not have a clip box
1389 return boundChildren(1);
1390 }
1391 SkRect r = sk_rect_from(colorGlyph.clipBox);
1392 ctm->mapRect(r);
1393 bounds->join(r);
1394 return true;
1395 }
1396
1397 case DWRITE_PAINT_TYPE_TRANSFORM: {
1398 // A transform paint element always has one child, which is the transformed content.
1399 ctm->preConcat(sk_matrix_from(element.paint.transform));
1400 return boundChildren(1);
1401 }
1402
1403 case DWRITE_PAINT_TYPE_COMPOSITE: {
1404 // A composite paint element has two children, the source and destination of the operation.
1405 return boundChildren(2);
1406 }
1407
1408 default:
1409 return false;
1410 }
1411}
1412
1413bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph& glyph, SkRect* bounds) {
1414 DWriteFontTypeface* typeface = this->getDWriteTypeface();
1415 IDWriteFontFace7* fontFace = typeface->fDWriteFontFace7/*.get()*/;
1416 if (!fontFace) {
1417 return false;
1418 }
1419 UINT32 glyphIndex = glyph.getGlyphID();
1420
1421 SkTScopedComPtr<IDWritePaintReader> paintReader;
1422 HRESULT hr;
1423 // No message on failure here, since this will fail if the font has no color glyphs.
1424 hr = fontFace->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1425 DWRITE_PAINT_FEATURE_LEVEL_COLR_V1,
1426 &paintReader);
1427 if (FAILED(hr)) {
1428 return false;
1429 }
1430
1431 DWRITE_PAINT_ELEMENT paintElement;
1432 D2D_RECT_F clipBox;
1433 DWRITE_PAINT_ATTRIBUTES attributes;
1434 // If the glyph is not color this will succeed but return paintType NONE.
1435 HRBM(paintReader->SetCurrentGlyph(glyphIndex, &paintElement, &clipBox, &attributes),
1436 "Could not set the current glyph.");
1437
1438 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1439 // Does not have paint layers, try another format.
1440 return false;
1441 }
1442
1443 // All coordinates (including top level clip) are reported in "em"s (1 == em).
1444 // Size up all em units to the current size and transform.
1445
1446 SkMatrix matrix = fSkXform;
1447 SkScalar scale = fTextSizeRender;
1448 matrix.preScale(scale, scale);
1449 if (this->isSubpixel()) {
1451 SkFixedToScalar(glyph.getSubYFixed()));
1452 }
1453
1454 SkRect r;
1455 if (D2D_RECT_F_is_empty(clipBox)) {
1456 // Does not have a clip box.
1457 r = SkRect::MakeEmpty();
1458 if (!this->generateColorV1PaintBounds(&matrix, &r, *paintReader, paintElement)) {
1459 return false;
1460 }
1461 *bounds = r;
1462 } else {
1463 *bounds = sk_rect_from(clipBox);
1464 matrix.mapRect(bounds);
1465 }
1466 return true;
1467}
1468
1469#else // DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
1470
1471bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph&, SkRect*) { return false; }
1472bool SkScalerContext_DW::generateColorV1Image(const SkGlyph&, void*) { return false; }
1473bool SkScalerContext_DW::drawColorV1Image(const SkGlyph&, SkCanvas&) { return false; }
1474
1475#endif // DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
1476
1477bool SkScalerContext_DW::setAdvance(const SkGlyph& glyph, SkVector* advance) {
1478 *advance = {0, 0};
1479 uint16_t glyphId = glyph.getGlyphID();
1480 DWriteFontTypeface* typeface = this->getDWriteTypeface();
1481
1482 // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0.
1483 // For consistency with all other backends, treat out of range glyph ids as an error.
1484 if (fGlyphCount <= glyphId) {
1485 return false;
1486 }
1487
1488 DWRITE_GLYPH_METRICS gm;
1489
1490 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
1491 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
1492 {
1493 Exclusive l(maybe_dw_mutex(*typeface));
1494 HRBM(typeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
1495 fTextSizeMeasure,
1496 1.0f, // pixelsPerDip
1497 // This parameter does not act like the lpmat2 parameter to GetGlyphOutlineW.
1498 // If it did then GsA here and G_inv below to mapVectors.
1499 nullptr,
1500 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
1501 &glyphId, 1,
1502 &gm),
1503 "Could not get gdi compatible glyph metrics.");
1504 } else {
1505 Exclusive l(maybe_dw_mutex(*typeface));
1506 HRBM(typeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
1507 "Could not get design metrics.");
1508 }
1509
1510 DWRITE_FONT_METRICS dwfm;
1511 {
1512 Shared l(maybe_dw_mutex(*typeface));
1513 typeface->fDWriteFontFace->GetMetrics(&dwfm);
1514 }
1515 SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
1516
1517 *advance = { advanceX, 0 };
1518 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
1519 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
1520 {
1521 // DirectWrite produced 'compatible' metrics, but while close,
1522 // the end result is not always an integer as it would be with GDI.
1523 advance->fX = SkScalarRoundToScalar(advance->fX);
1524 }
1525 fSkXform.mapVectors(advance, 1);
1526 return true;
1527}
1528
1529bool SkScalerContext_DW::generateDWMetrics(const SkGlyph& glyph,
1530 DWRITE_RENDERING_MODE renderingMode,
1531 DWRITE_TEXTURE_TYPE textureType,
1532 SkRect* bounds)
1533{
1534 DWriteFontTypeface* typeface = this->getDWriteTypeface();
1535
1536 //Measure raster size.
1537 fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
1538 fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
1539
1540 FLOAT advance = 0;
1541
1542 UINT16 glyphId = glyph.getGlyphID();
1543
1544 DWRITE_GLYPH_OFFSET offset;
1545 offset.advanceOffset = 0.0f;
1546 offset.ascenderOffset = 0.0f;
1547
1548 DWRITE_GLYPH_RUN run;
1549 run.glyphCount = 1;
1550 run.glyphAdvances = &advance;
1551 run.fontFace = typeface->fDWriteFontFace.get();
1552 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
1553 run.bidiLevel = 0;
1554 run.glyphIndices = &glyphId;
1555 run.isSideways = FALSE;
1556 run.glyphOffsets = &offset;
1557
1558 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
1559 {
1560 Exclusive l(maybe_dw_mutex(*typeface));
1561 // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
1562 if (typeface->fFactory2 &&
1563 (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
1564 fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
1565 {
1566 HRBM(typeface->fFactory2->CreateGlyphRunAnalysis(
1567 &run,
1568 &fXform,
1569 renderingMode,
1570 fMeasuringMode,
1571 fGridFitMode,
1572 fAntiAliasMode,
1573 0.0f, // baselineOriginX,
1574 0.0f, // baselineOriginY,
1575 &glyphRunAnalysis),
1576 "Could not create DW2 glyph run analysis.");
1577 } else {
1578 HRBM(typeface->fFactory->CreateGlyphRunAnalysis(&run,
1579 1.0f, // pixelsPerDip,
1580 &fXform,
1581 renderingMode,
1582 fMeasuringMode,
1583 0.0f, // baselineOriginX,
1584 0.0f, // baselineOriginY,
1585 &glyphRunAnalysis),
1586 "Could not create glyph run analysis.");
1587 }
1588 }
1589 RECT bbox;
1590 {
1591 Shared l(maybe_dw_mutex(*typeface));
1592 HRBM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, &bbox),
1593 "Could not get texture bounds.");
1594 }
1595
1596 // GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
1597 // { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
1598 // for small but not quite zero and large (but not really large) glyphs,
1599 // Only set as non-empty if the returned bounds are non-empty.
1600 if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
1601 return false;
1602 }
1603
1604 *bounds = SkRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom);
1605 return true;
1606}
1607
1608bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
1609 IDWriteColorGlyphRunEnumerator** colorGlyph)
1610{
1611 FLOAT advance = 0;
1612 UINT16 glyphId = glyph.getGlyphID();
1613
1614 DWRITE_GLYPH_OFFSET offset;
1615 offset.advanceOffset = 0.0f;
1616 offset.ascenderOffset = 0.0f;
1617
1618 DWRITE_GLYPH_RUN run;
1619 run.glyphCount = 1;
1620 run.glyphAdvances = &advance;
1621 run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
1622 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
1623 run.bidiLevel = 0;
1624 run.glyphIndices = &glyphId;
1625 run.isSideways = FALSE;
1626 run.glyphOffsets = &offset;
1627
1628 HRESULT hr = this->getDWriteTypeface()->fFactory2->TranslateColorGlyphRun(
1629 0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
1630 if (hr == DWRITE_E_NOCOLOR) {
1631 return false;
1632 }
1633 HRBM(hr, "Failed to translate color glyph run");
1634 return true;
1635}
1636
1637bool SkScalerContext_DW::generateColorMetrics(const SkGlyph& glyph, SkRect* bounds) {
1638 SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
1639 if (!getColorGlyphRun(glyph, &colorLayers)) {
1640 return false;
1641 }
1642 SkASSERT(colorLayers.get());
1643
1645 BOOL hasNextRun = FALSE;
1646 while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
1647 const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
1648 HRBM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
1649
1650 SkPath path;
1651 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1652 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
1653 "Could not create geometry to path converter.");
1654 {
1655 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
1656 HRBM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
1657 colorGlyph->glyphRun.fontEmSize,
1658 colorGlyph->glyphRun.glyphIndices,
1659 colorGlyph->glyphRun.glyphAdvances,
1660 colorGlyph->glyphRun.glyphOffsets,
1661 colorGlyph->glyphRun.glyphCount,
1662 colorGlyph->glyphRun.isSideways,
1663 colorGlyph->glyphRun.bidiLevel % 2, //rtl
1664 geometryToPath.get()),
1665 "Could not create glyph outline.");
1666 }
1667 bounds->join(path.getBounds());
1668 }
1669 SkMatrix matrix = fSkXform;
1670 if (this->isSubpixel()) {
1672 SkFixedToScalar(glyph.getSubYFixed()));
1673 }
1674 matrix.mapRect(bounds);
1675 return true;
1676}
1677
1678bool SkScalerContext_DW::generateSVGMetrics(const SkGlyph& glyph, SkRect* bounds) {
1679 SkPictureRecorder recorder;
1683 SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
1684 if (!this->drawSVGImage(glyph, *recordingCanvas)) {
1685 return false;
1686 }
1688 *bounds = pic->cullRect();
1689 SkASSERT(bounds->isFinite());
1690 bounds->roundOut(bounds);
1691 return true;
1692}
1693
1694namespace {
1695struct Context {
1696 SkTScopedComPtr<IDWriteFontFace4> fontFace4;
1697 void* glyphDataContext;
1698 Context(IDWriteFontFace4* face4, void* context)
1699 : fontFace4(SkRefComPtr(face4))
1700 , glyphDataContext(context)
1701 {}
1702};
1703
1704static void ReleaseProc(const void* ptr, void* context) {
1705 Context* ctx = (Context*)context;
1706 ctx->fontFace4->ReleaseGlyphImageData(ctx->glyphDataContext);
1707 delete ctx;
1708}
1709}
1710
1711bool SkScalerContext_DW::generatePngMetrics(const SkGlyph& glyph, SkRect* bounds) {
1712 IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
1713 if (!fontFace4) {
1714 return false;
1715 }
1716
1717 DWRITE_GLYPH_IMAGE_FORMATS imageFormats;
1718 HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &imageFormats),
1719 "Cannot get glyph image formats.");
1720 if (!(imageFormats & DWRITE_GLYPH_IMAGE_FORMATS_PNG)) {
1721 return false;
1722 }
1723
1724 DWRITE_GLYPH_IMAGE_DATA glyphData;
1725 void* glyphDataContext;
1726 HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
1727 fTextSizeRender,
1728 DWRITE_GLYPH_IMAGE_FORMATS_PNG,
1729 &glyphData,
1730 &glyphDataContext),
1731 "Glyph image data could not be acquired.");
1732
1733 Context* context = new Context(fontFace4, glyphDataContext);
1734 sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
1735 glyphData.imageDataSize,
1736 &ReleaseProc,
1737 context);
1738
1739 std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(std::move(data), nullptr);
1740 if (!codec) {
1741 return false;
1742 }
1743
1744 SkImageInfo info = codec->getInfo();
1745 *bounds = SkRect::MakeLTRB(SkIntToScalar(info.bounds().fLeft),
1746 SkIntToScalar(info.bounds().fTop),
1747 SkIntToScalar(info.bounds().fRight),
1748 SkIntToScalar(info.bounds().fBottom));
1749
1750 SkMatrix matrix = fSkXform;
1751 SkScalar scale = fTextSizeRender / glyphData.pixelsPerEm;
1752 matrix.preScale(scale, scale);
1753 matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
1754 if (this->isSubpixel()) {
1756 SkFixedToScalar(glyph.getSubYFixed()));
1757 }
1758 matrix.mapRect(bounds);
1759 bounds->roundOut(bounds);
1760 return true;
1761}
1762
1764 SkArenaAlloc* alloc) {
1765 GlyphMetrics mx(glyph.maskFormat());
1766
1767 mx.extraBits = ScalerContextBits::NONE;
1768
1769 if (!this->setAdvance(glyph, &mx.advance)) {
1770 return mx;
1771 }
1772
1773 DWriteFontTypeface* typeface = this->getDWriteTypeface();
1774 if (typeface->fIsColorFont) {
1775 if (generateColorV1Metrics(glyph, &mx.bounds)) {
1776 mx.maskFormat = SkMask::kARGB32_Format;
1777 mx.extraBits |= ScalerContextBits::COLRv1;
1778 mx.neverRequestPath = true;
1779 return mx;
1780 }
1781
1782 if (generateColorMetrics(glyph, &mx.bounds)) {
1783 mx.maskFormat = SkMask::kARGB32_Format;
1784 mx.extraBits |= ScalerContextBits::COLR;
1785 mx.neverRequestPath = true;
1786 return mx;
1787 }
1788
1789 if (generateSVGMetrics(glyph, &mx.bounds)) {
1790 mx.maskFormat = SkMask::kARGB32_Format;
1791 mx.extraBits |= ScalerContextBits::SVG;
1792 mx.neverRequestPath = true;
1793 return mx;
1794 }
1795
1796 if (generatePngMetrics(glyph, &mx.bounds)) {
1797 mx.maskFormat = SkMask::kARGB32_Format;
1798 mx.extraBits |= ScalerContextBits::PNG;
1799 mx.neverRequestPath = true;
1800 return mx;
1801 }
1802 }
1803
1804 if (this->generateDWMetrics(glyph, fRenderingMode, fTextureType, &mx.bounds)) {
1805 mx.extraBits = ScalerContextBits::DW;
1806 return mx;
1807 }
1808
1809 // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
1810 // glyphs of the specified texture type or it is too big for smoothing.
1811 // When this happens, try with the alternate texture type.
1812 if (DWRITE_TEXTURE_ALIASED_1x1 != fTextureType ||
1813 DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE == fAntiAliasMode)
1814 {
1815 if (this->generateDWMetrics(glyph,
1816 DWRITE_RENDERING_MODE_ALIASED,
1817 DWRITE_TEXTURE_ALIASED_1x1,
1818 &mx.bounds))
1819 {
1820 mx.maskFormat = SkMask::kBW_Format;
1821 mx.extraBits = ScalerContextBits::DW_1;
1822 return mx;
1823 }
1824 }
1825 // TODO: Try DWRITE_TEXTURE_CLEARTYPE_3x1 if DWRITE_TEXTURE_ALIASED_1x1 fails
1826
1827 // GetAlphaTextureBounds can fail for various reasons.
1828 // As a fallback, attempt to generate the metrics and image from the path.
1829 mx.computeFromPath = true;
1830 mx.extraBits = ScalerContextBits::PATH;
1831 return mx;
1832}
1833
1835 if (nullptr == metrics) {
1836 return;
1837 }
1838
1839 sk_bzero(metrics, sizeof(*metrics));
1840
1841 DWRITE_FONT_METRICS dwfm;
1842 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
1843 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
1844 {
1845 this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleMetrics(
1846 fTextSizeRender,
1847 1.0f, // pixelsPerDip
1848 &fXform,
1849 &dwfm);
1850 } else {
1851 this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
1852 }
1853
1854 SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
1855
1856 metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
1857 metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
1858 metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
1859 metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
1860 metrics->fCapHeight = fTextSizeRender * SkIntToScalar(dwfm.capHeight) / upem;
1861 metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
1862 metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
1863 metrics->fStrikeoutThickness = fTextSizeRender * SkIntToScalar(dwfm.strikethroughThickness) / upem;
1864 metrics->fStrikeoutPosition = -(fTextSizeRender * SkIntToScalar(dwfm.strikethroughPosition) / upem);
1865
1870
1871 SkTScopedComPtr<IDWriteFontFace5> fontFace5;
1872 if (SUCCEEDED(this->getDWriteTypeface()->fDWriteFontFace->QueryInterface(&fontFace5))) {
1873 if (fontFace5->HasVariations()) {
1874 // The bounds are only valid for the default variation.
1876 }
1877 }
1878
1879 if (this->getDWriteTypeface()->fDWriteFontFace1.get()) {
1880 DWRITE_FONT_METRICS1 dwfm1;
1881 this->getDWriteTypeface()->fDWriteFontFace1->GetMetrics(&dwfm1);
1882 metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
1883 metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
1884 metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
1885 metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
1886
1887 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
1888 return;
1889 }
1890
1891 AutoTDWriteTable<SkOTTableHead> head(this->getDWriteTypeface()->fDWriteFontFace.get());
1892 if (head.fExists &&
1893 head.fSize >= sizeof(SkOTTableHead) &&
1894 head->version == SkOTTableHead::version1)
1895 {
1896 metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
1897 metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
1898 metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
1899 metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
1900
1901 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
1902 return;
1903 }
1904
1905 // The real bounds weren't actually available.
1907 metrics->fTop = metrics->fAscent;
1908 metrics->fBottom = metrics->fDescent;
1909}
1910
1911///////////////////////////////////////////////////////////////////////////////
1912
1914
1915void SkScalerContext_DW::BilevelToBW(const uint8_t* SK_RESTRICT src,
1916 const SkGlyph& glyph, void* imageBuffer) {
1917 const int width = glyph.width();
1918 const size_t dstRB = (width + 7) >> 3;
1919 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
1920
1921 int byteCount = width >> 3;
1922 int bitCount = width & 7;
1923
1924 for (int y = 0; y < glyph.height(); ++y) {
1925 if (byteCount > 0) {
1926 for (int i = 0; i < byteCount; ++i) {
1927 unsigned byte = 0;
1928 byte |= src[0] & (1 << 7);
1929 byte |= src[1] & (1 << 6);
1930 byte |= src[2] & (1 << 5);
1931 byte |= src[3] & (1 << 4);
1932 byte |= src[4] & (1 << 3);
1933 byte |= src[5] & (1 << 2);
1934 byte |= src[6] & (1 << 1);
1935 byte |= src[7] & (1 << 0);
1936 dst[i] = byte;
1937 src += 8;
1938 }
1939 }
1940 if (bitCount > 0) {
1941 unsigned byte = 0;
1942 unsigned mask = 0x80;
1943 for (int i = 0; i < bitCount; i++) {
1944 byte |= (src[i]) & mask;
1945 mask >>= 1;
1946 }
1947 dst[byteCount] = byte;
1948 }
1949 src += bitCount;
1950 dst += dstRB;
1951 }
1952
1953 if constexpr (kSkShowTextBlitCoverage) {
1954 dst = static_cast<uint8_t*>(imageBuffer);
1955 for (unsigned y = 0; y < (unsigned)glyph.height(); y += 2) {
1956 for (unsigned x = (y & 0x2); x < (unsigned)glyph.width(); x+=4) {
1957 uint8_t& b = dst[(dstRB * y) + (x >> 3)];
1958 b = b ^ (1 << (0x7 - (x & 0x7)));
1959 }
1960 }
1961 }
1962}
1963
1964template<bool APPLY_PREBLEND>
1965void SkScalerContext_DW::GrayscaleToA8(const uint8_t* SK_RESTRICT src,
1966 const SkGlyph& glyph, void* imageBuffer,
1967 const uint8_t* table8) {
1968 const size_t dstRB = glyph.rowBytes();
1969 const int width = glyph.width();
1970 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
1971
1972 for (int y = 0; y < glyph.height(); y++) {
1973 for (int i = 0; i < width; i++) {
1974 U8CPU a = *(src++);
1975 dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(a, table8);
1976 if constexpr (kSkShowTextBlitCoverage) {
1977 dst[i] = std::max<U8CPU>(0x30, dst[i]);
1978 }
1979 }
1980 dst = SkTAddOffset<uint8_t>(dst, dstRB);
1981 }
1982}
1983
1984template<bool APPLY_PREBLEND>
1985void SkScalerContext_DW::RGBToA8(const uint8_t* SK_RESTRICT src,
1986 const SkGlyph& glyph, void* imageBuffer,
1987 const uint8_t* table8) {
1988 const size_t dstRB = glyph.rowBytes();
1989 const int width = glyph.width();
1990 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(imageBuffer);
1991
1992 for (int y = 0; y < glyph.height(); y++) {
1993 for (int i = 0; i < width; i++) {
1994 U8CPU r = *(src++);
1995 U8CPU g = *(src++);
1996 U8CPU b = *(src++);
1997 dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
1998 if constexpr (kSkShowTextBlitCoverage) {
1999 dst[i] = std::max<U8CPU>(0x30, dst[i]);
2000 }
2001 }
2002 dst = SkTAddOffset<uint8_t>(dst, dstRB);
2003 }
2004}
2005
2006template<bool APPLY_PREBLEND, bool RGB>
2007void SkScalerContext_DW::RGBToLcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
2008 void* imageBuffer,
2009 const uint8_t* tableR, const uint8_t* tableG,
2010 const uint8_t* tableB) {
2011 const size_t dstRB = glyph.rowBytes();
2012 const int width = glyph.width();
2013 uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(imageBuffer);
2014
2015 for (int y = 0; y < glyph.height(); y++) {
2016 for (int i = 0; i < width; i++) {
2017 U8CPU r, g, b;
2018 if (RGB) {
2019 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
2020 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
2021 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
2022 } else {
2023 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
2024 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
2025 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
2026 }
2027 if constexpr (kSkShowTextBlitCoverage) {
2028 r = std::max<U8CPU>(0x30, r);
2029 g = std::max<U8CPU>(0x30, g);
2030 b = std::max<U8CPU>(0x30, b);
2031 }
2032 dst[i] = SkPack888ToRGB16(r, g, b);
2033 }
2034 dst = SkTAddOffset<uint16_t>(dst, dstRB);
2035 }
2036}
2037
2038const void* SkScalerContext_DW::getDWMaskBits(const SkGlyph& glyph,
2039 DWRITE_RENDERING_MODE renderingMode,
2040 DWRITE_TEXTURE_TYPE textureType)
2041{
2042 DWriteFontTypeface* typeface = this->getDWriteTypeface();
2043
2044 int sizeNeeded = glyph.width() * glyph.height();
2045 if (DWRITE_TEXTURE_CLEARTYPE_3x1 == textureType) {
2046 sizeNeeded *= 3;
2047 }
2048 if (sizeNeeded > fBits.size()) {
2049 fBits.resize(sizeNeeded);
2050 }
2051
2052 // erase
2053 memset(fBits.begin(), 0, sizeNeeded);
2054
2055 fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
2056 fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
2057
2058 FLOAT advance = 0.0f;
2059
2060 UINT16 index = glyph.getGlyphID();
2061
2062 DWRITE_GLYPH_OFFSET offset;
2063 offset.advanceOffset = 0.0f;
2064 offset.ascenderOffset = 0.0f;
2065
2066 DWRITE_GLYPH_RUN run;
2067 run.glyphCount = 1;
2068 run.glyphAdvances = &advance;
2069 run.fontFace = typeface->fDWriteFontFace.get();
2070 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
2071 run.bidiLevel = 0;
2072 run.glyphIndices = &index;
2073 run.isSideways = FALSE;
2074 run.glyphOffsets = &offset;
2075 {
2076 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
2077 {
2078 Exclusive l(maybe_dw_mutex(*typeface));
2079 // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
2080 if (typeface->fFactory2 &&
2081 (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
2082 fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
2083 {
2084 HRNM(typeface->fFactory2->CreateGlyphRunAnalysis(&run,
2085 &fXform,
2086 renderingMode,
2087 fMeasuringMode,
2088 fGridFitMode,
2089 fAntiAliasMode,
2090 0.0f, // baselineOriginX,
2091 0.0f, // baselineOriginY,
2092 &glyphRunAnalysis),
2093 "Could not create DW2 glyph run analysis.");
2094 } else {
2095 HRNM(typeface->fFactory->CreateGlyphRunAnalysis(&run,
2096 1.0f, // pixelsPerDip,
2097 &fXform,
2098 renderingMode,
2099 fMeasuringMode,
2100 0.0f, // baselineOriginX,
2101 0.0f, // baselineOriginY,
2102 &glyphRunAnalysis),
2103 "Could not create glyph run analysis.");
2104 }
2105 }
2106 //NOTE: this assumes that the glyph has already been measured
2107 //with an exact same glyph run analysis.
2108 RECT bbox;
2109 bbox.left = glyph.left();
2110 bbox.top = glyph.top();
2111 bbox.right = glyph.left() + glyph.width();
2112 bbox.bottom = glyph.top() + glyph.height();
2113 {
2114 Shared l(maybe_dw_mutex(*typeface));
2115 HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
2116 &bbox,
2117 fBits.begin(),
2118 sizeNeeded),
2119 "Could not draw mask.");
2120 }
2121 }
2122 return fBits.begin();
2123}
2124
2125bool SkScalerContext_DW::generateDWImage(const SkGlyph& glyph, void* imageBuffer) {
2126 //Create the mask.
2127 ScalerContextBits::value_type format = glyph.extraBits();
2128 DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
2129 DWRITE_TEXTURE_TYPE textureType = fTextureType;
2130 if (format == ScalerContextBits::DW_1) {
2131 renderingMode = DWRITE_RENDERING_MODE_ALIASED;
2132 textureType = DWRITE_TEXTURE_ALIASED_1x1;
2133 }
2134 const void* bits = this->getDWMaskBits(glyph, renderingMode, textureType);
2135 if (!bits) {
2136 sk_bzero(imageBuffer, glyph.imageSize());
2137 return false;
2138 }
2139
2140 //Copy the mask into the glyph.
2141 const uint8_t* src = (const uint8_t*)bits;
2142 if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
2144 SkASSERT(DWRITE_TEXTURE_ALIASED_1x1 == textureType);
2145 BilevelToBW(src, glyph, imageBuffer);
2146 } else if (!isLCD(fRec)) {
2147 if (textureType == DWRITE_TEXTURE_ALIASED_1x1) {
2148 if (fPreBlend.isApplicable()) {
2149 GrayscaleToA8<true>(src, glyph, imageBuffer, fPreBlend.fG);
2150 } else {
2151 GrayscaleToA8<false>(src, glyph, imageBuffer, fPreBlend.fG);
2152 }
2153 } else {
2154 if (fPreBlend.isApplicable()) {
2155 RGBToA8<true>(src, glyph, imageBuffer, fPreBlend.fG);
2156 } else {
2157 RGBToA8<false>(src, glyph, imageBuffer, fPreBlend.fG);
2158 }
2159 }
2160 } else {
2162 if (fPreBlend.isApplicable()) {
2164 RGBToLcd16<true, false>(src, glyph, imageBuffer,
2166 } else {
2167 RGBToLcd16<true, true>(src, glyph, imageBuffer,
2169 }
2170 } else {
2172 RGBToLcd16<false, false>(src, glyph, imageBuffer,
2174 } else {
2175 RGBToLcd16<false, true>(src, glyph, imageBuffer,
2177 }
2178 }
2179 }
2180 return true;
2181}
2182
2183bool SkScalerContext_DW::drawColorImage(const SkGlyph& glyph, SkCanvas& canvas) {
2184 SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
2185 if (!getColorGlyphRun(glyph, &colorLayers)) {
2186 SkASSERTF(false, "Could not get color layers");
2187 return false;
2188 }
2189
2190 SkPaint paint;
2191 paint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
2192
2193 if (this->isSubpixel()) {
2194 canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
2195 SkFixedToScalar(glyph.getSubYFixed()));
2196 }
2197 canvas.concat(fSkXform);
2198
2199 DWriteFontTypeface* typeface = this->getDWriteTypeface();
2200 size_t paletteEntryCount = typeface->fPaletteEntryCount;
2201 SkColor* palette = typeface->fPalette.get();
2202 BOOL hasNextRun = FALSE;
2203 while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
2204 const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
2205 HRBM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
2206
2207 SkColor color;
2208 if (colorGlyph->paletteIndex == 0xffff) {
2210 } else if (colorGlyph->paletteIndex < paletteEntryCount) {
2211 color = palette[colorGlyph->paletteIndex];
2212 } else {
2213 SK_TRACEHR(DWRITE_E_NOCOLOR, "Invalid palette index.");
2215 }
2216 paint.setColor(color);
2217
2218 SkPath path;
2219 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
2220 HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
2221 "Could not create geometry to path converter.");
2222 {
2223 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
2224 HRBM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
2225 colorGlyph->glyphRun.fontEmSize,
2226 colorGlyph->glyphRun.glyphIndices,
2227 colorGlyph->glyphRun.glyphAdvances,
2228 colorGlyph->glyphRun.glyphOffsets,
2229 colorGlyph->glyphRun.glyphCount,
2230 colorGlyph->glyphRun.isSideways,
2231 colorGlyph->glyphRun.bidiLevel % 2, //rtl
2232 geometryToPath.get()),
2233 "Could not create glyph outline.");
2234 }
2235 canvas.drawPath(path, paint);
2236 }
2237 return true;
2238}
2239
2240bool SkScalerContext_DW::generateColorImage(const SkGlyph& glyph, void* imageBuffer) {
2242
2243 SkBitmap dstBitmap;
2244 // TODO: mark this as sRGB when the blits will be sRGB.
2245 dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
2246 kN32_SkColorType, kPremul_SkAlphaType),
2247 glyph.rowBytes());
2248 dstBitmap.setPixels(imageBuffer);
2249
2250 SkCanvas canvas(dstBitmap);
2251 if constexpr (kSkShowTextBlitCoverage) {
2252 canvas.clear(0x33FF0000);
2253 } else {
2254 canvas.clear(SK_ColorTRANSPARENT);
2255 }
2256 canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
2257
2258 return this->drawColorImage(glyph, canvas);
2259}
2260
2261bool SkScalerContext_DW::drawSVGImage(const SkGlyph& glyph, SkCanvas& canvas) {
2262 DWriteFontTypeface* typeface = this->getDWriteTypeface();
2263 IDWriteFontFace4* fontFace4 = typeface->fDWriteFontFace4.get();
2264 if (!fontFace4) {
2265 return false;
2266 }
2267
2268 DWRITE_GLYPH_IMAGE_FORMATS imageFormats;
2269 HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &imageFormats),
2270 "Cannot get glyph image formats.");
2271 if (!(imageFormats & DWRITE_GLYPH_IMAGE_FORMATS_SVG)) {
2272 return false;
2273 }
2274
2276 if (!svgFactory) {
2277 return false;
2278 }
2279
2280 DWRITE_GLYPH_IMAGE_DATA glyphData;
2281 void* glyphDataContext;
2282 HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
2283 fTextSizeRender,
2284 DWRITE_GLYPH_IMAGE_FORMATS_SVG,
2285 &glyphData,
2286 &glyphDataContext),
2287 "Glyph SVG data could not be acquired.");
2288 auto svgDecoder = svgFactory((const uint8_t*)glyphData.imageData, glyphData.imageDataSize);
2289 fontFace4->ReleaseGlyphImageData(glyphDataContext);
2290 if (!svgDecoder) {
2291 return false;
2292 }
2293
2294 size_t paletteEntryCount = typeface->fPaletteEntryCount;
2295 SkColor* palette = typeface->fPalette.get();
2296 int upem = typeface->getUnitsPerEm();
2297
2298 SkMatrix matrix = fSkXform;
2299 SkScalar scale = fTextSizeRender / upem;
2300 matrix.preScale(scale, scale);
2301 matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
2302 if (this->isSubpixel()) {
2304 SkFixedToScalar(glyph.getSubYFixed()));
2305 }
2306 canvas.concat(matrix);
2307
2308 return svgDecoder->render(canvas, upem, glyph.getGlyphID(),
2309 fRec.fForegroundColor, SkSpan(palette, paletteEntryCount));
2310}
2311
2312bool SkScalerContext_DW::generateSVGImage(const SkGlyph& glyph, void* imageBuffer) {
2314
2315 SkBitmap dstBitmap;
2316 // TODO: mark this as sRGB when the blits will be sRGB.
2317 dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
2318 kN32_SkColorType, kPremul_SkAlphaType),
2319 glyph.rowBytes());
2320 dstBitmap.setPixels(imageBuffer);
2321
2322 SkCanvas canvas(dstBitmap);
2323 if constexpr (kSkShowTextBlitCoverage) {
2324 canvas.clear(0x33FF0000);
2325 } else {
2326 canvas.clear(SK_ColorTRANSPARENT);
2327 }
2328 canvas.translate(-SkIntToScalar(glyph.left()), -SkIntToScalar(glyph.top()));
2329
2330 return this->drawSVGImage(glyph, canvas);
2331}
2332
2333bool SkScalerContext_DW::drawPngImage(const SkGlyph& glyph, SkCanvas& canvas) {
2334 IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
2335 if (!fontFace4) {
2336 return false;
2337 }
2338
2339 DWRITE_GLYPH_IMAGE_DATA glyphData;
2340 void* glyphDataContext;
2341 HRBM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
2342 fTextSizeRender,
2343 DWRITE_GLYPH_IMAGE_FORMATS_PNG,
2344 &glyphData,
2345 &glyphDataContext),
2346 "Glyph image data could not be acquired.");
2347 Context* context = new Context(fontFace4, glyphDataContext);
2348 sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
2349 glyphData.imageDataSize,
2350 &ReleaseProc,
2351 context);
2353 if (!image) {
2354 return false;
2355 }
2356
2357 if (this->isSubpixel()) {
2358 canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
2359 SkFixedToScalar(glyph.getSubYFixed()));
2360 }
2361 canvas.concat(fSkXform);
2362 SkScalar ratio = fTextSizeRender / glyphData.pixelsPerEm;
2363 canvas.scale(ratio, ratio);
2364 canvas.translate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
2365 canvas.drawImage(image, 0, 0);
2366 return true;
2367}
2368
2369bool SkScalerContext_DW::generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
2371
2372 SkBitmap dstBitmap;
2373 dstBitmap.setInfo(SkImageInfo::Make(glyph.width(), glyph.height(),
2374 kN32_SkColorType, kPremul_SkAlphaType),
2375 glyph.rowBytes());
2376 dstBitmap.setPixels(imageBuffer);
2377
2378 SkCanvas canvas(dstBitmap);
2379 canvas.clear(SK_ColorTRANSPARENT);
2380 canvas.translate(-glyph.left(), -glyph.top());
2381
2382 return this->drawPngImage(glyph, canvas);
2383}
2384
2385void SkScalerContext_DW::generateImage(const SkGlyph& glyph, void* imageBuffer) {
2386 ScalerContextBits::value_type format = glyph.extraBits();
2387 if (format == ScalerContextBits::DW ||
2388 format == ScalerContextBits::DW_1)
2389 {
2390 this->generateDWImage(glyph, imageBuffer);
2391 } else if (format == ScalerContextBits::COLRv1) {
2392 this->generateColorV1Image(glyph, imageBuffer);
2393 } else if (format == ScalerContextBits::COLR) {
2394 this->generateColorImage(glyph, imageBuffer);
2395 } else if (format == ScalerContextBits::SVG) {
2396 this->generateSVGImage(glyph, imageBuffer);
2397 } else if (format == ScalerContextBits::PNG) {
2398 this->generatePngImage(glyph, imageBuffer);
2399 } else if (format == ScalerContextBits::PATH) {
2400 const SkPath* devPath = glyph.path();
2401 SkASSERT_RELEASE(devPath);
2402 SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer),
2403 glyph.iRect(), glyph.rowBytes(), glyph.maskFormat());
2404 SkASSERT(SkMask::kARGB32_Format != mask.fFormat);
2408 const bool hairline = glyph.pathIsHairline();
2409 GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline);
2410 } else {
2411 SK_ABORT("Bad format");
2412 }
2413}
2414
2415bool SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) {
2416 SkASSERT(path);
2417 path->reset();
2418
2419 SkGlyphID glyphID = glyph.getGlyphID();
2420
2421 // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0.
2422 // For consistency with all other backends, treat out of range glyph ids as an error.
2423 if (fGlyphCount <= glyphID) {
2424 return false;
2425 }
2426
2427 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
2428 HRBM(SkDWriteGeometrySink::Create(path, &geometryToPath),
2429 "Could not create geometry to path converter.");
2430 UINT16 glyphId = SkTo<UINT16>(glyphID);
2431 {
2432 Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface()));
2433 //TODO: convert to<->from DIUs? This would make a difference if hinting.
2434 //It may not be needed, it appears that DirectWrite only hints at em size.
2435 HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
2436 SkScalarToFloat(fTextSizeRender),
2437 &glyphId,
2438 nullptr, //advances
2439 nullptr, //offsets
2440 1, //num glyphs
2441 FALSE, //sideways
2442 FALSE, //rtl
2443 geometryToPath.get()),
2444 "Could not create glyph outline.");
2445 }
2446
2447 path->transform(fSkXform);
2448 return true;
2449}
2450
2452 struct GlyphDrawable : public SkDrawable {
2453 SkScalerContext_DW* fSelf;
2454 SkGlyph fGlyph;
2455 GlyphDrawable(SkScalerContext_DW* self, const SkGlyph& glyph) : fSelf(self), fGlyph(glyph){}
2456 SkRect onGetBounds() override { return fGlyph.rect(); }
2457 size_t onApproximateBytesUsed() override { return sizeof(GlyphDrawable); }
2458 void maybeShowTextBlitCoverage(SkCanvas* canvas) {
2459 if constexpr (kSkShowTextBlitCoverage) {
2460 SkPaint paint;
2461 paint.setColor(0x3300FF00);
2462 paint.setStyle(SkPaint::kFill_Style);
2463 canvas->drawRect(this->onGetBounds(), paint);
2464 }
2465 }
2466 };
2467 struct COLRv1GlyphDrawable : public GlyphDrawable {
2468 using GlyphDrawable::GlyphDrawable;
2469 void onDraw(SkCanvas* canvas) override {
2470 this->maybeShowTextBlitCoverage(canvas);
2471 fSelf->drawColorV1Image(fGlyph, *canvas);
2472 }
2473 };
2474 struct COLRGlyphDrawable : public GlyphDrawable {
2475 using GlyphDrawable::GlyphDrawable;
2476 void onDraw(SkCanvas* canvas) override {
2477 this->maybeShowTextBlitCoverage(canvas);
2478 fSelf->drawColorImage(fGlyph, *canvas);
2479 }
2480 };
2481 struct SVGGlyphDrawable : public GlyphDrawable {
2482 using GlyphDrawable::GlyphDrawable;
2483 void onDraw(SkCanvas* canvas) override {
2484 this->maybeShowTextBlitCoverage(canvas);
2485 fSelf->drawSVGImage(fGlyph, *canvas);
2486 }
2487 };
2488 ScalerContextBits::value_type format = glyph.extraBits();
2489 if (format == ScalerContextBits::COLRv1) {
2490 return sk_sp<SkDrawable>(new COLRv1GlyphDrawable(this, glyph));
2491 }
2492 if (format == ScalerContextBits::COLR) {
2493 return sk_sp<SkDrawable>(new COLRGlyphDrawable(this, glyph));
2494 }
2495 if (format == ScalerContextBits::SVG) {
2496 return sk_sp<SkDrawable>(new SVGGlyphDrawable(this, glyph));
2497 }
2498 return nullptr;
2499}
2500
2501#endif//defined(SK_BUILD_FOR_WIN)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
SkColor4f color
uint16_t fFlags
@ 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
#define SkASSERTF(cond, fmt,...)
Definition SkAssert.h:117
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
unsigned U8CPU
Definition SkCPUTypes.h:18
static U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
static uint16_t SkEndianSwap16(uint16_t value)
Definition SkEndian.h:33
#define SkEndian_SwapBE16(n)
Definition SkEndian.h:135
static constexpr uint32_t SkEndianSwap32(uint32_t value)
Definition SkEndian.h:56
#define SK_RESTRICT
Definition SkFeatures.h:42
#define SkFixedToScalar(x)
Definition SkFixed.h:124
#define SkFixedToFloat(x)
Definition SkFixed.h:41
static bool isLCD(const SkScalerContextRec &rec)
@ kNone
glyph outlines unchanged
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
uint8_t fMax
Definition SkRegion.cpp:826
uint8_t fMin
Definition SkRegion.cpp:825
#define SkScalarTruncToInt(x)
Definition SkScalar.h:59
#define SkScalarToFloat(x)
Definition SkScalar.h:61
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SkScalarRoundToScalar(x)
Definition SkScalar.h:32
#define SK_ScalarInfinity
Definition SkScalar.h:26
#define SK_AT_SCOPE_EXIT(stmt)
Definition SkScopeExit.h:56
#define SK_SCOPED_CAPABILITY
#define SK_RELEASE_CAPABILITY(...)
#define SK_ACQUIRE(...)
#define SK_ACQUIRE_SHARED(...)
SkTileMode
Definition SkTileMode.h:13
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
uint16_t SkGlyphID
Definition SkTypes.h:179
static SkScalar center(float pos0, float pos1)
SkTScopedComPtr< IDWriteFontFace4 > fDWriteFontFace4
SkTScopedComPtr< IDWriteFontFace2 > fDWriteFontFace2
SkTScopedComPtr< IDWriteFactory2 > fFactory2
SkTScopedComPtr< IDWriteFactory > fFactory
std::unique_ptr< SkColor[]> fPalette
IDWriteFontFace7 * fDWriteFontFace7
SkTScopedComPtr< IDWriteFontFace1 > fDWriteFontFace1
std::unique_ptr< DWRITE_COLOR_F[]> fDWPalette
SkTScopedComPtr< IDWriteFontFace > fDWriteFontFace
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 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)
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
static sk_sp< SkColorSpace > MakeSRGB()
static HRESULT Create(SkPath *path, IDWriteGeometrySink **geometryToPath)
static sk_sp< SkData > MakeWithProc(const void *ptr, size_t length, ReleaseProc proc, void *ctx)
Definition SkData.cpp:128
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
size_t imageSize() const
Definition SkGlyph.cpp:241
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)
std::unique_ptr< SkOpenTypeSVGDecoder >(*)(const uint8_t *svg, size_t length) OpenTypeSVGDecoderFactory
Definition SkGraphics.h:164
static OpenTypeSVGDecoderFactory GetOpenTypeSVGDecoderFactory()
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:281
void mapVectors(SkVector dst[], const SkVector src[], int count) const
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
SkMatrix & preConcat(const SkMatrix &other)
Definition SkMatrix.cpp:674
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
void setColor4f(const SkColor4f &color, SkColorSpace *colorSpace=nullptr)
Definition SkPaint.h:253
void setShader(sk_sp< SkShader > shader)
void setBlendMode(SkBlendMode mode)
Definition SkPaint.cpp:151
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
void generateImage(const SkGlyph &, void *imageBuffer) override
~SkScalerContext_DW() override
SkScalerContext_DW(sk_sp< DWriteFontTypeface >, const SkScalerContextEffects &, const SkDescriptor *)
sk_sp< SkDrawable > generateDrawable(const SkGlyph &) override
GlyphMetrics generateMetrics(const SkGlyph &, SkArenaAlloc *) override
void generateFontMetrics(SkFontMetrics *) override
bool generatePath(const SkGlyph &, SkPath *) override
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
bool isSubpixel() const
int size() const
Definition SkTDArray.h:138
T * begin()
Definition SkTDArray.h:150
void resize(int count)
Definition SkTDArray.h:183
const uint8_t * fG
const uint8_t * fB
bool isApplicable() const
const uint8_t * fR
int getUnitsPerEm() const
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
FlutterSemanticsFlag flags
glong glong end
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
return FALSE
double y
double x
SK_API sk_sp< SkImage > DeferredFromEncodedData(sk_sp< SkData > encoded, std::optional< SkAlphaType > alphaType=std::nullopt)
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
Optional< SkRect > bounds
Definition SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
SkScalar startAngle
Definition SkRecords.h:250
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
dst
Definition cp.py:12
Definition run.py:1
Vec< 4, float > float4
Definition SkVx.h:1146
Definition ref_ptr.h:256
int32_t width
const Scalar scale
Point offset
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 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 SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition SkMask.h:31
@ kARGB32_Format
SkPMColor.
Definition SkMask.h:30
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27
static const SK_OT_Fixed version_initial
static const SK_OT_Fixed version_initial
union SkOTTableGridAndScanProcedure::GaspRange::behavior flags
static const SK_OT_USHORT version1
static const SK_OT_USHORT version0
static const SK_OT_Fixed version1
static const SK_OT_Fixed VERSION
static float CrossProduct(const SkVector &a, const SkVector &b)
float fX
x-axis value
static float DotProduct(const SkVector &a, const SkVector &b)
static constexpr SkPoint Make(float x, float y)
float length() const
void scale(float scale, SkPoint *dst) const
Definition SkPoint.cpp:17
constexpr float y() const
bool normalize()
Definition SkPoint.cpp:22
constexpr float x() const
static SkRGBA4f FromColor(SkColor color)
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
SkMask::Format fMaskFormat
SkScalar fPost2x2[2][2]
struct SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw raw
int BOOL
#define SUCCEEDED(hr)
#define FAILED(hr)