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 */
7 #include "src/utils/win/SkDWriteNTDDI_VERSION.h"
8
9 #include "include/core/SkTypes.h"
10 #if defined(SK_BUILD_FOR_WIN)
11
12 #undef GetGlyphIndices
13
14 #include "include/codec/SkCodec.h"
15 #include "include/codec/SkPngDecoder.h"
16 #include "include/core/SkBBHFactory.h"
17 #include "include/core/SkBitmap.h"
18 #include "include/core/SkData.h"
19 #include "include/core/SkDrawable.h"
20 #include "include/core/SkFontMetrics.h"
21 #include "include/core/SkGraphics.h"
22 #include "include/core/SkImage.h"
23 #include "include/core/SkOpenTypeSVGDecoder.h"
24 #include "include/core/SkPath.h"
25 #include "include/core/SkPictureRecorder.h"
26 #include "include/effects/SkGradientShader.h"
27 #include "include/private/base/SkMutex.h"
28 #include "include/private/base/SkTo.h"
29 #include "src/base/SkEndian.h"
30 #include "src/base/SkScopeExit.h"
31 #include "src/base/SkSharedMutex.h"
32 #include "src/core/SkDraw.h"
33 #include "src/core/SkGlyph.h"
34 #include "src/core/SkMaskGamma.h"
35 #include "src/core/SkRasterClip.h"
36 #include "src/core/SkScalerContext.h"
37 #include "src/ports/SkScalerContext_win_dw.h"
38 #include "src/ports/SkTypeface_win_dw.h"
39 #include "src/sfnt/SkOTTable_EBLC.h"
40 #include "src/sfnt/SkOTTable_EBSC.h"
41 #include "src/sfnt/SkOTTable_gasp.h"
42 #include "src/sfnt/SkOTTable_maxp.h"
43 #include "src/utils/SkMatrix22.h"
44 #include "src/utils/win/SkDWrite.h"
45 #include "src/utils/win/SkDWriteGeometrySink.h"
46 #include "src/utils/win/SkHRESULT.h"
47 #include "src/utils/win/SkTScopedComPtr.h"
48
49 #include <dwrite.h>
50 #include <dwrite_1.h>
51 #include <dwrite_3.h>
52
53 namespace {
54 static 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 */
maybe_dw_mutex(DWriteFontTypeface & typeface)61 static SkSharedMutex* maybe_dw_mutex(DWriteFontTypeface& typeface) {
62 static SkSharedMutex mutex;
63 return typeface.fDWriteFontFace4 ? nullptr : &mutex;
64 }
65
66 class SK_SCOPED_CAPABILITY Exclusive {
67 public:
68 explicit Exclusive(SkSharedMutex* maybe_lock) SK_ACQUIRE(*maybe_lock)
69 : fLock(maybe_lock) {
70 if (fLock) {
71 fLock->acquire();
72 }
73 }
SK_RELEASE_CAPABILITY()74 ~Exclusive() SK_RELEASE_CAPABILITY() {
75 if (fLock) {
76 fLock->release();
77 }
78 }
79
80 private:
81 SkSharedMutex* fLock;
82 };
83 class SK_SCOPED_CAPABILITY Shared {
84 public:
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.
SK_RELEASE_CAPABILITY()95 ~Shared() SK_RELEASE_CAPABILITY() {
96 if (fLock) {
97 fLock->releaseShared();
98 }
99 }
100
101 private:
102 SkSharedMutex* fLock;
103 };
104
isLCD(const SkScalerContextRec & rec)105 static bool isLCD(const SkScalerContextRec& rec) {
106 return SkMask::kLCD16_Format == rec.fMaskFormat;
107 }
108
is_hinted(DWriteFontTypeface * typeface)109 static bool is_hinted(DWriteFontTypeface* typeface) {
110 Exclusive l(maybe_dw_mutex(*typeface));
111 AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
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]. */
125 struct GaspRange {
126 using Behavior = SkOTTableGridAndScanProcedure::GaspRange::behavior;
GaspRange__anon0365400f0111::GaspRange127 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
get_gasp_range(DWriteFontTypeface * typeface,int size,GaspRange * range)135 bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) {
136 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
137 if (!gasp.fExists) {
138 return false;
139 }
140 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
141 return false;
142 }
143 if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
144 gasp->version != SkOTTableGridAndScanProcedure::version1)
145 {
146 return false;
147 }
148
149 uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
150 if (numRanges > 1024 ||
151 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
152 sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
153 {
154 return false;
155 }
156
157 const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
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 */
is_gridfit_only(GaspRange::Behavior flags)176 static bool is_gridfit_only(GaspRange::Behavior flags) {
177 return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
178 }
179
has_bitmap_strike(DWriteFontTypeface * typeface,GaspRange range)180 static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) {
181 Exclusive l(maybe_dw_mutex(*typeface));
182 {
183 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
184 if (!eblc.fExists) {
185 return false;
186 }
187 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
188 return false;
189 }
190 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
191 return false;
192 }
193
194 uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
195 if (numSizes > 1024 ||
196 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
197 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
198 {
199 return false;
200 }
201
202 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
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 {
222 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
223 if (!ebsc.fExists) {
224 return false;
225 }
226 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
227 return false;
228 }
229 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
230 return false;
231 }
232
233 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
234 if (numSizes > 1024 ||
235 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
236 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
237 {
238 return false;
239 }
240
241 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
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
both_zero(SkScalar a,SkScalar b)255 static 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
is_axis_aligned(const SkScalerContextRec & rec)260 static 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
SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef,const SkScalerContextEffects & effects,const SkDescriptor * desc)268 SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef,
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
292 SkVector scale;
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 &&
399 !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag))
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
~SkScalerContext_DW()430 SkScalerContext_DW::~SkScalerContext_DW() {
431 }
432
433 #if DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
434
435 namespace {
sk_color_from(DWRITE_COLOR_F const & color)436 SkColor4f 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 }
dw_color_from(SkColor4f const & color)440 DWRITE_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
sk_rect_from(D2D_RECT_F const & rect)453 SkRect 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 }
D2D_RECT_F_is_empty(const D2D_RECT_F & r)457 constexpr bool D2D_RECT_F_is_empty(const D2D_RECT_F& r) {
458 return r.right <= r.left || r.bottom <= r.top;
459 }
460
sk_matrix_from(DWRITE_MATRIX const & m)461 SkMatrix 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
sk_tile_mode_from(D2D1_EXTEND_MODE extendMode)469 SkTileMode 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
sk_blend_mode_from(DWRITE_COLOR_COMPOSITE_MODE compositeMode)482 SkBlendMode 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:
491 return SkBlendMode::kSrcOver;
492 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
493 return SkBlendMode::kDstOver;
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:
499 return SkBlendMode::kSrcOut;
500 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
501 return SkBlendMode::kDstOut;
502 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
503 return SkBlendMode::kSrcATop;
504 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
505 return SkBlendMode::kDstATop;
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:
512 return SkBlendMode::kScreen;
513 case DWRITE_COLOR_COMPOSITE_OVERLAY:
514 return SkBlendMode::kOverlay;
515 case DWRITE_COLOR_COMPOSITE_DARKEN:
516 return SkBlendMode::kDarken;
517 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
518 return SkBlendMode::kLighten;
519 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
520 return SkBlendMode::kColorDodge;
521 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
522 return SkBlendMode::kColorBurn;
523 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
524 return SkBlendMode::kHardLight;
525 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
526 return SkBlendMode::kSoftLight;
527 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
528 return SkBlendMode::kDifference;
529 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
530 return SkBlendMode::kExclusion;
531 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
532 return SkBlendMode::kMultiply;
533
534 case DWRITE_COLOR_COMPOSITE_HSL_HUE:
535 return SkBlendMode::kHue;
536 case DWRITE_COLOR_COMPOSITE_HSL_SATURATION:
537 return SkBlendMode::kSaturation;
538 case DWRITE_COLOR_COMPOSITE_HSL_COLOR:
539 return SkBlendMode::kColor;
540 case DWRITE_COLOR_COMPOSITE_HSL_LUMINOSITY:
541 return SkBlendMode::kLuminosity;
542 default:
543 return SkBlendMode::kDst;
544 }
545 }
546
SkVectorProjection(SkPoint a,SkPoint b)547 inline SkPoint SkVectorProjection(SkPoint a, SkPoint b) {
548 SkScalar length = b.length();
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.
lerpSkColor(D2D1_COLOR_F c0,D2D1_COLOR_F c1,float t)563 D2D1_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
580 enum 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.
truncateToStopInterpolating(SkScalar zeroRadiusStop,std::vector<D2D1_GRADIENT_STOP> & stops,TruncateStops truncateStops)586 void 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
drawColorV1Paint(SkCanvas & canvas,IDWritePaintReader & reader,DWRITE_PAINT_ELEMENT const & element)618 bool 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
792 sk_sp<SkShader> shader(SkGradientShader::MakeLinear(
793 linePositions,
794 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
795 tileMode,
796 SkGradientShader::Interpolation{
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);
985 skPaint.setShader(SkGradientShader::MakeTwoPointConical(
986 start, startRadius, end, endRadius,
987 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
988 tileMode,
989 SkGradientShader::Interpolation{
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
1105 skPaint.setShader(SkGradientShader::MakeSweep(
1106 center.x(), center.y(),
1107 skColors.get(), SkColorSpace::MakeSRGB(), skStops.get(), stops.size(),
1108 tileMode,
1109 startAngleScaled, endAngleScaled,
1110 SkGradientShader::Interpolation{
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
drawColorV1Image(const SkGlyph & glyph,SkCanvas & canvas)1202 bool 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()) {
1235 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
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
generateColorV1Image(const SkGlyph & glyph,void * imageBuffer)1254 bool SkScalerContext_DW::generateColorV1Image(const SkGlyph& glyph, void* imageBuffer) {
1255 SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
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
generateColorV1PaintBounds(SkMatrix * ctm,SkRect * bounds,IDWritePaintReader & reader,DWRITE_PAINT_ELEMENT const & element)1275 bool 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
generateColorV1Metrics(const SkGlyph & glyph,SkRect * bounds)1413 bool 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()) {
1450 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
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
generateColorV1Metrics(const SkGlyph &,SkRect *)1471 bool SkScalerContext_DW::generateColorV1Metrics(const SkGlyph&, SkRect*) { return false; }
generateColorV1Image(const SkGlyph &,void *)1472 bool SkScalerContext_DW::generateColorV1Image(const SkGlyph&, void*) { return false; }
drawColorV1Image(const SkGlyph &,SkCanvas &)1473 bool SkScalerContext_DW::drawColorV1Image(const SkGlyph&, SkCanvas&) { return false; }
1474
1475 #endif // DWRITE_CORE || (defined(NTDDI_WIN11_ZN) && NTDDI_VERSION >= NTDDI_WIN11_ZN)
1476
setAdvance(const SkGlyph & glyph,SkVector * advance)1477 bool 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
generateDWMetrics(const SkGlyph & glyph,DWRITE_RENDERING_MODE renderingMode,DWRITE_TEXTURE_TYPE textureType,SkRect * bounds)1529 bool 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
getColorGlyphRun(const SkGlyph & glyph,IDWriteColorGlyphRunEnumerator ** colorGlyph)1608 bool 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
generateColorMetrics(const SkGlyph & glyph,SkRect * bounds)1637 bool 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
1644 *bounds = SkRect::MakeEmpty();
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()) {
1671 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
1672 SkFixedToScalar(glyph.getSubYFixed()));
1673 }
1674 matrix.mapRect(bounds);
1675 return true;
1676 }
1677
generateSVGMetrics(const SkGlyph & glyph,SkRect * bounds)1678 bool SkScalerContext_DW::generateSVGMetrics(const SkGlyph& glyph, SkRect* bounds) {
1679 SkPictureRecorder recorder;
1680 SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity,
1681 SK_ScalarInfinity, SK_ScalarInfinity);
1682 sk_sp<SkBBoxHierarchy> bboxh = SkRTreeFactory()();
1683 SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
1684 if (!this->drawSVGImage(glyph, *recordingCanvas)) {
1685 return false;
1686 }
1687 sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
1688 *bounds = pic->cullRect();
1689 SkASSERT(bounds->isFinite());
1690 bounds->roundOut(bounds);
1691 return true;
1692 }
1693
1694 namespace {
1695 struct Context {
1696 SkTScopedComPtr<IDWriteFontFace4> fontFace4;
1697 void* glyphDataContext;
Context__anon0365400f0811::Context1698 Context(IDWriteFontFace4* face4, void* context)
1699 : fontFace4(SkRefComPtr(face4))
1700 , glyphDataContext(context)
1701 {}
1702 };
1703
ReleaseProc(const void * ptr,void * context)1704 static void ReleaseProc(const void* ptr, void* context) {
1705 Context* ctx = (Context*)context;
1706 ctx->fontFace4->ReleaseGlyphImageData(ctx->glyphDataContext);
1707 delete ctx;
1708 }
1709 }
1710
generatePngMetrics(const SkGlyph & glyph,SkRect * bounds)1711 bool 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()) {
1755 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
1756 SkFixedToScalar(glyph.getSubYFixed()));
1757 }
1758 matrix.mapRect(bounds);
1759 bounds->roundOut(bounds);
1760 return true;
1761 }
1762
generateMetrics(const SkGlyph & glyph,SkArenaAlloc * alloc)1763 SkScalerContext::GlyphMetrics SkScalerContext_DW::generateMetrics(const SkGlyph& glyph,
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
generateFontMetrics(SkFontMetrics * metrics)1834 void SkScalerContext_DW::generateFontMetrics(SkFontMetrics* metrics) {
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
1866 metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
1867 metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
1868 metrics->fFlags |= SkFontMetrics::kStrikeoutThicknessIsValid_Flag;
1869 metrics->fFlags |= SkFontMetrics::kStrikeoutPositionIsValid_Flag;
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.
1875 metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
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.
1906 metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
1907 metrics->fTop = metrics->fAscent;
1908 metrics->fBottom = metrics->fDescent;
1909 }
1910
1911 ///////////////////////////////////////////////////////////////////////////////
1912
1913 #include "include/private/SkColorData.h"
1914
BilevelToBW(const uint8_t * SK_RESTRICT src,const SkGlyph & glyph,void * imageBuffer)1915 void 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
1964 template<bool APPLY_PREBLEND>
GrayscaleToA8(const uint8_t * SK_RESTRICT src,const SkGlyph & glyph,void * imageBuffer,const uint8_t * table8)1965 void 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
1984 template<bool APPLY_PREBLEND>
RGBToA8(const uint8_t * SK_RESTRICT src,const SkGlyph & glyph,void * imageBuffer,const uint8_t * table8)1985 void 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
2006 template<bool APPLY_PREBLEND, bool RGB>
RGBToLcd16(const uint8_t * SK_RESTRICT src,const SkGlyph & glyph,void * imageBuffer,const uint8_t * tableR,const uint8_t * tableG,const uint8_t * tableB)2007 void 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
getDWMaskBits(const SkGlyph & glyph,DWRITE_RENDERING_MODE renderingMode,DWRITE_TEXTURE_TYPE textureType)2038 const 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
generateDWImage(const SkGlyph & glyph,void * imageBuffer)2125 bool 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) {
2143 SkASSERT(SkMask::kBW_Format == glyph.maskFormat());
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 {
2161 SkASSERT(SkMask::kLCD16_Format == glyph.maskFormat());
2162 if (fPreBlend.isApplicable()) {
2163 if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
2164 RGBToLcd16<true, false>(src, glyph, imageBuffer,
2165 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
2166 } else {
2167 RGBToLcd16<true, true>(src, glyph, imageBuffer,
2168 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
2169 }
2170 } else {
2171 if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
2172 RGBToLcd16<false, false>(src, glyph, imageBuffer,
2173 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
2174 } else {
2175 RGBToLcd16<false, true>(src, glyph, imageBuffer,
2176 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
2177 }
2178 }
2179 }
2180 return true;
2181 }
2182
drawColorImage(const SkGlyph & glyph,SkCanvas & canvas)2183 bool 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) {
2209 color = fRec.fForegroundColor;
2210 } else if (colorGlyph->paletteIndex < paletteEntryCount) {
2211 color = palette[colorGlyph->paletteIndex];
2212 } else {
2213 SK_TRACEHR(DWRITE_E_NOCOLOR, "Invalid palette index.");
2214 color = SK_ColorBLACK;
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
generateColorImage(const SkGlyph & glyph,void * imageBuffer)2240 bool SkScalerContext_DW::generateColorImage(const SkGlyph& glyph, void* imageBuffer) {
2241 SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
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
drawSVGImage(const SkGlyph & glyph,SkCanvas & canvas)2261 bool 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
2275 SkGraphics::OpenTypeSVGDecoderFactory svgFactory = SkGraphics::GetOpenTypeSVGDecoderFactory();
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()) {
2303 matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
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
generateSVGImage(const SkGlyph & glyph,void * imageBuffer)2312 bool SkScalerContext_DW::generateSVGImage(const SkGlyph& glyph, void* imageBuffer) {
2313 SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
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
drawPngImage(const SkGlyph & glyph,SkCanvas & canvas)2333 bool 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);
2352 sk_sp<SkImage> image = SkImages::DeferredFromEncodedData(std::move(data));
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
generatePngImage(const SkGlyph & glyph,void * imageBuffer)2369 bool SkScalerContext_DW::generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
2370 SkASSERT(glyph.maskFormat() == SkMask::Format::kARGB32_Format);
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
generateImage(const SkGlyph & glyph,void * imageBuffer)2385 void 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);
2405 const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
2406 const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
2407 const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
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
generatePath(const SkGlyph & glyph,SkPath * path)2415 bool 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
generateDrawable(const SkGlyph & glyph)2451 sk_sp<SkDrawable> SkScalerContext_DW::generateDrawable(const SkGlyph& glyph) {
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)
2502