Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkFontHost_win.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2006 The Android Open Source Project
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
9#if defined(SK_BUILD_FOR_WIN)
10
11#include "include/core/SkData.h"
14#include "include/core/SkPath.h"
24#include "src/base/SkBase64.h"
26#include "src/base/SkUTF.h"
30#include "src/core/SkGlyph.h"
37#include "src/sfnt/SkOTUtils.h"
41
42#include <tchar.h>
43#include <usp10.h>
44#include <objbase.h>
45
46using namespace skia_private;
47
48namespace {
49static inline const constexpr bool kSkShowTextBlitCoverage = false;
50}
51
52static void (*gEnsureLOGFONTAccessibleProc)(const LOGFONT&);
53
54void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*proc)(const LOGFONT&)) {
55 gEnsureLOGFONTAccessibleProc = proc;
56}
57
58static void call_ensure_accessible(const LOGFONT& lf) {
59 if (gEnsureLOGFONTAccessibleProc) {
60 gEnsureLOGFONTAccessibleProc(lf);
61 }
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66// always packed xxRRGGBB
67typedef uint32_t SkGdiRGB;
68
69// define this in your Makefile or .gyp to enforce AA requests
70// which GDI ignores at small sizes. This flag guarantees AA
71// for rotated text, regardless of GDI's notions.
72//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
73
74static bool isLCD(const SkScalerContextRec& rec) {
76}
77
78static bool bothZero(SkScalar a, SkScalar b) {
79 return 0 == a && 0 == b;
80}
81
82// returns false if there is any non-90-rotation or skew
83static bool isAxisAligned(const SkScalerContextRec& rec) {
84 return 0 == rec.fPreSkewX &&
85 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
86 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
87}
88
89static bool needToRenderWithSkia(const SkScalerContextRec& rec) {
90#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
91 // What we really want to catch is when GDI will ignore the AA request and give
92 // us BW instead. Smallish rotated text is one heuristic, so this code is just
93 // an approximation. We shouldn't need to do this for larger sizes, but at those
94 // sizes, the quality difference gets less and less between our general
95 // scanconverter and GDI's.
96 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
97 return true;
98 }
99#endif
101}
102
103static void tchar_to_skstring(const TCHAR t[], SkString* s) {
104#ifdef UNICODE
105 size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, nullptr, 0, nullptr, nullptr);
106 s->resize(sSize);
107 WideCharToMultiByte(CP_UTF8, 0, t, -1, s->data(), sSize, nullptr, nullptr);
108#else
109 s->set(t);
110#endif
111}
112
113static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
114 int fontNameLen; //length of fontName in TCHARS.
115 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
116 call_ensure_accessible(lf);
117 if (0 == (fontNameLen = GetTextFace(deviceContext, 0, nullptr))) {
118 fontNameLen = 0;
119 }
120 }
121
122 AutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
123 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
124 call_ensure_accessible(lf);
125 if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
126 fontName[0] = 0;
127 }
128 }
129
130 tchar_to_skstring(fontName.get(), familyName);
131}
132
133static void make_canonical(LOGFONT* lf) {
134 lf->lfHeight = -64;
135 lf->lfWidth = 0; // lfWidth is related to lfHeight, not to the OS/2::usWidthClass.
136 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
137 lf->lfCharSet = DEFAULT_CHARSET;
138// lf->lfClipPrecision = 64;
139}
140
141static SkFontStyle get_style(const LOGFONT& lf) {
142 return SkFontStyle(lf.lfWeight,
144 lf.lfItalic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
145}
146
147static inline FIXED SkFixedToFIXED(SkFixed x) {
148 return *(FIXED*)(&x);
149}
150static inline SkFixed SkFIXEDToFixed(FIXED x) {
151 return *(SkFixed*)(&x);
152}
153
154static inline FIXED SkScalarToFIXED(SkScalar x) {
155 return SkFixedToFIXED(SkScalarToFixed(x));
156}
157
158static inline SkScalar SkFIXEDToScalar(FIXED x) {
159 return SkFixedToScalar(SkFIXEDToFixed(x));
160}
161
162static unsigned calculateGlyphCount(HDC hdc, const LOGFONT& lf) {
163 TEXTMETRIC textMetric;
164 if (0 == GetTextMetrics(hdc, &textMetric)) {
165 textMetric.tmPitchAndFamily = TMPF_VECTOR;
166 call_ensure_accessible(lf);
167 GetTextMetrics(hdc, &textMetric);
168 }
169
170 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
171 return textMetric.tmLastChar;
172 }
173
174 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
175 uint16_t glyphs;
176 if (GDI_ERROR != GetFontData(hdc, SkOTTableMaximumProfile::TAG, 4, &glyphs, sizeof(glyphs))) {
178 }
179
180 // Binary search for glyph count.
181 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
182 int32_t max = UINT16_MAX + 1;
183 int32_t min = 0;
184 GLYPHMETRICS gm;
185 while (min < max) {
186 int32_t mid = min + ((max - min) / 2);
187 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
188 nullptr, &mat2) == GDI_ERROR) {
189 max = mid;
190 } else {
191 min = mid + 1;
192 }
193 }
194 SkASSERT(min == max);
195 return min;
196}
197
198static unsigned calculateUPEM(HDC hdc, const LOGFONT& lf) {
199 TEXTMETRIC textMetric;
200 if (0 == GetTextMetrics(hdc, &textMetric)) {
201 textMetric.tmPitchAndFamily = TMPF_VECTOR;
202 call_ensure_accessible(lf);
203 GetTextMetrics(hdc, &textMetric);
204 }
205
206 if (!(textMetric.tmPitchAndFamily & TMPF_VECTOR)) {
207 return textMetric.tmMaxCharWidth;
208 }
209
210 OUTLINETEXTMETRIC otm;
211 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
212 if (0 == otmRet) {
213 call_ensure_accessible(lf);
214 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
215 }
216
217 return (0 == otmRet) ? 0 : otm.otmEMSquare;
218}
219
220class SkAutoHDC {
221public:
222 explicit SkAutoHDC(const LOGFONT& lf)
223 : fHdc(::CreateCompatibleDC(nullptr))
224 , fFont(::CreateFontIndirect(&lf))
225 , fSavefont((HFONT)::SelectObject(fHdc, fFont))
226 { }
227 ~SkAutoHDC() {
228 if (fHdc) {
229 ::SelectObject(fHdc, fSavefont);
230 ::DeleteDC(fHdc);
231 }
232 if (fFont) {
233 ::DeleteObject(fFont);
234 }
235 }
236 operator HDC() { return fHdc; }
237private:
238 HDC fHdc;
239 HFONT fFont;
240 HFONT fSavefont;
241};
242
243class LogFontTypeface : public SkTypeface {
244public:
245 LogFontTypeface(const SkFontStyle& style, const LOGFONT& lf, bool serializeAsStream)
246 : SkTypeface(style, false)
247 , fLogFont(lf)
248 , fSerializeAsStream(serializeAsStream)
249 {
250 SkAutoHDC hdc(fLogFont);
251 TEXTMETRIC textMetric;
252 if (0 == GetTextMetrics(hdc, &textMetric)) {
253 call_ensure_accessible(lf);
254 if (0 == GetTextMetrics(hdc, &textMetric)) {
255 textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
256 }
257 }
258
259 // The fixed pitch bit is set if the font is *not* fixed pitch.
260 this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
261 this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
262
263 // Used a logfont on a memory context, should never get a device font.
264 // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
265 // If the font has cubic outlines, it will not be rendered with ClearType.
266 fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
267 (textMetric.tmPitchAndFamily & TMPF_DEVICE));
268 }
269
270 LOGFONT fLogFont;
271 bool fSerializeAsStream;
272 bool fCanBeLCD;
273
274 static sk_sp<LogFontTypeface> Make(const LOGFONT& lf) {
275 return sk_sp<LogFontTypeface>(new LogFontTypeface(get_style(lf), lf, false));
276 }
277
278 static void EnsureAccessible(const SkTypeface* face) {
279 call_ensure_accessible(static_cast<const LogFontTypeface*>(face)->fLogFont);
280 }
281
282protected:
283 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
284 sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override;
285 std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&,
286 const SkDescriptor*) const override;
287 void onFilterRec(SkScalerContextRec*) const override;
288 void getGlyphToUnicodeMap(SkUnichar*) const override;
289 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
290 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
291 void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
292 int onCountGlyphs() const override;
293 void getPostScriptGlyphNames(SkString*) const override;
294 int onGetUPEM() const override;
295 void onGetFamilyName(SkString* familyName) const override;
296 bool onGetPostScriptName(SkString*) const override { return false; }
298 bool onGlyphMaskNeedsCurrentColor() const override { return false; }
300 int coordinateCount) const override
301 {
302 return -1;
303 }
305 int parameterCount) const override
306 {
307 return -1;
308 }
309 int onGetTableTags(SkFontTableTag tags[]) const override;
310 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
312};
313
314class FontMemResourceTypeface : public LogFontTypeface {
315public:
316 /**
317 * The created FontMemResourceTypeface takes ownership of fontMemResource.
318 */
319 static sk_sp<FontMemResourceTypeface> Make(const LOGFONT& lf, HANDLE fontMemResource) {
321 new FontMemResourceTypeface(get_style(lf), lf, fontMemResource));
322 }
323
324protected:
325 void weak_dispose() const override {
326 RemoveFontMemResourceEx(fFontMemResource);
327 INHERITED::weak_dispose();
328 }
329
330private:
331 /**
332 * Takes ownership of fontMemResource.
333 */
334 FontMemResourceTypeface(const SkFontStyle& style, const LOGFONT& lf, HANDLE fontMemResource)
335 : LogFontTypeface(style, lf, true), fFontMemResource(fontMemResource)
336 { }
337
338 HANDLE fFontMemResource;
339
340 using INHERITED = LogFontTypeface;
341};
342
343static const LOGFONT& get_default_font() {
344 static LOGFONT gDefaultFont;
345 return gDefaultFont;
346}
347
348static bool FindByLogFont(SkTypeface* face, void* ctx) {
349 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
350 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
351
352 return !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
353}
354
355/**
356 * This is public. It first searches the cache, and if a match is not found,
357 * it creates a new face.
358 */
359sk_sp<SkTypeface> SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
360 LOGFONT lf = origLF;
361 make_canonical(&lf);
362 sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
363 if (!face) {
364 face = LogFontTypeface::Make(lf);
366 }
367 return face;
368}
369
370/**
371 * The created SkTypeface takes ownership of fontMemResource.
372 */
373sk_sp<SkTypeface> SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
374 LOGFONT lf = origLF;
375 make_canonical(&lf);
376 // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
377 return FontMemResourceTypeface::Make(lf, fontMemResource);
378}
379
380/**
381 * This is public
382 */
383void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
384 if (nullptr == face) {
385 *lf = get_default_font();
386 } else {
387 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
388 }
389}
390
391// Construct Glyph to Unicode table.
392// Unicode code points that require conjugate pairs in utf16 are not
393// supported.
394// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
395// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
396// of calling GetFontUnicodeRange().
397static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
398 SkUnichar* glyphToUnicode) {
399 sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
400 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr);
401 if (!glyphSetBufferSize) {
402 return;
403 }
404
405 std::unique_ptr<BYTE[]> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
406 GLYPHSET* glyphSet =
407 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
408 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
409 return;
410 }
411
412 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
413 // There is no guarantee that within a Unicode range, the corresponding
414 // glyph id in a font file are continuous. So, even if we have ranges,
415 // we can't just use the first and last entry of the range to compute
416 // result. We need to enumerate them one by one.
417 int count = glyphSet->ranges[i].cGlyphs;
418 AutoTArray<WCHAR> chars(count + 1);
419 chars[count] = 0; // termintate string
420 AutoTArray<WORD> glyph(count);
421 for (USHORT j = 0; j < count; ++j) {
422 chars[j] = glyphSet->ranges[i].wcLow + j;
423 }
424 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
425 GGI_MARK_NONEXISTING_GLYPHS);
426 // If the glyph ID is valid, and the glyph is not mapped, then we will
427 // fill in the char id into the vector. If the glyph is mapped already,
428 // skip it.
429 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
430 // font cache, then generate this mapping table from there. It's
431 // unlikely to have collisions since glyph reuse happens mostly for
432 // different Unicode pages.
433 for (USHORT j = 0; j < count; ++j) {
434 if (glyph[j] != 0xFFFF && glyph[j] < glyphCount && glyphToUnicode[glyph[j]] == 0) {
435 glyphToUnicode[glyph[j]] = chars[j];
436 }
437 }
438 }
439}
440
441//////////////////////////////////////////////////////////////////////////////////////
442
443static int alignTo32(int n) {
444 return (n + 31) & ~31;
445}
446
447struct MyBitmapInfo : public BITMAPINFO {
448 RGBQUAD fMoreSpaceForColors[1];
449};
450
451class HDCOffscreen {
452public:
453 HDCOffscreen() = default;
454
455 ~HDCOffscreen() {
456 if (fDC) {
457 ::SelectObject(fDC, fSavefont);
458 ::DeleteDC(fDC);
459 }
460 if (fBM) {
461 DeleteObject(fBM);
462 }
463 }
464
465 void init(HFONT font, const XFORM& xform) {
466 fFont = font;
467 fXform = xform;
468 }
469
470 void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
471
472private:
473 HDC fDC{nullptr};
474 HFONT fSavefont{nullptr};
475 HBITMAP fBM{nullptr};
476 HFONT fFont{nullptr};
477 XFORM fXform{1, 0, 0, 1, 0, 0};
478 void* fBits{nullptr}; // points into fBM
479 int fWidth{0};
480 int fHeight{0};
481 bool fIsBW{false};
482};
483
484void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr) {
485 // Can we share the scalercontext's fDDC, so we don't need to create
486 // a separate fDC here?
487 if (nullptr == fDC) {
488 fDC = CreateCompatibleDC(0);
489 if (nullptr == fDC) {
490 return nullptr;
491 }
492 SetGraphicsMode(fDC, GM_ADVANCED);
493 SetBkMode(fDC, TRANSPARENT);
494 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
495 fSavefont = (HFONT)SelectObject(fDC, fFont);
496
497 COLORREF color = 0x00FFFFFF;
498 SkDEBUGCODE(COLORREF prev =) SetTextColor(fDC, color);
499 SkASSERT(prev != CLR_INVALID);
500 }
501
502 if (fBM && (fIsBW != isBW || fWidth < glyph.width() || fHeight < glyph.height())) {
503 DeleteObject(fBM);
504 fBM = nullptr;
505 }
506 fIsBW = isBW;
507
508 fWidth = std::max(fWidth, glyph.width());
509 fHeight = std::max(fHeight, glyph.height());
510
511 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
512
513 if (nullptr == fBM) {
514 MyBitmapInfo info;
515 sk_bzero(&info, sizeof(info));
516 if (isBW) {
517 RGBQUAD blackQuad = { 0, 0, 0, 0 };
518 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
519 info.bmiColors[0] = blackQuad;
520 info.bmiColors[1] = whiteQuad;
521 }
522 info.bmiHeader.biSize = sizeof(info.bmiHeader);
523 info.bmiHeader.biWidth = biWidth;
524 info.bmiHeader.biHeight = fHeight;
525 info.bmiHeader.biPlanes = 1;
526 info.bmiHeader.biBitCount = isBW ? 1 : 32;
527 info.bmiHeader.biCompression = BI_RGB;
528 if (isBW) {
529 info.bmiHeader.biClrUsed = 2;
530 }
531 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
532 if (nullptr == fBM) {
533 return nullptr;
534 }
535 SelectObject(fDC, fBM);
536 }
537
538 // erase
539 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
540 size_t size = fHeight * srcRB;
541 memset(fBits, 0, size);
542
543 XFORM xform = fXform;
544 xform.eDx = (float)-glyph.left();
545 xform.eDy = (float)-glyph.top();
546 SetWorldTransform(fDC, &xform);
547
548 uint16_t glyphID = glyph.getGlyphID();
549 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, nullptr, reinterpret_cast<LPCWSTR>(&glyphID),
550 1, nullptr);
551 GdiFlush();
552 if (0 == ret) {
553 return nullptr;
554 }
555 *srcRBPtr = srcRB;
556 // offset to the start of the image
557 return (char*)fBits + (fHeight - glyph.height()) * srcRB;
558}
559
560//////////////////////////////////////////////////////////////////////////////
561#define BUFFERSIZE (1 << 13)
562
563class SkScalerContext_GDI : public SkScalerContext {
564public:
565 SkScalerContext_GDI(sk_sp<LogFontTypeface>,
567 const SkDescriptor* desc);
568 ~SkScalerContext_GDI() override;
569
570 // Returns true if the constructor was able to complete all of its
571 // initializations (which may include calling GDI).
572 bool isValid() const;
573
574protected:
575 GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override;
576 void generateImage(const SkGlyph&, void* imageBuffer) override;
577 bool generatePath(const SkGlyph& glyph, SkPath* path) override;
578 void generateFontMetrics(SkFontMetrics*) override;
579
580private:
581 DWORD getGDIGlyphPath(SkGlyphID glyph, UINT flags,
583 template<bool APPLY_PREBLEND>
584 static void RGBToA8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
585 const SkGlyph& glyph, void* imageBuffer, const uint8_t* table8);
586
587 template<bool APPLY_PREBLEND>
588 static void RGBToLcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
589 void* imageBuffer,
590 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB);
591
592 HDCOffscreen fOffscreen;
593 /** fGsA is the non-rotational part of total matrix without the text height scale.
594 * Used to find the magnitude of advances.
595 */
596 MAT2 fGsA;
597 /** The total matrix without the textSize. */
598 MAT2 fMat22;
599 /** Scales font to EM size. */
600 MAT2 fHighResMat22;
601 HDC fDDC;
602 HFONT fSavefont;
603 HFONT fFont;
604 SCRIPT_CACHE fSC;
605
606 /** The total matrix which also removes EM scale. */
607 SkMatrix fHiResMatrix;
608 /** fG_inv is the inverse of the rotational part of the total matrix.
609 * Used to set the direction of advances.
610 */
611 SkMatrix fG_inv;
612 enum Type {
613 kTrueType_Type, kBitmap_Type, kLine_Type
614 } fType;
615 TEXTMETRIC fTM;
616};
617
618static FIXED SkFloatToFIXED(float x) {
619 return SkFixedToFIXED(SkFloatToFixed(x));
620}
621
622static inline float SkFIXEDToFloat(FIXED x) {
623 return SkFixedToFloat(SkFIXEDToFixed(x));
624}
625
626static BYTE compute_quality(const SkScalerContextRec& rec) {
627 switch (rec.fMaskFormat) {
629 return NONANTIALIASED_QUALITY;
631 return CLEARTYPE_QUALITY;
632 default:
634 return CLEARTYPE_QUALITY;
635 } else {
636 return ANTIALIASED_QUALITY;
637 }
638 }
639}
640
641SkScalerContext_GDI::SkScalerContext_GDI(sk_sp<LogFontTypeface> rawTypeface,
642 const SkScalerContextEffects& effects,
643 const SkDescriptor* desc)
644 : SkScalerContext(std::move(rawTypeface), effects, desc)
645 , fDDC(nullptr)
646 , fSavefont(nullptr)
647 , fFont(nullptr)
648 , fSC(nullptr)
649{
650 LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface());
651
652 fDDC = ::CreateCompatibleDC(nullptr);
653 if (!fDDC) {
654 return;
655 }
656 SetGraphicsMode(fDDC, GM_ADVANCED);
657 SetBkMode(fDDC, TRANSPARENT);
658
659 // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
660 // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
661 SkScalerContextRec::PreMatrixScale scaleConstraints =
662 (fRec.getHinting() == SkFontHinting::kNone || fRec.getHinting() == SkFontHinting::kSlight)
664 : SkScalerContextRec::PreMatrixScale::kVertical;
666 SkMatrix sA;
667 SkMatrix GsA;
668 SkMatrix A;
669 fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
670
671 fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
672 fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
673 fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
674 fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
675
676 // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
677 // The sA and GsA transforms will be used to create 'linear' metrics.
678
679 // When hinting, scale was computed with kVertical, stating that our port can handle
680 // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
681 // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
682 // scales so we need to round in this case. This is fine, since all of the scale has been
683 // removed from sA and GsA, so GDI will be handling the scale completely.
684 SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
685
686 // GDI will not accept a size of zero, so round the range [0, 1] to 1.
687 // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
688 // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
689 if (gdiTextSize == 0) {
690 gdiTextSize = SK_Scalar1;
691 }
692
693 LOGFONT lf = typeface->fLogFont;
694 lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
695 lf.lfQuality = compute_quality(fRec);
696 fFont = CreateFontIndirect(&lf);
697 if (!fFont) {
698 return;
699 }
700
701 fSavefont = (HFONT)SelectObject(fDDC, fFont);
702
703 if (0 == GetTextMetrics(fDDC, &fTM)) {
704 call_ensure_accessible(lf);
705 if (0 == GetTextMetrics(fDDC, &fTM)) {
706 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
707 }
708 }
709
710 XFORM xform;
711 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
712 // Used a logfont on a memory context, should never get a device font.
713 // Therefore all TMPF_DEVICE will be PostScript fonts.
714
715 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE means that
716 // we have an outline font. Otherwise we have a vector FON, which is
717 // scalable, but not an outline font.
718 // This was determined by testing with Type1 PFM/PFB and
719 // OpenTypeCFF OTF, as well as looking at Wine bugs and sources.
720 if (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)) {
721 // Truetype or PostScript.
722 fType = SkScalerContext_GDI::kTrueType_Type;
723 } else {
724 // Stroked FON.
725 fType = SkScalerContext_GDI::kLine_Type;
726 }
727
728 // fPost2x2 is column-major, left handed (y down).
729 // XFORM 2x2 is row-major, left handed (y down).
730 xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX));
731 xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY));
732 xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX));
733 xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY));
734 xform.eDx = 0;
735 xform.eDy = 0;
736
737 // MAT2 is row major, right handed (y up).
738 fMat22.eM11 = SkFloatToFIXED(xform.eM11);
739 fMat22.eM12 = SkFloatToFIXED(-xform.eM12);
740 fMat22.eM21 = SkFloatToFIXED(-xform.eM21);
741 fMat22.eM22 = SkFloatToFIXED(xform.eM22);
742
743 if (needToRenderWithSkia(fRec)) {
744 this->forceGenerateImageFromPath();
745 }
746
747 // Create a hires matrix if we need linear metrics.
748 if (this->isLinearMetrics()) {
749 OUTLINETEXTMETRIC otm;
750 UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
751 if (0 == success) {
752 call_ensure_accessible(lf);
753 success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
754 }
755 if (0 != success) {
756 SkScalar upem = SkIntToScalar(otm.otmEMSquare);
757
758 SkScalar gdiTextSizeToEMScale = upem / gdiTextSize;
759 fHighResMat22.eM11 = SkScalarToFIXED(gdiTextSizeToEMScale);
760 fHighResMat22.eM12 = SkScalarToFIXED(0);
761 fHighResMat22.eM21 = SkScalarToFIXED(0);
762 fHighResMat22.eM22 = SkScalarToFIXED(gdiTextSizeToEMScale);
763
764 SkScalar removeEMScale = SkScalarInvert(upem);
765 fHiResMatrix = A;
766 fHiResMatrix.preScale(removeEMScale, removeEMScale);
767 }
768 }
769
770 } else {
771 // Assume bitmap
772 fType = SkScalerContext_GDI::kBitmap_Type;
773
774 xform.eM11 = 1.0f;
775 xform.eM12 = 0.0f;
776 xform.eM21 = 0.0f;
777 xform.eM22 = 1.0f;
778 xform.eDx = 0.0f;
779 xform.eDy = 0.0f;
780
781 // fPost2x2 is column-major, left handed (y down).
782 // MAT2 is row major, right handed (y up).
783 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
784 fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
785 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
786 fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
787 }
788
789 fOffscreen.init(fFont, xform);
790}
791
792SkScalerContext_GDI::~SkScalerContext_GDI() {
793 if (fDDC) {
794 ::SelectObject(fDDC, fSavefont);
795 ::DeleteDC(fDDC);
796 }
797 if (fFont) {
798 ::DeleteObject(fFont);
799 }
800 if (fSC) {
801 ::ScriptFreeCache(&fSC);
802 }
803}
804
805bool SkScalerContext_GDI::isValid() const {
806 return fDDC && fFont;
807}
808
809SkScalerContext::GlyphMetrics SkScalerContext_GDI::generateMetrics(const SkGlyph& glyph,
810 SkArenaAlloc*) {
811 SkASSERT(fDDC);
812
813 GlyphMetrics mx(glyph.maskFormat());
814
815 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
816 SIZE size;
817 WORD glyphs = glyph.getGlyphID();
818 int width, height;
819 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
820 width = fTM.tmMaxCharWidth;
821 height = fTM.tmHeight;
822 } else {
823 width = size.cx;
824 height = size.cy;
825 }
826
827 // Bitmap FON cannot underhang, but vector FON may.
828 // There appears no means of determining underhang of vector FON.
829 int left = 0;
830 int top = -fTM.tmAscent;
831
832 mx.bounds = SkRect::MakeXYWH(left, top, width, height);
833 mx.advance = SkVector{(float)width, 0};
834
835 // Vector FON will transform nicely, but bitmap FON do not.
836 if (fType == SkScalerContext_GDI::kLine_Type) {
838 SkMatrix m;
839 m.setAll(SkFIXEDToScalar(fMat22.eM11), -SkFIXEDToScalar(fMat22.eM21), 0,
840 -SkFIXEDToScalar(fMat22.eM12), SkFIXEDToScalar(fMat22.eM22), 0,
841 0, 0, 1);
842 m.mapRect(&bounds);
843 bounds.roundOut(&mx.bounds);
844 }
845
846 // Apply matrix to advance.
847 mx.advance.fY = -SkFIXEDToFloat(fMat22.eM12) * mx.advance.fX;
848 mx.advance.fX *= SkFIXEDToFloat(fMat22.eM11);
849
850 // These do not have an outline path at all.
851 mx.neverRequestPath = true;
852 return mx;
853 }
854
855 UINT glyphId = glyph.getGlyphID();
856
857 GLYPHMETRICS gm;
858 sk_bzero(&gm, sizeof(gm));
859
860 DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
861 if (GDI_ERROR == status) {
862 LogFontTypeface::EnsureAccessible(this->getTypeface());
863 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
864 if (GDI_ERROR == status) {
865 return mx;
866 }
867 }
868
869 bool empty = false;
870 // The black box is either the embedded bitmap size or the outline extent.
871 // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
872 // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
873 if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
874 // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
875 DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fMat22);
876 empty = (0 == bufferSize);
877 }
878
879
880 if (!empty) {
881 int y = -gm.gmptGlyphOrigin.y;
882 int x = gm.gmptGlyphOrigin.x;
883 // Outset, since the image may bleed out of the black box.
884 // For embedded bitmaps the black box should be exact.
885 // For outlines we need to outset by 1 in all directions for bleed.
886 // For ClearType we need to outset by 2 for bleed.
887 mx.bounds = SkRect::MakeXYWH(x, y, gm.gmBlackBoxX, gm.gmBlackBoxY).makeOutset(2, 2);
888 }
889 // TODO(benjaminwagner): What is the type of gm.gmCellInc[XY]?
890 mx.advance.fX = (float)((int)gm.gmCellIncX);
891 mx.advance.fY = (float)((int)gm.gmCellIncY);
892
893 if ((fTM.tmPitchAndFamily & TMPF_VECTOR) && this->isLinearMetrics()) {
894 sk_bzero(&gm, sizeof(gm));
895 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22);
896 if (GDI_ERROR != status) {
897 mx.advance = fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX),
898 SkIntToScalar(gm.gmCellIncY));
899 }
900 } else if (!isAxisAligned(this->fRec)) {
901 status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA);
902 if (GDI_ERROR != status) {
903 mx.advance = fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY));
904 }
905 }
906 return mx;
907}
908
909static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
910void SkScalerContext_GDI::generateFontMetrics(SkFontMetrics* metrics) {
911 if (nullptr == metrics) {
912 return;
913 }
914 sk_bzero(metrics, sizeof(*metrics));
915
916 SkASSERT(fDDC);
917
918#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
919 if (fType == SkScalerContext_GDI::kBitmap_Type || fType == SkScalerContext_GDI::kLine_Type) {
920#endif
921 metrics->fTop = SkIntToScalar(-fTM.tmAscent);
922 metrics->fAscent = SkIntToScalar(-fTM.tmAscent);
923 metrics->fDescent = SkIntToScalar(fTM.tmDescent);
924 metrics->fBottom = SkIntToScalar(fTM.tmDescent);
925 metrics->fLeading = SkIntToScalar(fTM.tmExternalLeading);
926 metrics->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth);
927 metrics->fMaxCharWidth = SkIntToScalar(fTM.tmMaxCharWidth);
928 metrics->fXMin = 0;
929 metrics->fXMax = metrics->fMaxCharWidth;
930 //metrics->fXHeight = 0;
931#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
932 return;
933 }
934#endif
935
936 OUTLINETEXTMETRIC otm;
937
938 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
939 if (0 == ret) {
940 LogFontTypeface::EnsureAccessible(this->getTypeface());
941 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
942 }
943 if (0 == ret) {
944 return;
945 }
946
947#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS
948 metrics->fTop = SkIntToScalar(-otm.otmrcFontBox.top);
949 metrics->fAscent = SkIntToScalar(-otm.otmAscent);
950 metrics->fDescent = SkIntToScalar(-otm.otmDescent);
951 metrics->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom);
952 metrics->fLeading = SkIntToScalar(otm.otmLineGap);
953 metrics->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth);
954 metrics->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth);
955 metrics->fXMin = SkIntToScalar(otm.otmrcFontBox.left);
956 metrics->fXMax = SkIntToScalar(otm.otmrcFontBox.right);
957#endif
958 metrics->fUnderlineThickness = SkIntToScalar(otm.otmsUnderscoreSize);
959 metrics->fUnderlinePosition = -SkIntToScalar(otm.otmsUnderscorePosition);
960
963
964 metrics->fXHeight = SkIntToScalar(otm.otmsXHeight);
965 GLYPHMETRICS gm;
966 sk_bzero(&gm, sizeof(gm));
967 DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, nullptr, &gMat2Identity);
968 if (len != GDI_ERROR && gm.gmBlackBoxY > 0) {
969 metrics->fXHeight = SkIntToScalar(gm.gmBlackBoxY);
970 }
971}
972
973////////////////////////////////////////////////////////////////////////////////////////
974
975static void build_power_table(uint8_t table[], float ee) {
976 for (int i = 0; i < 256; i++) {
977 float x = i / 255.f;
978 x = std::pow(x, ee);
979 int xx = SkScalarRoundToInt(x * 255);
980 table[i] = SkToU8(xx);
981 }
982}
983
984/**
985 * This will invert the gamma applied by GDI (gray-scale antialiased), so we
986 * can get linear values.
987 *
988 * GDI grayscale appears to use a hard-coded gamma of 2.3.
989 *
990 * GDI grayscale appears to draw using the black and white rasterizer at four
991 * times the size and then downsamples to compute the coverage mask. As a
992 * result there are only seventeen total grays. This lack of fidelity means
993 * that shifting into other color spaces is imprecise.
994 */
995static const uint8_t* getInverseGammaTableGDI() {
996 static SkOnce once;
997 static uint8_t gTableGdi[256];
998 once([]{
999 build_power_table(gTableGdi, 2.3f);
1000 });
1001 return gTableGdi;
1002}
1003
1004/**
1005 * This will invert the gamma applied by GDI ClearType, so we can get linear
1006 * values.
1007 *
1008 * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
1009 * If this value is not specified, the default is a gamma of 1.4.
1010 */
1011static const uint8_t* getInverseGammaTableClearType() {
1012 static SkOnce once;
1013 static uint8_t gTableClearType[256];
1014 once([]{
1015 UINT level = 0;
1016 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
1017 // can't get the data, so use a default
1018 level = 1400;
1019 }
1020 build_power_table(gTableClearType, level / 1000.0f);
1021 });
1022 return gTableClearType;
1023}
1024
1026
1027//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
1028template<bool APPLY_PREBLEND>
1029static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
1030 U8CPU r = (rgb >> 16) & 0xFF;
1031 U8CPU g = (rgb >> 8) & 0xFF;
1032 U8CPU b = (rgb >> 0) & 0xFF;
1033 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1034}
1035
1036template<bool APPLY_PREBLEND>
1037static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
1038 const uint8_t* tableG,
1039 const uint8_t* tableB) {
1040 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1041 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1042 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1043 if constexpr (kSkShowTextBlitCoverage) {
1044 r = std::max(r, 10u);
1045 g = std::max(g, 10u);
1046 b = std::max(b, 10u);
1047 }
1048 return SkPack888ToRGB16(r, g, b);
1049}
1050
1051template<bool APPLY_PREBLEND>
1052void SkScalerContext_GDI::RGBToA8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
1053 const SkGlyph& glyph, void* imageBuffer, const uint8_t* table8) {
1054 const size_t dstRB = glyph.rowBytes();
1055 const int width = glyph.width();
1056 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)imageBuffer + (glyph.height() - 1) * dstRB);
1057
1058 for (int y = 0; y < glyph.height(); y++) {
1059 for (int i = 0; i < width; i++) {
1060 dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
1061 if constexpr (kSkShowTextBlitCoverage) {
1062 dst[i] = std::max<uint8_t>(dst[i], 10u);
1063 }
1064 }
1065 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
1066 dst -= dstRB;
1067 }
1068}
1069
1070template<bool APPLY_PREBLEND>
1071void SkScalerContext_GDI::RGBToLcd16(
1072 const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph, void* imageBuffer,
1073 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1074 const size_t dstRB = glyph.rowBytes();
1075 const int width = glyph.width();
1076 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)imageBuffer + (glyph.height() - 1) * dstRB);
1077
1078 for (int y = 0; y < glyph.height(); y++) {
1079 for (int i = 0; i < width; i++) {
1080 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
1081 }
1082 src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
1083 dst = (uint16_t*)((char*)dst - dstRB);
1084 }
1085}
1086
1087void SkScalerContext_GDI::generateImage(const SkGlyph& glyph, void* imageBuffer) {
1088 SkASSERT(fDDC);
1089
1090 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
1091 const bool isAA = !isLCD(fRec);
1092
1093 size_t srcRB;
1094 void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
1095 if (nullptr == bits) {
1096 LogFontTypeface::EnsureAccessible(this->getTypeface());
1097 bits = fOffscreen.draw(glyph, isBW, &srcRB);
1098 if (nullptr == bits) {
1099 sk_bzero(imageBuffer, glyph.imageSize());
1100 return;
1101 }
1102 }
1103
1104 if (!isBW) {
1105 const uint8_t* table;
1106 //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
1107 //Otherwise the offscreen contains a ClearType blit.
1108 if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1109 table = getInverseGammaTableGDI();
1110 } else {
1111 table = getInverseGammaTableClearType();
1112 }
1113 //Note that the following cannot really be integrated into the
1114 //pre-blend, since we may not be applying the pre-blend; when we aren't
1115 //applying the pre-blend it means that a filter wants linear anyway.
1116 //Other code may also be applying the pre-blend, so we'd need another
1117 //one with this and one without.
1118 SkGdiRGB* addr = (SkGdiRGB*)bits;
1119 for (int y = 0; y < glyph.height(); ++y) {
1120 for (int x = 0; x < glyph.width(); ++x) {
1121 int r = (addr[x] >> 16) & 0xFF;
1122 int g = (addr[x] >> 8) & 0xFF;
1123 int b = (addr[x] >> 0) & 0xFF;
1124 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1125 }
1126 addr = SkTAddOffset<SkGdiRGB>(addr, srcRB);
1127 }
1128 }
1129
1130 size_t dstRB = glyph.rowBytes();
1131 if (isBW) {
1132 const uint8_t* src = (const uint8_t*)bits;
1133 uint8_t* dst = (uint8_t*)((char*)imageBuffer + (glyph.height() - 1) * dstRB);
1134 for (int y = 0; y < glyph.height(); y++) {
1135 memcpy(dst, src, dstRB);
1136 src += srcRB;
1137 dst -= dstRB;
1138 }
1139 if constexpr (kSkShowTextBlitCoverage) {
1140 if (glyph.width() > 0 && glyph.height() > 0) {
1141 int bitCount = glyph.width() & 7;
1142 uint8_t* first = (uint8_t*)imageBuffer;
1143 uint8_t* last = first + glyph.height() * dstRB - 1;
1144 *first |= 1 << 7;
1145 *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount);
1146 }
1147 }
1148 } else if (isAA) {
1149 // since the caller may require A8 for maskfilters, we can't check for BW
1150 // ... until we have the caller tell us that explicitly
1151 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1152 if (fPreBlend.isApplicable()) {
1153 RGBToA8<true>(src, srcRB, glyph, imageBuffer, fPreBlend.fG);
1154 } else {
1155 RGBToA8<false>(src, srcRB, glyph, imageBuffer, fPreBlend.fG);
1156 }
1157 } else { // LCD16
1158 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1160 if (fPreBlend.isApplicable()) {
1161 RGBToLcd16<true>(src, srcRB, glyph, imageBuffer,
1162 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1163 } else {
1164 RGBToLcd16<false>(src, srcRB, glyph, imageBuffer,
1165 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1166 }
1167 }
1168}
1169
1170namespace {
1171
1172class GDIGlyphbufferPointIter {
1173public:
1174 GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size)
1175 : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter()
1176 { }
1177
1178 POINTFX const * next() {
1179nextHeader:
1180 if (!fCurveIter.isSet()) {
1181 const TTPOLYGONHEADER* header = fHeaderIter.next();
1182 if (nullptr == header) {
1183 return nullptr;
1184 }
1185 fCurveIter.set(header);
1186 const TTPOLYCURVE* curve = fCurveIter.next();
1187 if (nullptr == curve) {
1188 return nullptr;
1189 }
1190 fPointIter.set(curve);
1191 return &header->pfxStart;
1192 }
1193
1194 const POINTFX* nextPoint = fPointIter.next();
1195 if (nullptr == nextPoint) {
1196 const TTPOLYCURVE* curve = fCurveIter.next();
1197 if (nullptr == curve) {
1198 fCurveIter.set();
1199 goto nextHeader;
1200 } else {
1201 fPointIter.set(curve);
1202 }
1203 nextPoint = fPointIter.next();
1204 }
1205 return nextPoint;
1206 }
1207
1208 WORD currentCurveType() {
1209 return fPointIter.fCurveType;
1210 }
1211
1212private:
1213 /** Iterates over all of the polygon headers in a glyphbuf. */
1214 class GDIPolygonHeaderIter {
1215 public:
1216 GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size)
1217 : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf))
1218 , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size))
1219 { }
1220
1221 const TTPOLYGONHEADER* next() {
1222 if (fCurPolygon >= fEndPolygon) {
1223 return nullptr;
1224 }
1225 const TTPOLYGONHEADER* thisPolygon = fCurPolygon;
1226 fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb);
1227 return thisPolygon;
1228 }
1229 private:
1230 const TTPOLYGONHEADER* fCurPolygon;
1231 const TTPOLYGONHEADER* fEndPolygon;
1232 };
1233
1234 /** Iterates over all of the polygon curves in a polygon header. */
1235 class GDIPolygonCurveIter {
1236 public:
1237 GDIPolygonCurveIter() : fCurCurve(nullptr), fEndCurve(nullptr) { }
1238
1239 GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon)
1240 : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)))
1241 , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb))
1242 { }
1243
1244 bool isSet() { return fCurCurve != nullptr; }
1245
1246 void set(const TTPOLYGONHEADER* curPolygon) {
1247 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER));
1248 fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb);
1249 }
1250 void set() {
1251 fCurCurve = nullptr;
1252 fEndCurve = nullptr;
1253 }
1254
1255 const TTPOLYCURVE* next() {
1256 if (fCurCurve >= fEndCurve) {
1257 return nullptr;
1258 }
1259 const TTPOLYCURVE* thisCurve = fCurCurve;
1260 fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve));
1261 return thisCurve;
1262 }
1263 private:
1264 size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) {
1265 return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX);
1266 }
1267 const TTPOLYCURVE* fCurCurve;
1268 const TTPOLYCURVE* fEndCurve;
1269 };
1270
1271 /** Iterates over all of the polygon points in a polygon curve. */
1272 class GDIPolygonCurvePointIter {
1273 public:
1274 GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(nullptr), fEndPoint(nullptr) { }
1275
1276 GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon)
1277 : fCurveType(curPolygon->wType)
1278 , fCurPoint(&curPolygon->apfx[0])
1279 , fEndPoint(&curPolygon->apfx[curPolygon->cpfx])
1280 { }
1281
1282 bool isSet() { return fCurPoint != nullptr; }
1283
1284 void set(const TTPOLYCURVE* curPolygon) {
1285 fCurveType = curPolygon->wType;
1286 fCurPoint = &curPolygon->apfx[0];
1287 fEndPoint = &curPolygon->apfx[curPolygon->cpfx];
1288 }
1289 void set() {
1290 fCurPoint = nullptr;
1291 fEndPoint = nullptr;
1292 }
1293
1294 const POINTFX* next() {
1295 if (fCurPoint >= fEndPoint) {
1296 return nullptr;
1297 }
1298 const POINTFX* thisPoint = fCurPoint;
1299 ++fCurPoint;
1300 return thisPoint;
1301 }
1302
1303 WORD fCurveType;
1304 private:
1305 const POINTFX* fCurPoint;
1306 const POINTFX* fEndPoint;
1307 };
1308
1309 GDIPolygonHeaderIter fHeaderIter;
1310 GDIPolygonCurveIter fCurveIter;
1311 GDIPolygonCurvePointIter fPointIter;
1312};
1313
1314class SkGDIGeometrySink {
1315 SkPath* fPath;
1316 bool fStarted = false;
1317 POINTFX fCurrent;
1318
1319 void goingTo(const POINTFX pt) {
1320 if (!fStarted) {
1321 fStarted = true;
1322 fPath->moveTo( SkFIXEDToScalar(fCurrent.x),
1323 -SkFIXEDToScalar(fCurrent.y));
1324 }
1325 fCurrent = pt;
1326 }
1327
1328 bool currentIsNot(const POINTFX pt) {
1329 return fCurrent.x.value != pt.x.value || fCurrent.x.fract != pt.x.fract ||
1330 fCurrent.y.value != pt.y.value || fCurrent.y.fract != pt.y.fract;
1331 }
1332
1333public:
1334 SkGDIGeometrySink(SkPath* path) : fPath(path) {}
1335 void process(const uint8_t* glyphbuf, DWORD total_size);
1336
1337 /** It is possible for the hinted and unhinted versions of the same path to have
1338 * a different number of points due to GDI's handling of flipped points.
1339 * If this is detected, this will return false.
1340 */
1341 bool process(const uint8_t* glyphbuf, DWORD total_size, GDIGlyphbufferPointIter hintedYs);
1342};
1343
1344void SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size) {
1345 const uint8_t* cur_glyph = glyphbuf;
1346 const uint8_t* end_glyph = glyphbuf + total_size;
1347
1348 while (cur_glyph < end_glyph) {
1349 const TTPOLYGONHEADER* th = (const TTPOLYGONHEADER*)cur_glyph;
1350
1351 const uint8_t* end_poly = cur_glyph + th->cb;
1352 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1353
1354 fStarted = false;
1355 fCurrent = th->pfxStart;
1356
1357 while (cur_poly < end_poly) {
1358 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1359 const POINTFX* apfx = pc->apfx;
1360 const WORD cpfx = pc->cpfx;
1361
1362 if (pc->wType == TT_PRIM_LINE) {
1363 for (uint16_t i = 0; i < cpfx; i++) {
1364 POINTFX pnt_b = apfx[i];
1365 if (this->currentIsNot(pnt_b)) {
1366 this->goingTo(pnt_b);
1367 fPath->lineTo( SkFIXEDToScalar(pnt_b.x),
1368 -SkFIXEDToScalar(pnt_b.y));
1369 }
1370 }
1371 }
1372
1373 if (pc->wType == TT_PRIM_QSPLINE) {
1374 for (uint16_t u = 0; u < cpfx - 1; u++) { // Walk through points in spline
1375 POINTFX pnt_b = apfx[u]; // B is always the current point
1376 POINTFX pnt_c = apfx[u+1];
1377
1378 if (u < cpfx - 2) { // If not on last spline, compute C
1379 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1380 SkFIXEDToFixed(pnt_c.x)));
1381 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1382 SkFIXEDToFixed(pnt_c.y)));
1383 }
1384
1385
1386 if (this->currentIsNot(pnt_b) || this->currentIsNot(pnt_c)) {
1387 this->goingTo(pnt_c);
1388 fPath->quadTo( SkFIXEDToScalar(pnt_b.x),
1389 -SkFIXEDToScalar(pnt_b.y),
1390 SkFIXEDToScalar(pnt_c.x),
1391 -SkFIXEDToScalar(pnt_c.y));
1392 }
1393 }
1394 }
1395
1396 // Advance past this TTPOLYCURVE.
1397 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * cpfx;
1398 }
1399 cur_glyph += th->cb;
1400 if (this->fStarted) {
1401 fPath->close();
1402 }
1403 }
1404}
1405
1406#define move_next_expected_hinted_point(iter, pElem) do {\
1407 pElem = iter.next(); \
1408 if (nullptr == pElem) return false; \
1409} while(0)
1410
1411bool SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size,
1412 GDIGlyphbufferPointIter hintedYs) {
1413 const uint8_t* cur_glyph = glyphbuf;
1414 const uint8_t* end_glyph = glyphbuf + total_size;
1415
1416 POINTFX const * hintedPoint;
1417
1418 while (cur_glyph < end_glyph) {
1419 const TTPOLYGONHEADER* th = (const TTPOLYGONHEADER*)cur_glyph;
1420
1421 const uint8_t* end_poly = cur_glyph + th->cb;
1422 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1423
1424 move_next_expected_hinted_point(hintedYs, hintedPoint);
1425 fStarted = false;
1426 fCurrent = {th->pfxStart.x, hintedPoint->y};
1427
1428 while (cur_poly < end_poly) {
1429 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1430 const POINTFX* apfx = pc->apfx;
1431 const WORD cpfx = pc->cpfx;
1432
1433 if (pc->wType == TT_PRIM_LINE) {
1434 for (uint16_t i = 0; i < cpfx; i++) {
1435 move_next_expected_hinted_point(hintedYs, hintedPoint);
1436 POINTFX pnt_b = {apfx[i].x, hintedPoint->y};
1437 if (this->currentIsNot(pnt_b)) {
1438 this->goingTo(pnt_b);
1439 fPath->lineTo( SkFIXEDToScalar(pnt_b.x),
1440 -SkFIXEDToScalar(pnt_b.y));
1441 }
1442 }
1443 }
1444
1445 if (pc->wType == TT_PRIM_QSPLINE) {
1446 POINTFX currentPoint = apfx[0];
1447 move_next_expected_hinted_point(hintedYs, hintedPoint);
1448 // only take the hinted y if it wasn't flipped
1449 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
1450 currentPoint.y = hintedPoint->y;
1451 }
1452 for (uint16_t u = 0; u < cpfx - 1; u++) { // Walk through points in spline
1453 POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point
1454 POINTFX pnt_c = apfx[u+1];
1455 move_next_expected_hinted_point(hintedYs, hintedPoint);
1456 // only take the hinted y if it wasn't flipped
1457 if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) {
1458 pnt_c.y = hintedPoint->y;
1459 }
1460 currentPoint.x = pnt_c.x;
1461 currentPoint.y = pnt_c.y;
1462
1463 if (u < cpfx - 2) { // If not on last spline, compute C
1464 pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x),
1465 SkFIXEDToFixed(pnt_c.x)));
1466 pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y),
1467 SkFIXEDToFixed(pnt_c.y)));
1468 }
1469
1470 if (this->currentIsNot(pnt_b) || this->currentIsNot(pnt_c)) {
1471 this->goingTo(pnt_c);
1472 fPath->quadTo( SkFIXEDToScalar(pnt_b.x),
1473 -SkFIXEDToScalar(pnt_b.y),
1474 SkFIXEDToScalar(pnt_c.x),
1475 -SkFIXEDToScalar(pnt_c.y));
1476 }
1477 }
1478 }
1479
1480 // Advance past this TTPOLYCURVE.
1481 cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * cpfx;
1482 }
1483 cur_glyph += th->cb;
1484 if (this->fStarted) {
1485 fPath->close();
1486 }
1487 }
1488 return true;
1489}
1490} // namespace
1491
1492DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags,
1494{
1495 GLYPHMETRICS gm;
1496
1497 DWORD total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22);
1498 // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0.
1499 // It has been verified that this does not involve a buffer overrun.
1500 if (GDI_ERROR == total_size || total_size > BUFFERSIZE) {
1501 // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible.
1502 // When the data is not accessable GetGlyphOutlineW fails rather quickly,
1503 // so just try to get the size. If that fails then ensure the data is accessible.
1504 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
1505 if (GDI_ERROR == total_size) {
1506 LogFontTypeface::EnsureAccessible(this->getTypeface());
1507 total_size = GetGlyphOutlineW(fDDC, glyph, flags, &gm, 0, nullptr, &fMat22);
1508 if (GDI_ERROR == total_size) {
1509 // GetGlyphOutlineW is known to fail for some characters, such as spaces.
1510 // In these cases, just return that the glyph does not have a shape.
1511 return 0;
1512 }
1513 }
1514
1515 glyphbuf->reset(total_size);
1516
1517 DWORD ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
1518 if (GDI_ERROR == ret) {
1519 LogFontTypeface::EnsureAccessible(this->getTypeface());
1520 ret = GetGlyphOutlineW(fDDC, glyph, flags, &gm, total_size, glyphbuf->get(), &fMat22);
1521 if (GDI_ERROR == ret) {
1522 SkASSERT(false);
1523 return 0;
1524 }
1525 }
1526 }
1527 return total_size;
1528}
1529
1530bool SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path) {
1531 SkASSERT(path);
1532 SkASSERT(fDDC);
1533
1534 path->reset();
1535
1536 SkGlyphID glyphID = glyph.getGlyphID();
1537
1538 // Out of all the fonts on a typical Windows box,
1539 // 25% of glyphs require more than 2KB.
1540 // 1% of glyphs require more than 4KB.
1541 // 0.01% of glyphs require more than 8KB.
1542 // 8KB is less than 1% of the normal 1MB stack on Windows.
1543 // Note that some web fonts glyphs require more than 20KB.
1544 //static const DWORD BUFFERSIZE = (1 << 13);
1545
1546 //GDI only uses hinted outlines when axis aligned.
1547 UINT format = GGO_NATIVE | GGO_GLYPH_INDEX;
1548 if (fRec.getHinting() == SkFontHinting::kNone || fRec.getHinting() == SkFontHinting::kSlight){
1549 format |= GGO_UNHINTED;
1550 }
1551 AutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE);
1552 DWORD total_size = getGDIGlyphPath(glyphID, format, &glyphbuf);
1553 if (0 == total_size) {
1554 return false;
1555 }
1556
1557 if (fRec.getHinting() != SkFontHinting::kSlight) {
1558 SkGDIGeometrySink sink(path);
1559 sink.process(glyphbuf, total_size);
1560 } else {
1561 AutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE);
1562 //GDI only uses hinted outlines when axis aligned.
1563 DWORD hinted_total_size = getGDIGlyphPath(glyphID, GGO_NATIVE | GGO_GLYPH_INDEX,
1564 &hintedGlyphbuf);
1565 if (0 == hinted_total_size) {
1566 return false;
1567 }
1568
1569 SkGDIGeometrySink sinkXBufYIter(path);
1570 if (!sinkXBufYIter.process(glyphbuf, total_size,
1571 GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)))
1572 {
1573 // Both path and sinkXBufYIter are in the state they were in at the time of failure.
1574 path->reset();
1575 SkGDIGeometrySink sink(path);
1576 sink.process(glyphbuf, total_size);
1577 }
1578 }
1579 return true;
1580}
1581
1582static void logfont_for_name(const char* familyName, LOGFONT* lf) {
1583 sk_bzero(lf, sizeof(LOGFONT));
1584#ifdef UNICODE
1585 // Get the buffer size needed first.
1586 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1587 -1, nullptr, 0);
1588 // Allocate a buffer (str_len already has terminating null
1589 // accounted for).
1590 wchar_t *wideFamilyName = new wchar_t[str_len];
1591 // Now actually convert the string.
1592 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1593 wideFamilyName, str_len);
1594 ::wcsncpy(lf->lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1595 delete [] wideFamilyName;
1596 lf->lfFaceName[LF_FACESIZE-1] = L'\0';
1597#else
1598 ::strncpy(lf->lfFaceName, familyName, LF_FACESIZE - 1);
1599 lf->lfFaceName[LF_FACESIZE - 1] = '\0';
1600#endif
1601}
1602
1603void LogFontTypeface::onGetFamilyName(SkString* familyName) const {
1604 // Get the actual name of the typeface. The logfont may not know this.
1605 SkAutoHDC hdc(fLogFont);
1606 dcfontname_to_skstring(hdc, fLogFont, familyName);
1607}
1608
1609void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
1610 bool* isLocalStream) const {
1611 SkString familyName;
1612 this->onGetFamilyName(&familyName);
1613 desc->setFamilyName(familyName.c_str());
1614 desc->setStyle(this->fontStyle());
1615 *isLocalStream = this->fSerializeAsStream;
1616}
1617
1618void LogFontTypeface::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
1619 SkAutoHDC hdc(fLogFont);
1620 unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont);
1621 populate_glyph_to_unicode(hdc, glyphCount, dstArray);
1622}
1623
1624std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const {
1625 LOGFONT lf = fLogFont;
1626 std::unique_ptr<SkAdvancedTypefaceMetrics> info(nullptr);
1627
1628 // The design HFONT must be destroyed after the HDC
1629 using HFONT_T = typename std::remove_pointer<HFONT>::type;
1630 std::unique_ptr<HFONT_T, SkFunctionObject<DeleteObject>> designFont;
1631 SkAutoHDC hdc(lf);
1632
1633 const char stem_chars[] = {'i', 'I', '!', '1'};
1634 int16_t min_width;
1635 unsigned glyphCount;
1636
1637 // To request design units, create a logical font whose height is specified
1638 // as unitsPerEm.
1639 OUTLINETEXTMETRIC otm;
1640 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1641 if (0 == otmRet) {
1642 call_ensure_accessible(lf);
1643 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1644 }
1645 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
1646 return info;
1647 }
1648 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1649 designFont.reset(CreateFontIndirect(&lf));
1650 SelectObject(hdc, designFont.get());
1651 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1652 return info;
1653 }
1654 glyphCount = calculateGlyphCount(hdc, fLogFont);
1655
1657
1658 SkOTTableOS2_V4::Type fsType;
1659 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1660 offsetof(SkOTTableOS2_V4, fsType),
1661 sizeof(fsType),
1662 &fsType)) {
1664 } else {
1665 // If bit 1 is set, the font may not be embedded in a document.
1666 // If bit 1 is clear, the font can be embedded.
1667 // If bit 2 is set, the embedding is read-only.
1668 if (otm.otmfsType & 0x1) {
1670 }
1671 }
1672
1673 if (glyphCount == 0 || (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) == 0) {
1674 return info;
1675 }
1677
1678 // If this bit is clear the font is a fixed pitch font.
1679 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1681 }
1682 if (otm.otmTextMetrics.tmItalic) {
1684 }
1685 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1687 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1689 }
1690
1691 // The main italic angle of the font, in tenths of a degree counterclockwise
1692 // from vertical.
1693 info->fItalicAngle = otm.otmItalicAngle / 10;
1694 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1695 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1696 // TODO(ctguil): Use alternate cap height calculation.
1697 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1698 // my Win7 box.
1699 info->fCapHeight = otm.otmsCapEmHeight;
1700 info->fBBox =
1701 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1702 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1703
1704 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1705 // This probably isn't very good with an italic font.
1706 min_width = SHRT_MAX;
1707 info->fStemV = 0;
1708 for (size_t i = 0; i < std::size(stem_chars); i++) {
1709 ABC abcWidths;
1710 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1711 int16_t width = abcWidths.abcB;
1712 if (width > 0 && width < min_width) {
1713 min_width = width;
1714 info->fStemV = min_width;
1715 }
1716 }
1717 }
1718
1719 return info;
1720}
1721
1722//Placeholder representation of a Base64 encoded GUID from create_unique_font_name.
1723#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
1724//Length of GUID representation from create_id, including nullptr terminator.
1725#define BASE64_GUID_ID_LEN std::size(BASE64_GUID_ID)
1726
1727static_assert(BASE64_GUID_ID_LEN < LF_FACESIZE, "GUID_longer_than_facesize");
1728
1729/**
1730 NameID 6 Postscript names cannot have the character '/'.
1731 It would be easier to hex encode the GUID, but that is 32 bytes,
1732 and many systems have issues with names longer than 28 bytes.
1733 The following need not be any standard base64 encoding.
1734 The encoded value is never decoded.
1735*/
1736static const char postscript_safe_base64_encode[] =
1737 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1738 "abcdefghijklmnopqrstuvwxyz"
1739 "0123456789-_=";
1740
1741/**
1742 Formats a GUID into Base64 and places it into buffer.
1743 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1744 The string will always be null terminated.
1745 XXXXXXXXXXXXXXXXXXXXXXXX0
1746 */
1747static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1748 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1749 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1750 SkASSERT(written < LF_FACESIZE);
1751 buffer[written] = '\0';
1752}
1753
1754/**
1755 Creates a Base64 encoded GUID and places it into buffer.
1756 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1757 The string will always be null terminated.
1758 XXXXXXXXXXXXXXXXXXXXXXXX0
1759 */
1760static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1761 GUID guid = {};
1762 if (FAILED(CoCreateGuid(&guid))) {
1763 return E_UNEXPECTED;
1764 }
1765 format_guid_b64(guid, buffer, bufferSize);
1766
1767 return S_OK;
1768}
1769
1770/**
1771 Introduces a font to GDI. On failure will return nullptr. The returned handle
1772 should eventually be passed to RemoveFontMemResourceEx.
1773*/
1774static HANDLE activate_font(SkData* fontData) {
1775 DWORD numFonts = 0;
1776 //AddFontMemResourceEx just copies the data, but does not specify const.
1777 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
1778 static_cast<DWORD>(fontData->size()),
1779 nullptr,
1780 &numFonts);
1781
1782 if (fontHandle != nullptr && numFonts < 1) {
1783 RemoveFontMemResourceEx(fontHandle);
1784 return nullptr;
1785 }
1786
1787 return fontHandle;
1788}
1789
1790// Does not affect ownership of stream.
1791static sk_sp<SkTypeface> create_from_stream(std::unique_ptr<SkStreamAsset> stream) {
1792 // Create a unique and unpredictable font name.
1793 // Avoids collisions and access from CSS.
1794 char familyName[BASE64_GUID_ID_LEN];
1795 const int familyNameSize = std::size(familyName);
1796 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
1797 return nullptr;
1798 }
1799
1800 // Change the name of the font.
1801 sk_sp<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream.get(), familyName, familyNameSize-1));
1802 if (nullptr == rewrittenFontData.get()) {
1803 return nullptr;
1804 }
1805
1806 // Register the font with GDI.
1807 HANDLE fontReference = activate_font(rewrittenFontData.get());
1808 if (nullptr == fontReference) {
1809 return nullptr;
1810 }
1811
1812 // Create the typeface.
1813 LOGFONT lf;
1814 logfont_for_name(familyName, &lf);
1815
1816 return sk_sp<SkTypeface>(SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference));
1817}
1818
1819std::unique_ptr<SkStreamAsset> LogFontTypeface::onOpenStream(int* ttcIndex) const {
1820 *ttcIndex = 0;
1821
1822 const DWORD kTTCTag = SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
1823 LOGFONT lf = fLogFont;
1824
1825 SkAutoHDC hdc(lf);
1826
1827 std::unique_ptr<SkStreamAsset> stream;
1828 DWORD tables[2] = {kTTCTag, 0};
1829 for (size_t i = 0; i < std::size(tables); i++) {
1830 DWORD bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
1831 if (bufferSize == GDI_ERROR) {
1832 call_ensure_accessible(lf);
1833 bufferSize = GetFontData(hdc, tables[i], 0, nullptr, 0);
1834 }
1835 if (bufferSize != GDI_ERROR) {
1836 sk_sp<SkData> streamData = SkData::MakeUninitialized(bufferSize);
1837 if (GetFontData(hdc, tables[i], 0, streamData->writable_data(), bufferSize)) {
1838 stream.reset(new SkMemoryStream(std::move(streamData)));
1839 break;
1840 } else {
1841 stream.reset();
1842 }
1843 }
1844 }
1845 return stream;
1846}
1847
1848sk_sp<SkTypeface> LogFontTypeface::onMakeClone(const SkFontArguments& args) const {
1849 return sk_ref_sp(this);
1850}
1851
1852static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs,
1853 bool Ox1FHack)
1854{
1855 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
1856
1857 /** Real documentation for GetGlyphIndicesW:
1858 *
1859 * When GGI_MARK_NONEXISTING_GLYPHS is not specified and a character does not map to a
1860 * glyph, then the 'default character's glyph is returned instead. The 'default character'
1861 * is available in fTM.tmDefaultChar. FON fonts have a default character, and there exists
1862 * a usDefaultChar in the 'OS/2' table, version 2 and later. If there is no
1863 * 'default character' specified by the font, then often the first character found is used.
1864 *
1865 * When GGI_MARK_NONEXISTING_GLYPHS is specified and a character does not map to a glyph,
1866 * then the glyph 0xFFFF is used. In Windows XP and earlier, Bitmap/Vector FON usually use
1867 * glyph 0x1F instead ('Terminal' appears to be special, returning 0xFFFF).
1868 * Type1 PFM/PFB, TT, OT TT, OT CFF all appear to use 0xFFFF, even on XP.
1869 */
1870 DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
1871 if (GDI_ERROR == result) {
1872 for (int i = 0; i < count; ++i) {
1873 glyphs[i] = 0;
1874 }
1875 return;
1876 }
1877
1878 if (Ox1FHack) {
1879 for (int i = 0; i < count; ++i) {
1880 if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) {
1881 glyphs[i] = 0;
1882 }
1883 }
1884 } else {
1885 for (int i = 0; i < count; ++i) {
1886 if (0xFFFF == glyphs[i]){
1887 glyphs[i] = 0;
1888 }
1889 }
1890 }
1891}
1892
1893static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) {
1894 uint16_t index = 0;
1895 // Use uniscribe to detemine glyph index for non-BMP characters.
1896 static const int numWCHAR = 2;
1897 static const int maxItems = 2;
1898 // MSDN states that this can be nullptr, but some things don't work then.
1899 SCRIPT_CONTROL scriptControl;
1900 memset(&scriptControl, 0, sizeof(scriptControl));
1901 // Add extra item to SCRIPT_ITEM to work around a bug (now documented).
1902 // https://bugzilla.mozilla.org/show_bug.cgi?id=366643
1903 SCRIPT_ITEM si[maxItems + 1];
1904 int numItems;
1905 HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, nullptr, si, &numItems),
1906 "Could not itemize character.");
1907
1908 // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs.
1909 static const int maxGlyphs = 2;
1910 SCRIPT_VISATTR vsa[maxGlyphs];
1911 WORD outGlyphs[maxGlyphs];
1912 WORD logClust[numWCHAR];
1913 int numGlyphs;
1914 SCRIPT_ANALYSIS& script = si[0].a;
1915 script.eScript = SCRIPT_UNDEFINED;
1916 script.fRTL = FALSE;
1917 script.fLayoutRTL = FALSE;
1918 script.fLinkBefore = FALSE;
1919 script.fLinkAfter = FALSE;
1920 script.fLogicalOrder = FALSE;
1921 script.fNoGlyphIndex = FALSE;
1922 script.s.uBidiLevel = 0;
1923 script.s.fOverrideDirection = 0;
1924 script.s.fInhibitSymSwap = TRUE;
1925 script.s.fCharShape = FALSE;
1926 script.s.fDigitSubstitute = FALSE;
1927 script.s.fInhibitLigate = FALSE;
1928 script.s.fDisplayZWG = TRUE;
1929 script.s.fArabicNumContext = FALSE;
1930 script.s.fGcpClusters = FALSE;
1931 script.s.fReserved = 0;
1932 script.s.fEngineReserved = 0;
1933 // For the future, 0x80040200 from here is USP_E_SCRIPT_NOT_IN_FONT
1934 HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &script,
1935 outGlyphs, logClust, vsa, &numGlyphs),
1936 "Could not shape character.");
1937 if (1 == numGlyphs) {
1938 index = outGlyphs[0];
1939 }
1940 return index;
1941}
1942
1943void LogFontTypeface::onCharsToGlyphs(const SkUnichar* uni, int glyphCount,
1944 SkGlyphID glyphs[]) const
1945{
1946 SkAutoHDC hdc(fLogFont);
1947
1948 TEXTMETRIC tm;
1949 if (0 == GetTextMetrics(hdc, &tm)) {
1950 call_ensure_accessible(fLogFont);
1951 if (0 == GetTextMetrics(hdc, &tm)) {
1952 tm.tmPitchAndFamily = TMPF_TRUETYPE;
1953 }
1954 }
1955 bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */;
1956
1957 SCRIPT_CACHE sc = nullptr;
1958 static const int scratchCount = 256;
1959 WCHAR scratch[scratchCount];
1960 int glyphIndex = 0;
1961 const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(uni);
1962 while (glyphIndex < glyphCount) {
1963 // Try a run of bmp.
1964 int glyphsLeft = std::min(glyphCount - glyphIndex, scratchCount);
1965 int runLength = 0;
1966 while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) {
1967 scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]);
1968 ++runLength;
1969 }
1970 if (runLength) {
1971 bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack);
1972 glyphIndex += runLength;
1973 }
1974
1975 // Try a run of non-bmp.
1976 while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) {
1977 SkUTF::ToUTF16(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch));
1978 glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch);
1979 ++glyphIndex;
1980 }
1981 }
1982
1983 if (sc) {
1984 ::ScriptFreeCache(&sc);
1985 }
1986}
1987
1988int LogFontTypeface::onCountGlyphs() const {
1989 SkAutoHDC hdc(fLogFont);
1990 return calculateGlyphCount(hdc, fLogFont);
1991}
1992
1993void LogFontTypeface::getPostScriptGlyphNames(SkString*) const {}
1994
1995int LogFontTypeface::onGetUPEM() const {
1996 SkAutoHDC hdc(fLogFont);
1997 return calculateUPEM(hdc, fLogFont);
1998}
1999
2000SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() const {
2003 if (!nameIter) {
2004 SkString familyName;
2005 this->getFamilyName(&familyName);
2006 SkString language("und"); //undetermined
2007 nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(familyName, language);
2008 }
2009 return nameIter.release();
2010}
2011
2012int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
2014 if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
2015 return 0;
2016 }
2017
2018 int numTables = SkEndian_SwapBE16(header.numTables);
2019
2020 if (tags) {
2021 size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry);
2023 if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) {
2024 return 0;
2025 }
2026
2027 for (int i = 0; i < numTables; ++i) {
2028 tags[i] = SkEndian_SwapBE32(dir[i].tag);
2029 }
2030 }
2031 return numTables;
2032}
2033
2034size_t LogFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
2035 size_t length, void* data) const
2036{
2037 LOGFONT lf = fLogFont;
2038 SkAutoHDC hdc(lf);
2039
2040 tag = SkEndian_SwapBE32(tag);
2041 if (nullptr == data) {
2042 length = 0;
2043 }
2044 DWORD bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
2045 if (bufferSize == GDI_ERROR) {
2046 call_ensure_accessible(lf);
2047 bufferSize = GetFontData(hdc, tag, (DWORD) offset, data, (DWORD) length);
2048 }
2049 return bufferSize == GDI_ERROR ? 0 : bufferSize;
2050}
2051
2052sk_sp<SkData> LogFontTypeface::onCopyTableData(SkFontTableTag tag) const {
2053 LOGFONT lf = fLogFont;
2054 SkAutoHDC hdc(lf);
2055
2056 tag = SkEndian_SwapBE32(tag);
2057 DWORD size = GetFontData(hdc, tag, 0, nullptr, 0);
2058 if (size == GDI_ERROR) {
2059 call_ensure_accessible(lf);
2060 size = GetFontData(hdc, tag, 0, nullptr, 0);
2061 }
2062
2064 if (size != GDI_ERROR) {
2066 if (GetFontData(hdc, tag, 0, data->writable_data(), size) == GDI_ERROR) {
2067 data.reset();
2068 }
2069 }
2070 return data;
2071}
2072
2073std::unique_ptr<SkScalerContext> LogFontTypeface::onCreateScalerContext(
2074 const SkScalerContextEffects& effects, const SkDescriptor* desc) const
2075{
2076 auto ctx = std::make_unique<SkScalerContext_GDI>(
2077 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
2078 if (ctx->isValid()) {
2079 return std::move(ctx);
2080 }
2081
2082 ctx.reset();
2084 ctx = std::make_unique<SkScalerContext_GDI>(
2085 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
2086 if (ctx->isValid()) {
2087 return std::move(ctx);
2088 }
2089
2091 sk_ref_sp(const_cast<LogFontTypeface*>(this)), effects, desc);
2092}
2093
2094void LogFontTypeface::onFilterRec(SkScalerContextRec* rec) const {
2097 {
2100 }
2101
2102 unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
2107 rec->fFlags &= ~flagsWeDontSupport;
2108
2109 SkFontHinting h = rec->getHinting();
2110 switch (h) {
2112 break;
2114 // Only do slight hinting when axis aligned.
2115 // TODO: re-enable slight hinting when FontHostTest can pass.
2116 //if (!isAxisAligned(*rec)) {
2118 //}
2119 break;
2122 // TODO: need to be able to distinguish subpixel positioned glyphs
2123 // and linear metrics.
2124 //rec->fFlags &= ~SkScalerContext::kSubpixelPositioning_Flag;
2126 break;
2127 default:
2128 SkDEBUGFAIL("unknown hinting");
2129 }
2130 //TODO: if this is a bitmap font, squash hinting and subpixel.
2131 rec->setHinting(h);
2132
2133// turn this off since GDI might turn A8 into BW! Need a bigger fix.
2134#if 0
2135 // Disable LCD when rotated, since GDI's output is ugly
2136 if (isLCD(*rec) && !isAxisAligned(*rec)) {
2138 }
2139#endif
2140
2141 if (!fCanBeLCD && isLCD(*rec)) {
2143 rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
2144 }
2145}
2146
2147///////////////////////////////////////////////////////////////////////////////
2148
2150#include "include/core/SkFontMgr.h"
2151
2152static bool valid_logfont_for_enum(const LOGFONT& lf) {
2153 // TODO: Vector FON is unsupported and should not be listed.
2154 return
2155 // Ignore implicit vertical variants.
2156 lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
2157
2158 // DEFAULT_CHARSET is used to get all fonts, but also implies all
2159 // character sets. Filter assuming all fonts support ANSI_CHARSET.
2160 && ANSI_CHARSET == lf.lfCharSet
2161 ;
2162}
2163
2164/** An EnumFontFamExProc implementation which interprets builderParam as
2165 * an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
2166 * pass the valid_logfont_for_enum predicate.
2167 */
2168static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
2169 DWORD fontType, LPARAM builderParam) {
2170 if (valid_logfont_for_enum(*lf)) {
2171 SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
2172 *array->append() = *(const ENUMLOGFONTEX*)lf;
2173 }
2174 return 1; // non-zero means continue
2175}
2176
2177class SkFontStyleSetGDI : public SkFontStyleSet {
2178public:
2179 SkFontStyleSetGDI(const TCHAR familyName[]) {
2180 LOGFONT lf;
2181 sk_bzero(&lf, sizeof(lf));
2182 lf.lfCharSet = DEFAULT_CHARSET;
2183 _tcscpy_s(lf.lfFaceName, familyName);
2184
2185 HDC hdc = ::CreateCompatibleDC(nullptr);
2186 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
2187 ::DeleteDC(hdc);
2188 }
2189
2190 int count() override {
2191 return fArray.size();
2192 }
2193
2194 void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
2195 if (fs) {
2196 *fs = get_style(fArray[index].elfLogFont);
2197 }
2198 if (styleName) {
2199 const ENUMLOGFONTEX& ref = fArray[index];
2200 // For some reason, ENUMLOGFONTEX and LOGFONT disagree on their type in the
2201 // non-unicode version.
2202 // ENUMLOGFONTEX uses BYTE
2203 // LOGFONT uses CHAR
2204 // Here we assert they that the style name is logically the same (size) as
2205 // a TCHAR, so we can use the same converter function.
2206 SkASSERT(sizeof(TCHAR) == sizeof(ref.elfStyle[0]));
2207 tchar_to_skstring((const TCHAR*)ref.elfStyle, styleName);
2208 }
2209 }
2210
2211 sk_sp<SkTypeface> createTypeface(int index) override {
2212 return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
2213 }
2214
2215 sk_sp<SkTypeface> matchStyle(const SkFontStyle& pattern) override {
2216 return this->matchStyleCSS3(pattern);
2217 }
2218
2219private:
2221};
2222
2223class SkFontMgrGDI : public SkFontMgr {
2224public:
2225 SkFontMgrGDI() {
2226 LOGFONT lf;
2227 sk_bzero(&lf, sizeof(lf));
2228 lf.lfCharSet = DEFAULT_CHARSET;
2229
2230 HDC hdc = ::CreateCompatibleDC(nullptr);
2231 ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fLogFontArray, 0);
2232 ::DeleteDC(hdc);
2233 }
2234
2235protected:
2236 int onCountFamilies() const override {
2237 return fLogFontArray.size();
2238 }
2239
2240 void onGetFamilyName(int index, SkString* familyName) const override {
2241 SkASSERT(index < fLogFontArray.size());
2242 tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
2243 }
2244
2245 sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override {
2246 SkASSERT(index < fLogFontArray.size());
2247 return sk_sp<SkFontStyleSet>(
2248 new SkFontStyleSetGDI(fLogFontArray[index].elfLogFont.lfFaceName));
2249 }
2250
2251 sk_sp<SkFontStyleSet> onMatchFamily(const char familyName[]) const override {
2252 if (nullptr == familyName) {
2253 familyName = ""; // do we need this check???
2254 }
2255 LOGFONT lf;
2256 logfont_for_name(familyName, &lf);
2257 return sk_sp<SkFontStyleSet>(new SkFontStyleSetGDI(lf.lfFaceName));
2258 }
2259
2260 sk_sp<SkTypeface> onMatchFamilyStyle(const char familyName[],
2261 const SkFontStyle& fontstyle) const override {
2262 // could be in base impl
2263 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
2264 return sset->matchStyle(fontstyle);
2265 }
2266
2267 sk_sp<SkTypeface> onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2268 const char* bcp47[], int bcp47Count,
2269 SkUnichar character) const override {
2270 return nullptr;
2271 }
2272
2273 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2274 int ttcIndex) const override {
2275 if (ttcIndex != 0) {
2276 return nullptr;
2277 }
2278 return create_from_stream(std::move(stream));
2279 }
2280
2281 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
2282 const SkFontArguments& args) const override {
2283 return this->makeFromStream(std::move(stream), args.getCollectionIndex());
2284 }
2285
2286 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
2287 // could be in base impl
2288 return this->makeFromStream(std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))),
2289 ttcIndex);
2290 }
2291
2292 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
2293 // could be in base impl
2294 auto stream = SkStream::MakeFromFile(path);
2295 return stream ? this->makeFromStream(std::move(stream), ttcIndex) : nullptr;
2296 }
2297
2298 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
2299 LOGFONT lf;
2300 if (nullptr == familyName) {
2301 lf = get_default_font();
2302 } else {
2303 logfont_for_name(familyName, &lf);
2304 }
2305
2306 lf.lfWeight = style.weight();
2307 lf.lfItalic = style.slant() == SkFontStyle::kUpright_Slant ? FALSE : TRUE;
2308 return sk_sp<SkTypeface>(SkCreateTypefaceFromLOGFONT(lf));
2309 }
2310
2311private:
2312 SkTDArray<ENUMLOGFONTEX> fLogFontArray;
2313};
2314
2315///////////////////////////////////////////////////////////////////////////////
2316
2317sk_sp<SkFontMgr> SkFontMgr_New_GDI() { return sk_make_sp<SkFontMgrGDI>(); }
2318
2319#endif//defined(SK_BUILD_FOR_WIN)
SkPath fPath
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
uint16_t glyphs[5]
int count
SkColor4f color
static float next(float f)
static float prev(float f)
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
static size_t total_size(SkSBlockAllocator< N > &pool)
unsigned U8CPU
Definition SkCPUTypes.h:18
static U8CPU SkComputeLuminance(U8CPU r, U8CPU g, U8CPU b)
static U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
#define SkEndian_SwapBE32(n)
Definition SkEndian.h:136
#define SkEndian_SwapBE16(n)
Definition SkEndian.h:135
#define SkTEndian_SwapBE32(n)
Definition SkEndian.h:143
#define SK_RESTRICT
Definition SkFeatures.h:42
int32_t SkFixed
Definition SkFixed.h:25
#define SkScalarToFixed(x)
Definition SkFixed.h:125
#define SkFixedToScalar(x)
Definition SkFixed.h:124
#define SkFixedAve(a, b)
Definition SkFixed.h:90
#define SkFixedToFloat(x)
Definition SkFixed.h:41
#define SkFloatToFixed(x)
Definition SkFixed.h:42
static bool isLCD(const SkScalerContextRec &rec)
static bool bothZero(SkScalar a, SkScalar b)
static bool isAxisAligned(const SkScalerContextRec &rec)
SkFontHinting
Definition SkFontTypes.h:18
@ kNormal
glyph outlines modified to improve constrast
@ kNone
glyph outlines unchanged
@ kSlight
minimal modification to improve constrast
@ kFull
modifies glyph outlines for maximum constrast
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
static bool left(const SkPoint &p0, const SkPoint &p1)
#define INHERITED(method,...)
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
#define SkScalarInvert(x)
Definition SkScalar.h:73
#define SkScalarTruncToInt(x)
Definition SkScalar.h:59
#define SkScalarToFloat(x)
Definition SkScalar.h:61
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SkScalarRoundToScalar(x)
Definition SkScalar.h:32
D * SkTAddOffset(S *ptr, ptrdiff_t byteOffset)
Definition SkTemplates.h:60
constexpr int32_t SkToS32(S x)
Definition SkTo.h:25
constexpr uint8_t SkToU8(S x)
Definition SkTo.h:22
constexpr int16_t SkToS16(S x)
Definition SkTo.h:23
uint32_t SkFontTableTag
Definition SkTypeface.h:41
int32_t SkUnichar
Definition SkTypes.h:175
uint16_t SkGlyphID
Definition SkTypes.h:179
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
SI F table(const skcms_Curve *curve, F v)
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition SkData.cpp:116
const void * data() const
Definition SkData.h:37
size_t size() const
Definition SkData.h:30
virtual void onGetFamilyName(int index, SkString *familyName) const =0
sk_sp< SkTypeface > makeFromStream(std::unique_ptr< SkStreamAsset >, int ttcIndex=0) const
sk_sp< SkFontStyleSet > matchFamily(const char familyName[]) const
virtual sk_sp< SkTypeface > onMakeFromData(sk_sp< SkData >, int ttcIndex) const =0
virtual sk_sp< SkFontStyleSet > onCreateStyleSet(int index) const =0
virtual sk_sp< SkTypeface > onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle &, const char *bcp47[], int bcp47Count, SkUnichar character) const =0
virtual sk_sp< SkFontStyleSet > onMatchFamily(const char familyName[]) const =0
virtual sk_sp< SkTypeface > onMakeFromStreamIndex(std::unique_ptr< SkStreamAsset >, int ttcIndex) const =0
virtual sk_sp< SkTypeface > onMakeFromStreamArgs(std::unique_ptr< SkStreamAsset >, const SkFontArguments &) const =0
virtual sk_sp< SkTypeface > onMatchFamilyStyle(const char familyName[], const SkFontStyle &) const =0
virtual sk_sp< SkTypeface > onLegacyMakeTypeface(const char familyName[], SkFontStyle) const =0
virtual sk_sp< SkTypeface > onMakeFromFile(const char path[], int ttcIndex) const =0
virtual int onCountFamilies() const =0
sk_sp< SkTypeface > matchStyleCSS3(const SkFontStyle &pattern)
virtual sk_sp< SkTypeface > createTypeface(int index)=0
virtual sk_sp< SkTypeface > matchStyle(const SkFontStyle &pattern)=0
virtual int count()=0
virtual void getStyle(int index, SkFontStyle *, SkString *style)=0
Slant slant() const
Definition SkFontStyle.h:64
int width() const
Definition SkFontStyle.h:63
int weight() const
Definition SkFontStyle.h:62
int top() const
Definition SkGlyph.h:511
size_t rowBytes() const
Definition SkGlyph.cpp:233
SkGlyphID getGlyphID() const
Definition SkGlyph.h:429
size_t imageSize() const
Definition SkGlyph.cpp:241
SkMask::Format maskFormat() const
Definition SkGlyph.h:500
int height() const
Definition SkGlyph.h:513
int width() const
Definition SkGlyph.h:512
int left() const
Definition SkGlyph.h:510
static constexpr int kMScaleX
horizontal scale factor
Definition SkMatrix.h:353
static constexpr int kMSkewY
vertical skew factor
Definition SkMatrix.h:356
SkScalar get(int index) const
Definition SkMatrix.h:392
static constexpr int kMScaleY
vertical scale factor
Definition SkMatrix.h:357
static constexpr int kMSkewX
horizontal skew factor
Definition SkMatrix.h:354
static sk_sp< LocalizedStrings_NameTable > MakeForFamilyNames(const SkTypeface &typeface)
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition SkPath.cpp:736
SkPath & close()
Definition SkPath.cpp:813
virtual void generateFontMetrics(SkFontMetrics *)=0
virtual void generateImage(const SkGlyph &glyph, void *imageBuffer)=0
virtual GlyphMetrics generateMetrics(const SkGlyph &, SkArenaAlloc *)=0
virtual bool generatePath(const SkGlyph &, SkPath *)=0
static std::unique_ptr< SkScalerContext > MakeEmpty(sk_sp< SkTypeface > typeface, const SkScalerContextEffects &effects, const SkDescriptor *desc)
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
Definition SkStream.cpp:922
static void PurgeAll()
const char * c_str() const
Definition SkString.h:133
T * append()
Definition SkTDArray.h:191
static sk_sp< SkTypeface > FindByProcAndRef(FindProc proc, void *ctx)
static void Add(sk_sp< SkTypeface >)
virtual sk_sp< SkData > onCopyTableData(SkFontTableTag) const
virtual void getPostScriptGlyphNames(SkString *) const =0
virtual std::unique_ptr< SkAdvancedTypefaceMetrics > onGetAdvancedMetrics() const =0
void setFontStyle(SkFontStyle style)
Definition SkTypeface.h:349
virtual void getGlyphToUnicodeMap(SkUnichar *dstArray) const =0
void setIsFixedPitch(bool isFixedPitch)
Definition SkTypeface.h:347
virtual std::unique_ptr< SkScalerContext > onCreateScalerContext(const SkScalerContextEffects &, const SkDescriptor *) const =0
virtual int onCountGlyphs() const =0
virtual int onGetTableTags(SkFontTableTag tags[]) const =0
virtual std::unique_ptr< SkStreamAsset > onOpenStream(int *ttcIndex) const =0
virtual void onGetFontDescriptor(SkFontDescriptor *, bool *isLocal) const =0
virtual sk_sp< SkTypeface > onMakeClone(const SkFontArguments &) const =0
virtual size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void *data) const =0
virtual bool onGetPostScriptName(SkString *) const =0
virtual void onGetFamilyName(SkString *familyName) const =0
virtual void onFilterRec(SkScalerContextRec *) const =0
virtual bool onGlyphMaskNeedsCurrentColor() const =0
virtual LocalizedStrings * onCreateFamilyNameIterator() const =0
virtual int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], int parameterCount) const =0
virtual void onCharsToGlyphs(const SkUnichar *chars, int count, SkGlyphID glyphs[]) const =0
virtual int onGetUPEM() const =0
virtual int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const =0
T * release()
Definition SkRefCnt.h:324
T * reset(size_t count)
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
EMSCRIPTEN_KEEPALIVE void empty()
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
GAsyncResult * result
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition hsl.cpp:49
static float min(float r, float g, float b)
Definition hsl.cpp:48
size_t length
return FALSE
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
SK_SPI size_t ToUTF16(SkUnichar uni, uint16_t utf16[2]=nullptr)
Definition SkUTF.cpp:243
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition switches.h:145
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition switches.h:76
font
Font Metadata and Metrics.
dst
Definition cp.py:12
Definition ref_ptr.h:256
init(device_serial, adb_binary)
Definition _adb_path.py:12
SkScalar h
#define SIZE
int32_t height
int32_t width
static const char header[]
Definition skpbench.cpp:88
static void process(const char *inPath, const char *lexer, const char *token, const char *hPath, const char *cppPath)
Definition Main.cpp:114
const Scalar scale
Point offset
@ kNotEmbeddable_FontFlag
May not be embedded.
static size_t Encode(const void *src, size_t length, void *dst, const char *encode=nullptr)
Definition SkBase64.cpp:113
SkScalar fTop
greatest extent above origin of any glyph bounding box, typically negative; deprecated with variable ...
SkScalar fLeading
distance to add between lines, typically positive or zero
SkScalar fAvgCharWidth
average character width, zero if unknown
SkScalar fMaxCharWidth
maximum character width, zero if unknown
SkScalar fBottom
greatest extent below origin of any glyph bounding box, typically positive; deprecated with variable ...
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fXHeight
height of lower-case 'x', zero if unknown, typically negative
SkScalar fUnderlineThickness
underline thickness
@ kUnderlinePositionIsValid_Flag
set if fUnderlinePosition is valid
@ kUnderlineThicknessIsValid_Flag
set if fUnderlineThickness is valid
SkScalar fDescent
distance to reserve below baseline, typically positive
SkScalar fXMin
greatest extent to left of origin of any glyph bounding box, typically negative; deprecated with vari...
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
SkScalar fXMax
greatest extent to right of origin of any glyph bounding box, typically positive; deprecated with var...
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition SkRect.h:91
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition SkMask.h:31
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27
static const SK_OT_ULONG TAG
static constexpr SK_OT_ULONG TAG
static SkData * RenameFont(SkStreamAsset *fontData, const char *fontName, int fontNameLen)
Definition SkOTUtils.cpp:31
static void SetAdvancedTypefaceFlags(SkOTTableOS2_V4::Type fsType, SkAdvancedTypefaceMetrics *info)
SkRect makeOutset(float dx, float dy) const
Definition SkRect.h:1002
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
SkMask::Format fMaskFormat
SkScalar fPost2x2[2][2]
SkFontHinting getHinting() const
void setHinting(SkFontHinting)
int BOOL
LOGFONTW LOGFONT
unsigned short WORD
unsigned int UINT
LONG_PTR LPARAM
unsigned char BYTE
void * HANDLE
#define FAILED(hr)
unsigned long DWORD
#define CALLBACK
struct _GUID GUID