Source code

Revision control

Other Tools

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 "SkDWriteNTDDI_VERSION.h"
8
9
#include "SkTypes.h"
10
#if defined(SK_BUILD_FOR_WIN)
11
12
#undef GetGlyphIndices
13
14
#include "SkCodec.h"
15
#include "SkDWrite.h"
16
#include "SkDWriteGeometrySink.h"
17
#include "SkDraw.h"
18
#include "SkEndian.h"
19
#include "SkFontMetrics.h"
20
#include "SkGlyph.h"
21
#include "SkHRESULT.h"
22
#include "SkMaskGamma.h"
23
#include "SkMatrix22.h"
24
#include "SkMutex.h"
25
#include "SkOTTable_EBLC.h"
26
#include "SkOTTable_EBSC.h"
27
#include "SkOTTable_gasp.h"
28
#include "SkOTTable_maxp.h"
29
#include "SkPath.h"
30
#include "SkRasterClip.h"
31
#include "SkScalerContext.h"
32
#include "SkScalerContext_win_dw.h"
33
#include "SkSharedMutex.h"
34
#include "SkTScopedComPtr.h"
35
#include "SkTo.h"
36
#include "SkTypeface_win_dw.h"
37
38
#include <dwrite.h>
39
#include <dwrite_1.h>
40
#include <dwrite_3.h>
41
42
/* Note:
43
* In versions 8 and 8.1 of Windows, some calls in DWrite are not thread safe.
44
* The DWriteFactoryMutex protects the calls that are problematic.
45
*
46
* On DWrite 3 or above, which is only available on Windows 10, we don't enable
47
* the locking to avoid thread contention.
48
*/
49
static SkSharedMutex DWriteFactoryMutex;
50
51
struct MaybeExclusive {
52
MaybeExclusive(SkScalerContext_DW* ctx) : fEnabled(!ctx->isDWrite3()) {
53
if (fEnabled) {
54
DWriteFactoryMutex.acquire();
55
}
56
}
57
~MaybeExclusive() {
58
if (fEnabled) {
59
DWriteFactoryMutex.release();
60
}
61
}
62
bool fEnabled;
63
};
64
65
struct MaybeShared {
66
MaybeShared(SkScalerContext_DW* ctx) : fEnabled(!ctx->isDWrite3()) {
67
if (fEnabled) {
68
DWriteFactoryMutex.acquireShared();
69
}
70
}
71
~MaybeShared() {
72
if (fEnabled) {
73
DWriteFactoryMutex.releaseShared();
74
}
75
}
76
bool fEnabled;
77
};
78
79
static bool isLCD(const SkScalerContextRec& rec) {
80
return SkMask::kLCD16_Format == rec.fMaskFormat;
81
}
82
83
static bool is_hinted(SkScalerContext_DW* ctx, DWriteFontTypeface* typeface) {
84
MaybeExclusive l(ctx);
85
AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
86
if (!maxp.fExists) {
87
return false;
88
}
89
if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
90
return false;
91
}
92
if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
93
return false;
94
}
95
return (0 != maxp->version.tt.maxSizeOfInstructions);
96
}
97
98
/** A GaspRange is inclusive, [min, max]. */
99
struct GaspRange {
100
using Behavior = SkOTTableGridAndScanProcedure::GaspRange::behavior;
101
GaspRange(int min, int max, int version, Behavior flags)
102
: fMin(min), fMax(max), fVersion(version), fFlags(flags) { }
103
int fMin;
104
int fMax;
105
int fVersion;
106
Behavior fFlags;
107
};
108
109
bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) {
110
AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
111
if (!gasp.fExists) {
112
return false;
113
}
114
if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
115
return false;
116
}
117
if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
118
gasp->version != SkOTTableGridAndScanProcedure::version1)
119
{
120
return false;
121
}
122
123
uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
124
if (numRanges > 1024 ||
125
gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
126
sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
127
{
128
return false;
129
}
130
131
const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
132
SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
133
int minPPEM = -1;
134
for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
135
int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
136
if (minPPEM < size && size <= maxPPEM) {
137
range->fMin = minPPEM + 1;
138
range->fMax = maxPPEM;
139
range->fVersion = SkEndian_SwapBE16(gasp->version);
140
range->fFlags = rangeTable->flags;
141
return true;
142
}
143
minPPEM = maxPPEM;
144
}
145
return false;
146
}
147
/** If the rendering mode for the specified 'size' is gridfit, then place
148
* the gridfit range into 'range'. Otherwise, leave 'range' alone.
149
*/
150
static bool is_gridfit_only(GaspRange::Behavior flags) {
151
return flags.raw.value == GaspRange::Behavior::Raw::GridfitMask;
152
}
153
154
static bool has_bitmap_strike(SkScalerContext_DW* ctx, DWriteFontTypeface* typeface, GaspRange range) {
155
MaybeExclusive l(ctx);
156
{
157
AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
158
if (!eblc.fExists) {
159
return false;
160
}
161
if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
162
return false;
163
}
164
if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
165
return false;
166
}
167
168
uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
169
if (numSizes > 1024 ||
170
eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
171
sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
172
{
173
return false;
174
}
175
176
const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
177
SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
178
for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
179
if (sizeTable->ppemX == sizeTable->ppemY &&
180
range.fMin <= sizeTable->ppemX && sizeTable->ppemX <= range.fMax)
181
{
182
// TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
183
// to determine the actual number of glyphs with bitmaps.
184
185
// TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
186
187
// TODO: Ensure that the bitmaps are bi-level?
188
if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
189
return true;
190
}
191
}
192
}
193
}
194
195
{
196
AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
197
if (!ebsc.fExists) {
198
return false;
199
}
200
if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
201
return false;
202
}
203
if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
204
return false;
205
}
206
207
uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
208
if (numSizes > 1024 ||
209
ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
210
sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
211
{
212
return false;
213
}
214
215
const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
216
SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
217
for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
218
if (scaleTable->ppemX == scaleTable->ppemY &&
219
range.fMin <= scaleTable->ppemX && scaleTable->ppemX <= range.fMax) {
220
// EBSC tables are normally only found in bitmap only fonts.
221
return true;
222
}
223
}
224
}
225
226
return false;
227
}
228
229
static bool both_zero(SkScalar a, SkScalar b) {
230
return 0 == a && 0 == b;
231
}
232
233
// returns false if there is any non-90-rotation or skew
234
static bool is_axis_aligned(const SkScalerContextRec& rec) {
235
return 0 == rec.fPreSkewX &&
236
(both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
237
both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
238
}
239
240
SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef,
241
const SkScalerContextEffects& effects,
242
const SkDescriptor* desc)
243
: SkScalerContext(std::move(typefaceRef), effects, desc)
244
, fGlyphCount(-1) {
245
246
DWriteFontTypeface* typeface = this->getDWriteTypeface();
247
fIsColorFont = typeface->fFactory2 &&
248
typeface->fDWriteFontFace2 &&
249
typeface->fDWriteFontFace2->IsColorFont();
250
251
// In general, all glyphs should use DWriteFontFace::GetRecommendedRenderingMode
252
// except when bi-level rendering is requested or there are embedded
253
// bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
254
//
255
// DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
256
// this. As a result, determine the actual size of the text and then see if
257
// there are any embedded bi-level bitmaps of that size. If there are, then
258
// force bitmaps by requesting bi-level rendering.
259
//
260
// FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
261
// square pixels and only uses ppemY. Therefore the transform must track any
262
// non-uniform x-scale.
263
//
264
// Also, rotated glyphs should have the same absolute advance widths as
265
// horizontal glyphs and the subpixel flag should not affect glyph shapes.
266
267
SkVector scale;
268
fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &fSkXform);
269
270
fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
271
fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
272
fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
273
fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
274
fXform.dx = 0;
275
fXform.dy = 0;
276
277
// realTextSize is the actual device size we want (as opposed to the size the user requested).
278
// gdiTextSize is the size we request when GDI compatible.
279
// If the scale is negative, this means the matrix will do the flip anyway.
280
const SkScalar realTextSize = scale.fY;
281
// Due to floating point math, the lower bits are suspect. Round carefully.
282
SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
283
if (gdiTextSize == 0) {
284
gdiTextSize = SK_Scalar1;
285
}
286
287
bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
288
bool treatLikeBitmap = false;
289
bool axisAlignedBitmap = false;
290
if (bitmapRequested) {
291
// When embedded bitmaps are requested, treat the entire range like
292
// a bitmap strike if the range is gridfit only and contains a bitmap.
293
int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
294
GaspRange range(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
295
if (get_gasp_range(typeface, bitmapPPEM, &range)) {
296
if (!is_gridfit_only(range.fFlags)) {
297
range = GaspRange(bitmapPPEM, bitmapPPEM, 0, GaspRange::Behavior());
298
}
299
}
300
treatLikeBitmap = has_bitmap_strike(this, typeface, range);
301
302
axisAlignedBitmap = is_axis_aligned(fRec);
303
}
304
305
GaspRange range(0, 0xFFFF, 0, GaspRange::Behavior());
306
307
// If the user requested aliased, do so with aliased compatible metrics.
308
if (SkMask::kBW_Format == fRec.fMaskFormat) {
309
fTextSizeRender = gdiTextSize;
310
fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
311
fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
312
fTextSizeMeasure = gdiTextSize;
313
fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
314
315
// If we can use a bitmap, use gdi classic rendering and measurement.
316
// This will not always provide a bitmap, but matches expected behavior.
317
} else if ((treatLikeBitmap && axisAlignedBitmap) || typeface->ForceGDI()) {
318
fTextSizeRender = gdiTextSize;
319
fRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
320
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
321
fTextSizeMeasure = gdiTextSize;
322
fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
323
324
// If rotated but the horizontal text could have used a bitmap,
325
// render high quality rotated glyphs but measure using bitmap metrics.
326
} else if (treatLikeBitmap) {
327
fTextSizeRender = gdiTextSize;
328
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
329
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
330
fTextSizeMeasure = gdiTextSize;
331
fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
332
333
// If the font has a gasp table version 1, use it to determine symmetric rendering.
334
} else if ((get_gasp_range(typeface, SkScalarRoundToInt(gdiTextSize), &range) &&
335
range.fVersion >= 1) ||
336
realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
337
fTextSizeRender = realTextSize;
338
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
339
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
340
fTextSizeMeasure = realTextSize;
341
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
342
343
DWriteFontTypeface* typeface = static_cast<DWriteFontTypeface*>(getTypeface());
344
switch (typeface->GetRenderingMode()) {
345
case DWRITE_RENDERING_MODE_NATURAL:
346
case DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC:
347
fRenderingMode = typeface->GetRenderingMode();
348
break;
349
default:
350
if (IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params()) {
351
typeface->fDWriteFontFace->GetRecommendedRenderingMode(
352
fTextSizeRender, 1.0f, fMeasuringMode, params, &fRenderingMode);
353
}
354
break;
355
}
356
357
// We don't support outline mode right now.
358
if (fRenderingMode == DWRITE_RENDERING_MODE_OUTLINE) {
359
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
360
}
361
362
// Fonts with hints, no gasp or gasp version 0, and below 20px get non-symmetric rendering.
363
// Often such fonts have hints which were only tested with GDI ClearType classic.
364
// Some of these fonts rely on drop out control in the y direction in order to be legible.
365
// Tenor Sans
367
// Gill Sans W04
371
} else {
372
fTextSizeRender = gdiTextSize;
373
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
374
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
375
fTextSizeMeasure = realTextSize;
376
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
377
}
378
379
// DirectWrite2 allows for grayscale hinting.
380
fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE;
381
if (typeface->fFactory2 && typeface->fDWriteFontFace2 &&
382
SkMask::kA8_Format == fRec.fMaskFormat &&
383
!(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag))
384
{
385
// DWRITE_TEXTURE_ALIASED_1x1 is now misnamed, it must also be used with grayscale.
386
fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
387
fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE;
388
}
389
390
// DirectWrite2 allows hinting to be disabled.
391
fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED;
392
if (fRec.getHinting() == kNo_SkFontHinting) {
393
fGridFitMode = DWRITE_GRID_FIT_MODE_DISABLED;
394
if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
395
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
396
}
397
}
398
399
if (this->isSubpixel()) {
400
fTextSizeMeasure = realTextSize;
401
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
402
}
403
}
404
405
SkScalerContext_DW::~SkScalerContext_DW() {
406
}
407
408
unsigned SkScalerContext_DW::generateGlyphCount() {
409
if (fGlyphCount < 0) {
410
fGlyphCount = this->getDWriteTypeface()->fDWriteFontFace->GetGlyphCount();
411
}
412
return fGlyphCount;
413
}
414
415
uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) {
416
uint16_t index = 0;
417
UINT32* uniPtr = reinterpret_cast<UINT32*>(&uni);
418
this->getDWriteTypeface()->fDWriteFontFace->GetGlyphIndices(uniPtr, 1, &index);
419
return index;
420
}
421
422
bool SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
423
glyph->fAdvanceX = 0;
424
glyph->fAdvanceY = 0;
425
426
uint16_t glyphId = glyph->getGlyphID();
427
DWRITE_GLYPH_METRICS gm;
428
429
if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
430
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
431
{
432
MaybeExclusive l(this);
433
HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
434
fTextSizeMeasure,
435
1.0f, // pixelsPerDip
436
// This parameter does not act like the lpmat2 parameter to GetGlyphOutlineW.
437
// If it did then GsA here and G_inv below to mapVectors.
438
nullptr,
439
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
440
&glyphId, 1,
441
&gm),
442
"Could not get gdi compatible glyph metrics.");
443
} else {
444
MaybeExclusive l(this);
445
HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
446
"Could not get design metrics.");
447
}
448
449
DWRITE_FONT_METRICS dwfm;
450
{
451
MaybeShared l(this);
452
this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
453
}
454
SkScalar advanceX = fTextSizeMeasure * gm.advanceWidth / dwfm.designUnitsPerEm;
455
456
SkVector advance = { advanceX, 0 };
457
if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
458
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
459
{
460
// DirectWrite produced 'compatible' metrics, but while close,
461
// the end result is not always an integer as it would be with GDI.
462
advance.fX = SkScalarRoundToScalar(advance.fX);
463
}
464
fSkXform.mapVectors(&advance, 1);
465
466
glyph->fAdvanceX = SkScalarToFloat(advance.fX);
467
glyph->fAdvanceY = SkScalarToFloat(advance.fY);
468
return true;
469
}
470
471
HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
472
DWRITE_RENDERING_MODE renderingMode,
473
DWRITE_TEXTURE_TYPE textureType,
474
RECT* bbox)
475
{
476
//Measure raster size.
477
fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
478
fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
479
480
FLOAT advance = 0;
481
482
UINT16 glyphId = glyph->getGlyphID();
483
484
DWRITE_GLYPH_OFFSET offset;
485
offset.advanceOffset = 0.0f;
486
offset.ascenderOffset = 0.0f;
487
488
DWRITE_GLYPH_RUN run;
489
run.glyphCount = 1;
490
run.glyphAdvances = &advance;
491
run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
492
run.fontEmSize = SkScalarToFloat(fTextSizeRender);
493
run.bidiLevel = 0;
494
run.glyphIndices = &glyphId;
495
run.isSideways = FALSE;
496
run.glyphOffsets = &offset;
497
498
SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
499
{
500
MaybeExclusive l(this);
501
// IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
502
if (this->getDWriteTypeface()->fFactory2 &&
503
(fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
504
fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
505
{
506
HRM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis(
507
&run,
508
&fXform,
509
renderingMode,
510
fMeasuringMode,
511
fGridFitMode,
512
fAntiAliasMode,
513
0.0f, // baselineOriginX,
514
0.0f, // baselineOriginY,
515
&glyphRunAnalysis),
516
"Could not create DW2 glyph run analysis.");
517
} else {
518
HRM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run,
519
1.0f, // pixelsPerDip,
520
&fXform,
521
renderingMode,
522
fMeasuringMode,
523
0.0f, // baselineOriginX,
524
0.0f, // baselineOriginY,
525
&glyphRunAnalysis),
526
"Could not create glyph run analysis.");
527
}
528
}
529
{
530
MaybeShared l(this);
531
HRM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox),
532
"Could not get texture bounds.");
533
}
534
return S_OK;
535
}
536
537
/** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
538
* { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
539
* for small, but not quite zero, sized glyphs.
540
* Only set as non-empty if the returned bounds are non-empty.
541
*/
542
static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) {
543
if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
544
return false;
545
}
546
547
// We're trying to pack left and top into int16_t,
548
// and width and height into uint16_t, after outsetting by 1.
549
if (!SkIRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(
550
SkIRect::MakeLTRB(bbox.left, bbox.top, bbox.right, bbox.bottom))) {
551
return false;
552
}
553
554
glyph->fWidth = SkToU16(bbox.right - bbox.left);
555
glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
556
glyph->fLeft = SkToS16(bbox.left);
557
glyph->fTop = SkToS16(bbox.top);
558
return true;
559
}
560
561
bool SkScalerContext_DW::isColorGlyph(const SkGlyph& glyph) {
562
SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayer;
563
return getColorGlyphRun(glyph, &colorLayer);
564
}
565
566
bool SkScalerContext_DW::isPngGlyph(const SkGlyph& glyph) {
567
if (!this->getDWriteTypeface()->fDWriteFontFace4) {
568
return false;
569
}
570
571
DWRITE_GLYPH_IMAGE_FORMATS f;
572
IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
573
HRBM(fontFace4->GetGlyphImageFormats(glyph.getGlyphID(), 0, UINT32_MAX, &f),
574
"Cannot get glyph image formats.");
575
return f & DWRITE_GLYPH_IMAGE_FORMATS_PNG;
576
}
577
578
bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
579
IDWriteColorGlyphRunEnumerator** colorGlyph)
580
{
581
FLOAT advance = 0;
582
UINT16 glyphId = glyph.getGlyphID();
583
584
DWRITE_GLYPH_OFFSET offset;
585
offset.advanceOffset = 0.0f;
586
offset.ascenderOffset = 0.0f;
587
588
DWRITE_GLYPH_RUN run;
589
run.glyphCount = 1;
590
run.glyphAdvances = &advance;
591
run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
592
run.fontEmSize = SkScalarToFloat(fTextSizeRender);
593
run.bidiLevel = 0;
594
run.glyphIndices = &glyphId;
595
run.isSideways = FALSE;
596
run.glyphOffsets = &offset;
597
598
HRESULT hr = this->getDWriteTypeface()->fFactory2->TranslateColorGlyphRun(
599
0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
600
if (hr == DWRITE_E_NOCOLOR) {
601
return false;
602
}
603
HRBM(hr, "Failed to translate color glyph run");
604
return true;
605
}
606
607
void SkScalerContext_DW::generateColorMetrics(SkGlyph* glyph) {
608
SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
609
HRVM(getColorGlyphRun(*glyph, &colorLayers), "Could not get color glyph run");
610
SkASSERT(colorLayers.get());
611
612
SkRect bounds = SkRect::MakeEmpty();
613
BOOL hasNextRun = FALSE;
614
while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
615
const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
616
HRVM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
617
618
SkPath path;
619
SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
620
HRVM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
621
"Could not create geometry to path converter.");
622
{
623
MaybeExclusive l(this);
624
HRVM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
625
colorGlyph->glyphRun.fontEmSize,
626
colorGlyph->glyphRun.glyphIndices,
627
colorGlyph->glyphRun.glyphAdvances,
628
colorGlyph->glyphRun.glyphOffsets,
629
colorGlyph->glyphRun.glyphCount,
630
colorGlyph->glyphRun.isSideways,
631
colorGlyph->glyphRun.bidiLevel % 2, //rtl
632
geometryToPath.get()),
633
"Could not create glyph outline.");
634
}
635
bounds.join(path.getBounds());
636
}
637
SkMatrix matrix = fSkXform;
638
if (this->isSubpixel()) {
639
matrix.postTranslate(SkFixedToScalar(glyph->getSubXFixed()),
640
SkFixedToScalar(glyph->getSubYFixed()));
641
}
642
matrix.mapRect(&bounds);
643
// Round float bound values into integer.
644
SkIRect ibounds = bounds.roundOut();
645
646
glyph->fWidth = ibounds.fRight - ibounds.fLeft;
647
glyph->fHeight = ibounds.fBottom - ibounds.fTop;
648
glyph->fLeft = ibounds.fLeft;
649
glyph->fTop = ibounds.fTop;
650
}
651
652
#ifdef USE_PNG
653
namespace {
654
struct Context {
655
SkTScopedComPtr<IDWriteFontFace4> fontFace4;
656
void* glyphDataContext;
657
Context(IDWriteFontFace4* face4, void* context)
658
: fontFace4(SkRefComPtr(face4))
659
, glyphDataContext(context)
660
{}
661
};
662
663
static void ReleaseProc(const void* ptr, void* context) {
664
Context* ctx = (Context*)context;
665
ctx->fontFace4->ReleaseGlyphImageData(ctx->glyphDataContext);
666
delete ctx;
667
}
668
}
669
670
void SkScalerContext_DW::generatePngMetrics(SkGlyph* glyph) {
671
SkASSERT(isPngGlyph(*glyph));
672
SkASSERT(glyph->fMaskFormat == SkMask::Format::kARGB32_Format);
673
SkASSERT(this->getDWriteTypeface()->fDWriteFontFace4);
674
675
IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
676
DWRITE_GLYPH_IMAGE_DATA glyphData;
677
void* glyphDataContext;
678
HRVM(fontFace4->GetGlyphImageData(glyph->getGlyphID(),
679
fTextSizeRender,
680
DWRITE_GLYPH_IMAGE_FORMATS_PNG,
681
&glyphData,
682
&glyphDataContext),
683
"Glyph image data could not be acquired.");
684
685
Context* context = new Context(fontFace4, glyphDataContext);
686
sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
687
glyphData.imageDataSize,
688
&ReleaseProc,
689
context);
690
691
std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(std::move(data));
692
if (!codec) {
693
return;
694
}
695
696
SkImageInfo info = codec->getInfo();
697
SkRect bounds = SkRect::MakeLTRB(SkIntToScalar(info.bounds().fLeft),
698
SkIntToScalar(info.bounds().fTop),
699
SkIntToScalar(info.bounds().fRight),
700
SkIntToScalar(info.bounds().fBottom));
701
702
SkMatrix matrix = fSkXform;
703
SkScalar scale = fTextSizeRender / glyphData.pixelsPerEm;
704
matrix.preScale(scale, scale);
705
matrix.preTranslate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
706
if (this->isSubpixel()) {
707
matrix.postTranslate(SkFixedToScalar(glyph->getSubXFixed()),
708
SkFixedToScalar(glyph->getSubYFixed()));
709
}
710
matrix.mapRect(&bounds);
711
bounds.roundOut();
712
713
glyph->fWidth = bounds.width();
714
glyph->fHeight = bounds.height();
715
glyph->fLeft = bounds.left();
716
glyph->fTop = bounds.top();
717
return;
718
}
719
#endif
720
721
void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
722
glyph->fWidth = 0;
723
glyph->fHeight = 0;
724
glyph->fLeft = 0;
725
glyph->fTop = 0;
726
glyph->fMaskFormat = fRec.fMaskFormat;
727
728
if (!this->generateAdvance(glyph)) {
729
return;
730
}
731
732
if (fIsColorFont && isColorGlyph(*glyph)) {
733
glyph->fMaskFormat = SkMask::kARGB32_Format;
734
generateColorMetrics(glyph);
735
return;
736
}
737
738
if (fIsColorFont && isPngGlyph(*glyph)) {
739
#ifdef USE_PNG
740
glyph->fMaskFormat = SkMask::kARGB32_Format;
741
generatePngMetrics(glyph);
742
#endif
743
return;
744
}
745
746
RECT bbox;
747
HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
748
"Requested bounding box could not be determined.");
749
750
if (glyph_check_and_set_bounds(glyph, bbox)) {
751
return;
752
}
753
754
// GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
755
// glyphs of the specified texture type or it is too big for smoothing.
756
// When this happens, try with the alternate texture type.
757
if (DWRITE_TEXTURE_ALIASED_1x1 != fTextureType ||
758
DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE == fAntiAliasMode)
759
{
760
HRVM(this->getBoundingBox(glyph,
761
DWRITE_RENDERING_MODE_ALIASED,
762
DWRITE_TEXTURE_ALIASED_1x1,
763
&bbox),
764
"Fallback bounding box could not be determined.");
765
if (glyph_check_and_set_bounds(glyph, bbox)) {
766
glyph->fForceBW = 1;
767
glyph->fMaskFormat = SkMask::kBW_Format;
768
}
769
}
770
// TODO: handle the case where a request for DWRITE_TEXTURE_ALIASED_1x1
771
// fails, and try DWRITE_TEXTURE_CLEARTYPE_3x1.
772
}
773
774
void SkScalerContext_DW::generateFontMetrics(SkFontMetrics* metrics) {
775
if (nullptr == metrics) {
776
return;
777
}
778
779
sk_bzero(metrics, sizeof(*metrics));
780
781
DWRITE_FONT_METRICS dwfm;
782
if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
783
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
784
{
785
this->getDWriteTypeface()->fDWriteFontFace->GetGdiCompatibleMetrics(
786
fTextSizeRender,
787
1.0f, // pixelsPerDip
788
&fXform,
789
&dwfm);
790
} else {
791
this->getDWriteTypeface()->fDWriteFontFace->GetMetrics(&dwfm);
792
}
793
794
SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
795
796
metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
797
metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
798
metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
799
metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
800
metrics->fCapHeight = fTextSizeRender * SkIntToScalar(dwfm.capHeight) / upem;
801
metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
802
metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
803
metrics->fStrikeoutThickness = fTextSizeRender * SkIntToScalar(dwfm.strikethroughThickness) / upem;
804
metrics->fStrikeoutPosition = -(fTextSizeRender * SkIntToScalar(dwfm.strikethroughPosition) / upem);
805
806
metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
807
metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
808
metrics->fFlags |= SkFontMetrics::kStrikeoutThicknessIsValid_Flag;
809
metrics->fFlags |= SkFontMetrics::kStrikeoutPositionIsValid_Flag;
810
811
if (this->getDWriteTypeface()->fDWriteFontFace1.get()) {
812
DWRITE_FONT_METRICS1 dwfm1;
813
this->getDWriteTypeface()->fDWriteFontFace1->GetMetrics(&dwfm1);
814
metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
815
metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
816
metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
817
metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
818
819
metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
820
return;
821
}
822
823
AutoTDWriteTable<SkOTTableHead> head(this->getDWriteTypeface()->fDWriteFontFace.get());
824
if (head.fExists &&
825
head.fSize >= sizeof(SkOTTableHead) &&
826
head->version == SkOTTableHead::version1)
827
{
828
metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
829
metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
830
metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
831
metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
832
833
metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
834
return;
835
}
836
837
metrics->fTop = metrics->fAscent;
838
metrics->fBottom = metrics->fDescent;
839
}
840
841
///////////////////////////////////////////////////////////////////////////////
842
843
#include "SkColorData.h"
844
845
static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) {
846
const int width = glyph.fWidth;
847
const size_t dstRB = (width + 7) >> 3;
848
uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
849
850
int byteCount = width >> 3;
851
int bitCount = width & 7;
852
853
for (int y = 0; y < glyph.fHeight; ++y) {
854
if (byteCount > 0) {
855
for (int i = 0; i < byteCount; ++i) {
856
unsigned byte = 0;
857
byte |= src[0] & (1 << 7);
858
byte |= src[1] & (1 << 6);
859
byte |= src[2] & (1 << 5);
860
byte |= src[3] & (1 << 4);
861
byte |= src[4] & (1 << 3);
862
byte |= src[5] & (1 << 2);
863
byte |= src[6] & (1 << 1);
864
byte |= src[7] & (1 << 0);
865
dst[i] = byte;
866
src += 8;
867
}
868
}
869
if (bitCount > 0) {
870
unsigned byte = 0;
871
unsigned mask = 0x80;
872
for (int i = 0; i < bitCount; i++) {
873
byte |= (src[i]) & mask;
874
mask >>= 1;
875
}
876
dst[byteCount] = byte;
877
}
878
src += bitCount;
879
dst += dstRB;
880
}
881
}
882
883
template<bool APPLY_PREBLEND>
884
static void grayscale_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
885
const uint8_t* table8) {
886
const size_t dstRB = glyph.rowBytes();
887
const U16CPU width = glyph.fWidth;
888
uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
889
890
for (U16CPU y = 0; y < glyph.fHeight; y++) {
891
for (U16CPU i = 0; i < width; i++) {
892
U8CPU a = *(src++);
893
dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(a, table8);
894
}
895
dst = SkTAddOffset<uint8_t>(dst, dstRB);
896
}
897
}
898
899
template<bool APPLY_PREBLEND>
900
static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
901
const size_t dstRB = glyph.rowBytes();
902
const U16CPU width = glyph.fWidth;
903
uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
904
905
for (U16CPU y = 0; y < glyph.fHeight; y++) {
906
for (U16CPU i = 0; i < width; i++) {
907
U8CPU g = src[1];
908
src += 3;
909
910
// Ignore the R, B channels. It looks the closest to what
911
// D2D with grayscale AA has. But there's no way
912
// to just get a grayscale AA alpha texture from a glyph run.
913
dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(g, table8);
914
}
915
dst = SkTAddOffset<uint8_t>(dst, dstRB);
916
}
917
}
918
919
template<bool APPLY_PREBLEND, bool RGB>
920
static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
921
const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
922
const size_t dstRB = glyph.rowBytes();
923
const U16CPU width = glyph.fWidth;
924
uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
925
926
for (U16CPU y = 0; y < glyph.fHeight; y++) {
927
for (U16CPU i = 0; i < width; i++) {
928
U8CPU r, g, b;
929
if (RGB) {
930
r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
931
g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
932
b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
933
} else {
934
b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
935
g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
936
r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
937
}
938
dst[i] = SkPack888ToRGB16(r, g, b);
939
}
940
dst = SkTAddOffset<uint16_t>(dst, dstRB);
941
}
942
}
943
944
const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
945
DWRITE_RENDERING_MODE renderingMode,
946
DWRITE_TEXTURE_TYPE textureType)
947
{
948
int sizeNeeded = glyph.fWidth * glyph.fHeight;
949
if (DWRITE_TEXTURE_CLEARTYPE_3x1 == textureType) {
950
sizeNeeded *= 3;
951
}
952
if (sizeNeeded > fBits.count()) {
953
fBits.setCount(sizeNeeded);
954
}
955
956
// erase
957
memset(fBits.begin(), 0, sizeNeeded);
958
959
fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
960
fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
961
962
FLOAT advance = 0.0f;
963
964
UINT16 index = glyph.getGlyphID();
965
966
DWRITE_GLYPH_OFFSET offset;
967
offset.advanceOffset = 0.0f;
968
offset.ascenderOffset = 0.0f;
969
970
DWRITE_GLYPH_RUN run;
971
run.glyphCount = 1;
972
run.glyphAdvances = &advance;
973
run.fontFace = this->getDWriteTypeface()->fDWriteFontFace.get();
974
run.fontEmSize = SkScalarToFloat(fTextSizeRender);
975
run.bidiLevel = 0;
976
run.glyphIndices = &index;
977
run.isSideways = FALSE;
978
run.glyphOffsets = &offset;
979
{
980
SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
981
{
982
MaybeExclusive l(this);
983
// IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs.
984
if (this->getDWriteTypeface()->fFactory2 &&
985
(fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED ||
986
fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE))
987
{
988
HRNM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis(&run,
989
&fXform,
990
renderingMode,
991
fMeasuringMode,
992
fGridFitMode,
993
fAntiAliasMode,
994
0.0f, // baselineOriginX,
995
0.0f, // baselineOriginY,
996
&glyphRunAnalysis),
997
"Could not create DW2 glyph run analysis.");
998
} else {
999
HRNM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run,
1000
1.0f, // pixelsPerDip,
1001
&fXform,
1002
renderingMode,
1003
fMeasuringMode,
1004
0.0f, // baselineOriginX,
1005
0.0f, // baselineOriginY,
1006
&glyphRunAnalysis),
1007
"Could not create glyph run analysis.");
1008
}
1009
}
1010
//NOTE: this assumes that the glyph has already been measured
1011
//with an exact same glyph run analysis.
1012
RECT bbox;
1013
bbox.left = glyph.fLeft;
1014
bbox.top = glyph.fTop;
1015
bbox.right = glyph.fLeft + glyph.fWidth;
1016
bbox.bottom = glyph.fTop + glyph.fHeight;
1017
{
1018
MaybeShared l(this);
1019
HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
1020
&bbox,
1021
fBits.begin(),
1022
sizeNeeded),
1023
"Could not draw mask.");
1024
}
1025
}
1026
return fBits.begin();
1027
}
1028
1029
void SkScalerContext_DW::generateColorGlyphImage(const SkGlyph& glyph) {
1030
SkASSERT(isColorGlyph(glyph));
1031
SkASSERT(glyph.fMaskFormat == SkMask::Format::kARGB32_Format);
1032
1033
memset(glyph.fImage, 0, glyph.computeImageSize());
1034
1035
SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
1036
getColorGlyphRun(glyph, &colorLayers);
1037
SkASSERT(colorLayers.get());
1038
1039
SkMatrix matrix = fSkXform;
1040
matrix.postTranslate(-SkIntToScalar(glyph.fLeft), -SkIntToScalar(glyph.fTop));
1041
if (this->isSubpixel()) {
1042
matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
1043
SkFixedToScalar(glyph.getSubYFixed()));
1044
}
1045
SkRasterClip rc(SkIRect::MakeWH(glyph.fWidth, glyph.fHeight));
1046
SkDraw draw;
1047
draw.fDst = SkPixmap(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
1048
glyph.fImage,
1049
glyph.rowBytesUsingFormat(SkMask::Format::kARGB32_Format));
1050
draw.fMatrix = &matrix;
1051
draw.fRC = &rc;
1052
1053
SkPaint paint;
1054
paint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED);
1055
1056
BOOL hasNextRun = FALSE;
1057
while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
1058
const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
1059
HRVM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
1060
1061
SkColor color;
1062
if (colorGlyph->paletteIndex != 0xffff) {
1063
color = SkColorSetARGB(sk_float_round2int(colorGlyph->runColor.a * 255),
1064
sk_float_round2int(colorGlyph->runColor.r * 255),
1065
sk_float_round2int(colorGlyph->runColor.g * 255),
1066
sk_float_round2int(colorGlyph->runColor.b * 255));
1067
} else {
1068
// If all components of runColor are 0 or (equivalently) paletteIndex is 0xFFFF then
1069
// the 'current brush' is used. fRec.getLuminanceColor() is kinda sorta what is wanted
1070
// here, but not really, it will often be the wrong value because it wan't designed for
1071
// this.
1072
// TODO: implement this fully, bug.skia.org/5788
1073
color = fRec.getLuminanceColor();
1074
}
1075
paint.setColor(color);
1076
1077
SkPath path;
1078
SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1079
HRVM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
1080
"Could not create geometry to path converter.");
1081
{
1082
MaybeExclusive l(this);
1083
HRVM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
1084
colorGlyph->glyphRun.fontEmSize,
1085
colorGlyph->glyphRun.glyphIndices,
1086
colorGlyph->glyphRun.glyphAdvances,
1087
colorGlyph->glyphRun.glyphOffsets,
1088
colorGlyph->glyphRun.glyphCount,
1089
colorGlyph->glyphRun.isSideways,
1090
colorGlyph->glyphRun.bidiLevel % 2, //rtl
1091
geometryToPath.get()),
1092
"Could not create glyph outline.");
1093
}
1094
draw.drawPath(path, paint, nullptr, true /* pathIsMutable */);
1095
}
1096
}
1097
1098
#ifdef USE_PNG
1099
void SkScalerContext_DW::generatePngGlyphImage(const SkGlyph& glyph) {
1100
SkASSERT(isPngGlyph(glyph));
1101
SkASSERT(glyph.fMaskFormat == SkMask::Format::kARGB32_Format);
1102
SkASSERT(this->getDWriteTypeface()->fDWriteFontFace4);
1103
1104
IDWriteFontFace4* fontFace4 = this->getDWriteTypeface()->fDWriteFontFace4.get();
1105
DWRITE_GLYPH_IMAGE_DATA glyphData;
1106
void* glyphDataContext;
1107
HRVM(fontFace4->GetGlyphImageData(glyph.getGlyphID(),
1108
fTextSizeRender,
1109
DWRITE_GLYPH_IMAGE_FORMATS_PNG,
1110
&glyphData,
1111
&glyphDataContext),
1112
"Glyph image data could not be acquired.");
1113
Context* context = new Context(fontFace4, glyphDataContext);
1114
sk_sp<SkData> data = SkData::MakeWithProc(glyphData.imageData,
1115
glyphData.imageDataSize,
1116
&ReleaseProc,
1117
context);
1118
sk_sp<SkImage> image = SkImage::MakeFromEncoded(std::move(data));
1119
1120
SkBitmap dstBitmap;
1121
dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,
1122
kN32_SkColorType,
1123
kPremul_SkAlphaType),
1124
glyph.rowBytes());
1125
dstBitmap.setPixels(glyph.fImage);
1126
1127
SkCanvas canvas(dstBitmap);
1128
canvas.clear(SK_ColorTRANSPARENT);
1129
canvas.translate(-glyph.fLeft, -glyph.fTop);
1130
if (this->isSubpixel()) {
1131
canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
1132
SkFixedToScalar(glyph.getSubYFixed()));
1133
}
1134
canvas.concat(fSkXform);
1135
SkScalar ratio = fTextSizeRender / glyphData.pixelsPerEm;
1136
canvas.scale(ratio, ratio);
1137
canvas.translate(-glyphData.horizontalLeftOrigin.x, -glyphData.horizontalLeftOrigin.y);
1138
canvas.drawImage(image, 0, 0, nullptr);
1139
}
1140
#endif
1141
1142
void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
1143
//Create the mask.
1144
DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
1145
DWRITE_TEXTURE_TYPE textureType = fTextureType;
1146
if (glyph.fForceBW) {
1147
renderingMode = DWRITE_RENDERING_MODE_ALIASED;
1148
textureType = DWRITE_TEXTURE_ALIASED_1x1;
1149
}
1150
1151
if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
1152
if (fIsColorFont) {
1153
if (isColorGlyph(glyph)) {
1154
generateColorGlyphImage(glyph);
1155
return;
1156
#ifdef USE_PNG
1157
} else if (isPngGlyph(glyph)) {
1158
generatePngGlyphImage(glyph);
1159
return;
1160
#endif
1161
}
1162
}
1163
SkDEBUGFAIL("Could not generate image from the given color font format.");
1164
return;
1165
}
1166
1167
const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
1168
if (!bits) {
1169
sk_bzero(glyph.fImage, glyph.computeImageSize());
1170
return;
1171
}
1172
1173
//Copy the mask into the glyph.
1174
const uint8_t* src = (const uint8_t*)bits;
1175
if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
1176
SkASSERT(SkMask::kBW_Format == glyph.fMaskFormat);
1177
SkASSERT(DWRITE_TEXTURE_ALIASED_1x1 == textureType);
1178
bilevel_to_bw(src, glyph);
1179
} else if (!isLCD(fRec)) {
1180
if (textureType == DWRITE_TEXTURE_ALIASED_1x1) {
1181
if (fPreBlend.isApplicable()) {
1182
grayscale_to_a8<true>(src, glyph, fPreBlend.fG);
1183
} else {
1184
grayscale_to_a8<false>(src, glyph, fPreBlend.fG);
1185
}
1186
} else {
1187
if (fPreBlend.isApplicable()) {
1188
rgb_to_a8<true>(src, glyph, fPreBlend.fG);
1189
} else {
1190
rgb_to_a8<false>(src, glyph, fPreBlend.fG);
1191
}
1192
}
1193
} else {
1194
SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
1195
if (fPreBlend.isApplicable()) {
1196
if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
1197
rgb_to_lcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1198
} else {
1199
rgb_to_lcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1200
}
1201
} else {
1202
if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
1203
rgb_to_lcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1204
} else {
1205
rgb_to_lcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1206
}
1207
}
1208
}
1209
}
1210
1211
bool SkScalerContext_DW::generatePath(SkGlyphID glyph, SkPath* path) {
1212
SkASSERT(path);
1213
1214
path->reset();
1215
1216
SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
1217
HRBM(SkDWriteGeometrySink::Create(path, &geometryToPath),
1218
"Could not create geometry to path converter.");
1219
UINT16 glyphId = SkTo<UINT16>(glyph);
1220
{
1221
MaybeExclusive l(this);
1222
//TODO: convert to<->from DIUs? This would make a difference if hinting.
1223
//It may not be needed, it appears that DirectWrite only hints at em size.
1224
HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline(
1225
SkScalarToFloat(fTextSizeRender),
1226
&glyphId,
1227
nullptr, //advances
1228
nullptr, //offsets
1229
1, //num glyphs
1230
FALSE, //sideways
1231
FALSE, //rtl
1232
geometryToPath.get()),
1233
"Could not create glyph outline.");
1234
}
1235
1236
path->transform(fSkXform);
1237
return true;
1238
}
1239
1240
#endif//defined(SK_BUILD_FOR_WIN)