blob: 62b0a0c358adcf110cc04a3595f69f51f62e3928 [file] [log] [blame]
reed@google.comac6b9792011-03-11 15:42:51 +00001/*
2 ** Copyright 2006, The Android Open Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16
17#include "SkString.h"
18//#include "SkStream.h"
19
20#include "SkEndian.h"
21#include "SkFontHost.h"
22#include "SkDescriptor.h"
23#include "SkAdvancedTypefaceMetrics.h"
24#include "SkStream.h"
25#include "SkThread.h"
26#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000027#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000028#include "SkUtils.h"
29
30#ifdef WIN32
31#include "windows.h"
32#include "tchar.h"
33#include "Usp10.h"
34
35// client3d has to undefine this for now
36#define CAN_USE_LOGFONT_NAME
37
38using namespace skia_advanced_typeface_metrics_utils;
39
reed@google.comac6b9792011-03-11 15:42:51 +000040static const uint16_t BUFFERSIZE = (16384 - 32);
41static uint8_t glyphbuf[BUFFERSIZE];
42
43// Give 1MB font cache budget
44#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
45
46/**
47 * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
48 * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
49 * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
50 * actual requested size.
51 */
52static const int gCanonicalTextSize = 64;
53
54static void make_canonical(LOGFONT* lf) {
55 lf->lfHeight = -gCanonicalTextSize;
reed@google.com59d2f632011-05-02 19:36:59 +000056 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
57 lf->lfCharSet = DEFAULT_CHARSET;
58}
59
60static SkTypeface::Style getStyle(const LOGFONT& lf) {
61 unsigned style = 0;
62 if (lf.lfWeight >= FW_BOLD) {
63 style |= SkTypeface::kBold;
64 }
65 if (lf.lfItalic) {
66 style |= SkTypeface::kItalic;
67 }
68 return (SkTypeface::Style)style;
69}
70
71static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
72 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
73 lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
reed@google.comac6b9792011-03-11 15:42:51 +000074}
75
76static inline FIXED SkFixedToFIXED(SkFixed x) {
77 return *(FIXED*)(&x);
78}
79
80static inline FIXED SkScalarToFIXED(SkScalar x) {
81 return SkFixedToFIXED(SkScalarToFixed(x));
82}
83
84static unsigned calculateGlyphCount(HDC hdc) {
85 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +000086 const DWORD maxpTag =
87 SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
reed@google.comac6b9792011-03-11 15:42:51 +000088 uint16_t glyphs;
89 if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
90 return SkEndian_SwapBE16(glyphs);
91 }
92
93 // Binary search for glyph count.
94 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
95 int32_t max = SK_MaxU16 + 1;
96 int32_t min = 0;
97 GLYPHMETRICS gm;
98 while (min < max) {
99 int32_t mid = min + ((max - min) / 2);
100 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
101 NULL, &mat2) == GDI_ERROR) {
102 max = mid;
103 } else {
104 min = mid + 1;
105 }
106 }
107 SkASSERT(min == max);
108 return min;
109}
110
111static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
112 int style = SkTypeface::kNormal;
113 if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
114 style |= SkTypeface::kBold;
115 if (lf.lfItalic)
116 style |= SkTypeface::kItalic;
117
118 return (SkTypeface::Style)style;
119}
120
reed@google.comac6b9792011-03-11 15:42:51 +0000121class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000122public:
reed@google.com59d2f632011-05-02 19:36:59 +0000123 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
124 SkTypeface(style, fontID, false), fLogFont(lf) {}
reed@google.comac6b9792011-03-11 15:42:51 +0000125
reed@google.com59d2f632011-05-02 19:36:59 +0000126 LOGFONT fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000127
reed@google.com59d2f632011-05-02 19:36:59 +0000128 static LogFontTypeface* Create(const LOGFONT& lf) {
129 SkTypeface::Style style = GetFontStyle(lf);
130 SkFontID fontID = SkTypefaceCache::NewFontID();
131 return new LogFontTypeface(style, fontID, lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000132 }
133};
134
reed@google.comac6b9792011-03-11 15:42:51 +0000135static const LOGFONT& get_default_font() {
136 static LOGFONT gDefaultFont;
137 // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
138 // and the user could change too
139
140
141// lfMessageFont is garbage on my XP, so skip for now
142#if 0
143 if (gDefaultFont.lfFaceName[0] != 0) {
144 return gDefaultFont;
145 }
146
147 NONCLIENTMETRICS ncm;
148 ncm.cbSize = sizeof(NONCLIENTMETRICS);
149 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
150
151 //memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
152#endif
153
154 return gDefaultFont;
155}
156
reed@google.com59d2f632011-05-02 19:36:59 +0000157static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
158 LogFontTypeface* lface = reinterpret_cast<LogFontTypeface*>(face);
159 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000160
reed@google.com59d2f632011-05-02 19:36:59 +0000161 return getStyle(lface->fLogFont) == requestedStyle &&
162 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
163}
164
165/**
166 * This guy is public. It first searches the cache, and if a match is not found,
167 * it creates a new face.
168 */
169SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
170 LOGFONT lf = origLF;
171 make_canonical(&lf);
172 SkTypeface* face = SkTypefaceCache::FindByProc(FindByLogFont, &lf);
173 if (face) {
174 face->ref();
reed@google.comac6b9792011-03-11 15:42:51 +0000175 } else {
reed@google.com59d2f632011-05-02 19:36:59 +0000176 face = LogFontTypeface::Create(lf);
177 SkTypefaceCache::Add(face, getStyle(lf));
178 }
179 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000180}
181
reed@google.com7d26c592011-06-13 13:01:10 +0000182SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@google.comac6b9792011-03-11 15:42:51 +0000183 // Zero means that we don't have any fallback fonts for this fontID.
184 // This function is implemented on Android, but doesn't have much
185 // meaning here.
186 return 0;
187}
188
reed@google.com59d2f632011-05-02 19:36:59 +0000189static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
190 LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
191 if (face) {
192 *lf = face->fLogFont;
193 } else {
194 sk_bzero(lf, sizeof(LOGFONT));
195 }
196}
197
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000198// Construct Glyph to Unicode table.
199// Unicode code points that require conjugate pairs in utf16 are not
200// supported.
201// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
202// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
203// of calling GetFontUnicodeRange().
204static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
205 SkTDArray<SkUnichar>* glyphToUnicode) {
206 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
207 if (!glyphSetBufferSize) {
208 return;
209 }
210
211 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
212 GLYPHSET* glyphSet =
213 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
214 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
215 return;
216 }
217
218 glyphToUnicode->setCount(glyphCount);
219 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
220 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
221 // There is no guarantee that within a Unicode range, the corresponding
222 // glyph id in a font file are continuous. So, even if we have ranges,
223 // we can't just use the first and last entry of the range to compute
224 // result. We need to enumerate them one by one.
225 int count = glyphSet->ranges[i].cGlyphs;
226 SkAutoTArray<WCHAR> chars(count + 1);
227 chars[count] = 0; // termintate string
228 SkAutoTArray<WORD> glyph(count);
229 for (USHORT j = 0; j < count; ++j) {
230 chars[j] = glyphSet->ranges[i].wcLow + j;
231 }
232 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
233 GGI_MARK_NONEXISTING_GLYPHS);
234 // If the glyph ID is valid, and the glyph is not mapped, then we will
235 // fill in the char id into the vector. If the glyph is mapped already,
236 // skip it.
237 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
238 // font cache, then generate this mapping table from there. It's
239 // unlikely to have collisions since glyph reuse happens mostly for
240 // different Unicode pages.
241 for (USHORT j = 0; j < count; ++j) {
242 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
243 (*glyphToUnicode)[glyph[j]] == 0) {
244 (*glyphToUnicode)[glyph[j]] = chars[j];
245 }
246 }
247 }
248}
249
reed@google.com59d2f632011-05-02 19:36:59 +0000250//////////////////////////////////////////////////////////////////////////////////////////////
251
reed@google.comac6b9792011-03-11 15:42:51 +0000252class SkScalerContext_Windows : public SkScalerContext {
253public:
254 SkScalerContext_Windows(const SkDescriptor* desc);
255 virtual ~SkScalerContext_Windows();
256
257protected:
258 virtual unsigned generateGlyphCount();
259 virtual uint16_t generateCharToGlyph(SkUnichar uni);
260 virtual void generateAdvance(SkGlyph* glyph);
261 virtual void generateMetrics(SkGlyph* glyph);
262 virtual void generateImage(const SkGlyph& glyph);
263 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
264 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
265 //virtual SkDeviceContext getDC() {return ddc;}
266private:
267 SkScalar fScale; // to get from canonical size to real size
268 MAT2 fMat22;
269 XFORM fXform;
270 HDC fDDC;
271 HFONT fSavefont;
272 HFONT fFont;
273 SCRIPT_CACHE fSC;
274 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000275
276 HFONT fHiResFont;
277 MAT2 fMat22Identity;
278 SkMatrix fHiResMatrix;
reed@google.comac6b9792011-03-11 15:42:51 +0000279};
280
281static float mul2float(SkScalar a, SkScalar b) {
282 return SkScalarToFloat(SkScalarMul(a, b));
283}
284
285static FIXED float2FIXED(float x) {
286 return SkFixedToFIXED(SkFloatToFixed(x));
287}
288
reed@google.com59d2f632011-05-02 19:36:59 +0000289static SkMutex gFTMutex;
290
reed@google.com1dd17a12011-05-17 14:04:41 +0000291#define HIRES_TEXTSIZE 2048
292#define HIRES_SHIFT 11
293static inline SkFixed HiResToFixed(int value) {
294 return value << (16 - HIRES_SHIFT);
295}
296
297static bool needHiResMetrics(const SkScalar mat[2][2]) {
298 return mat[1][0] || mat[0][1];
299}
300
reed@google.comac6b9792011-03-11 15:42:51 +0000301SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
302 : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
303 , fGlyphCount(-1) {
304 SkAutoMutexAcquire ac(gFTMutex);
305
306 fScale = fRec.fTextSize / gCanonicalTextSize;
307
308 fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
309 fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
310 fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
311 fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
312 fXform.eDx = 0;
313 fXform.eDy = 0;
314
315 fMat22.eM11 = float2FIXED(fXform.eM11);
316 fMat22.eM12 = float2FIXED(fXform.eM12);
317 fMat22.eM21 = float2FIXED(-fXform.eM21);
318 fMat22.eM22 = float2FIXED(-fXform.eM22);
319
320 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com1dd17a12011-05-17 14:04:41 +0000321 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000322 SetBkMode(fDDC, TRANSPARENT);
323
324 // Scaling by the DPI is inconsistent with how Skia draws elsewhere
325 //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
reed@google.com59d2f632011-05-02 19:36:59 +0000326 LOGFONT lf;
327 GetLogFontByID(fRec.fFontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000328 lf.lfHeight = -gCanonicalTextSize;
329 fFont = CreateFontIndirect(&lf);
reed@google.com1dd17a12011-05-17 14:04:41 +0000330
331 // if we're rotated, or want fractional widths, create a hires font
332 fHiResFont = 0;
333 if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) {
334 lf.lfHeight = -HIRES_TEXTSIZE;
335 fHiResFont = CreateFontIndirect(&lf);
336
337 fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
338 fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
339
340 // construct a matrix to go from HIRES logical units to our device units
341 fRec.getSingleMatrix(&fHiResMatrix);
342 SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
343 fHiResMatrix.preScale(scale, scale);
344 }
reed@google.comac6b9792011-03-11 15:42:51 +0000345 fSavefont = (HFONT)SelectObject(fDDC, fFont);
346}
347
348SkScalerContext_Windows::~SkScalerContext_Windows() {
349 if (fDDC) {
350 ::SelectObject(fDDC, fSavefont);
351 ::DeleteDC(fDDC);
352 }
353 if (fFont) {
354 ::DeleteObject(fFont);
355 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000356 if (fHiResFont) {
357 ::DeleteObject(fHiResFont);
358 }
reed@google.comac6b9792011-03-11 15:42:51 +0000359 if (fSC) {
360 ::ScriptFreeCache(&fSC);
361 }
362}
363
364unsigned SkScalerContext_Windows::generateGlyphCount() {
365 if (fGlyphCount < 0) {
366 fGlyphCount = calculateGlyphCount(fDDC);
367 }
368 return fGlyphCount;
369}
370
371uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
372 uint16_t index = 0;
373 WCHAR c[2];
374 // TODO(ctguil): Support characters that generate more than one glyph.
375 if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
376 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
377 SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
378 } else {
379 // Use uniscribe to detemine glyph index for non-BMP characters.
380 // Need to add extra item to SCRIPT_ITEM to work around a bug in older
381 // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
382 SCRIPT_ITEM si[2 + 1];
383 int items;
384 SkAssertResult(
385 SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
386
387 WORD log[2];
388 SCRIPT_VISATTR vsa;
389 int glyphs;
390 SkAssertResult(SUCCEEDED(ScriptShape(
391 fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
392 }
393 return index;
394}
395
396void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
397 this->generateMetrics(glyph);
398}
399
400void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
401
402 SkASSERT(fDDC);
403
404 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000405 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000406
407 glyph->fRsbDelta = 0;
408 glyph->fLsbDelta = 0;
409
410 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
411 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
412 uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
413
414 if (GDI_ERROR != ret) {
415 if (ret == 0) {
416 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
417 gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
418 }
419 glyph->fWidth = gm.gmBlackBoxX;
420 glyph->fHeight = gm.gmBlackBoxY;
421 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
422 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
423 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
424 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
425
426 // we outset by 1 in all dimensions, since the lcd image may bleed outside
427 // of the computed bounds returned by GetGlyphOutline.
428 // This was deduced by trial and error for small text (e.g. 8pt), so there
429 // maybe a more precise way to make this adjustment...
430 if (SkMask::kLCD16_Format == fRec.fMaskFormat) {
431 glyph->fWidth += 2;
432 glyph->fHeight += 2;
433 glyph->fTop -= 1;
434 glyph->fLeft -= 1;
435 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000436
437 if (fHiResFont) {
438 SelectObject(fDDC, fHiResFont);
439 sk_bzero(&gm, sizeof(gm));
440 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
441 if (GDI_ERROR != ret) {
442 SkPoint advance;
443 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
444 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
445 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
446 }
447 SelectObject(fDDC, fFont);
448 }
reed@google.comac6b9792011-03-11 15:42:51 +0000449 } else {
450 glyph->fWidth = 0;
451 }
452}
453
454void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
455// Note: This code was borrowed from generateLineHeight, which has a note
456// stating that it may be incorrect.
457 if (!(mx || my))
458 return;
459
460 SkASSERT(fDDC);
461
462 OUTLINETEXTMETRIC otm;
463
464 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
465 if (sizeof(otm) != ret) {
466 return;
467 }
468
469 if (mx) {
470 mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
471 mx->fAscent = -fScale * otm.otmAscent;
472 mx->fDescent = -fScale * otm.otmDescent;
473 mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
474 mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
475 + otm.otmTextMetrics.tmExternalLeading);
476 }
477
478 if (my) {
479 my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
480 my->fAscent = -fScale * otm.otmAscent;
481 my->fDescent = -fScale * otm.otmDescent;
482 my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
483 my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
484 + otm.otmTextMetrics.tmExternalLeading);
485 }
486}
487
488#include "SkColorPriv.h"
489
490static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
491 int r = (rgb >> 16) & 0xFF;
492 int g = (rgb >> 8) & 0xFF;
493 int b = (rgb >> 0) & 0xFF;
494
495 // invert, since we draw black-on-white, but we want the original
496 // src mask values.
497 r = 255 - r;
498 g = 255 - g;
499 b = 255 - b;
500 return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
501}
502
reed@google.com62711172011-05-18 15:08:10 +0000503static int alignTo32(int n) {
504 return (n + 31) & ~31;
505}
506
507struct MyBitmapInfo : public BITMAPINFO {
508 RGBQUAD fMoreSpaceForColors[1];
509};
510
reed@google.comac6b9792011-03-11 15:42:51 +0000511void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
512
513 SkAutoMutexAcquire ac(gFTMutex);
514
515 SkASSERT(fDDC);
516
reed@google.com62711172011-05-18 15:08:10 +0000517 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
518 if ((SkMask::kLCD16_Format == fRec.fMaskFormat) || isBW) {
reed@google.comac6b9792011-03-11 15:42:51 +0000519 HDC dc = CreateCompatibleDC(0);
520 void* bits = 0;
reed@google.com62711172011-05-18 15:08:10 +0000521 int biWidth = isBW ? alignTo32(glyph.fWidth) : glyph.fWidth;
522 MyBitmapInfo info;
reed@google.comac6b9792011-03-11 15:42:51 +0000523 sk_bzero(&info, sizeof(info));
reed@google.com62711172011-05-18 15:08:10 +0000524 if (isBW) {
525 RGBQUAD blackQuad = { 0, 0, 0, 0 };
526 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
527 info.bmiColors[0] = blackQuad;
528 info.bmiColors[1] = whiteQuad;
529 }
reed@google.comac6b9792011-03-11 15:42:51 +0000530 info.bmiHeader.biSize = sizeof(info.bmiHeader);
reed@google.com62711172011-05-18 15:08:10 +0000531 info.bmiHeader.biWidth = biWidth;
reed@google.comac6b9792011-03-11 15:42:51 +0000532 info.bmiHeader.biHeight = glyph.fHeight;
533 info.bmiHeader.biPlanes = 1;
reed@google.com62711172011-05-18 15:08:10 +0000534 info.bmiHeader.biBitCount = isBW ? 1 : 32;
reed@google.comac6b9792011-03-11 15:42:51 +0000535 info.bmiHeader.biCompression = BI_RGB;
reed@google.com62711172011-05-18 15:08:10 +0000536 if (isBW) {
537 info.bmiHeader.biClrUsed = 2;
538 }
reed@google.comac6b9792011-03-11 15:42:51 +0000539 HBITMAP bm = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, 0, 0);
540 SelectObject(dc, bm);
541
542 // erase to white
reed@google.com62711172011-05-18 15:08:10 +0000543 size_t srcRB = isBW ? (biWidth >> 3) : (glyph.fWidth << 2);
reed@google.comac6b9792011-03-11 15:42:51 +0000544 size_t size = glyph.fHeight * srcRB;
reed@google.com62711172011-05-18 15:08:10 +0000545 memset(bits, isBW ? 0 : 0xFF, size);
reed@google.comac6b9792011-03-11 15:42:51 +0000546
reed@google.com1dd17a12011-05-17 14:04:41 +0000547 SetGraphicsMode(dc, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000548 SetBkMode(dc, TRANSPARENT);
549 SetTextAlign(dc, TA_LEFT | TA_BASELINE);
reed@google.comac6b9792011-03-11 15:42:51 +0000550
551 XFORM xform = fXform;
552 xform.eDx = (float)-glyph.fLeft;
553 xform.eDy = (float)-glyph.fTop;
554 SetWorldTransform(dc, &xform);
555
556 HGDIOBJ prevFont = SelectObject(dc, fFont);
reed@google.com62711172011-05-18 15:08:10 +0000557 COLORREF color = SetTextColor(dc, isBW ? 0xFFFFFF : 0);
reed@google.comac6b9792011-03-11 15:42:51 +0000558 SkASSERT(color != CLR_INVALID);
559 uint16_t glyphID = glyph.getGlyphID();
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000560#if defined(UNICODE)
reed@google.comac6b9792011-03-11 15:42:51 +0000561 ExtTextOut(dc, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCWSTR)&glyphID, 1, NULL);
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000562#else
563 ExtTextOut(dc, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCSTR)&glyphID, 1, NULL);
564#endif
reed@google.comac6b9792011-03-11 15:42:51 +0000565 GdiFlush();
566
567 // downsample from rgba to rgb565
568 int width = glyph.fWidth;
569 size_t dstRB = glyph.rowBytes();
reed@google.com62711172011-05-18 15:08:10 +0000570 if (isBW) {
571 const uint8_t* src = (const uint8_t*)bits;
572 // gdi's bitmap is upside-down, so we reverse dst walking in Y
573 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
574 for (int y = 0; y < glyph.fHeight; y++) {
575 memcpy(dst, src, dstRB);
576 src += srcRB;
577 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +0000578 }
reed@google.com62711172011-05-18 15:08:10 +0000579 } else { // LCD16
580 const uint32_t* src = (const uint32_t*)bits;
581 // gdi's bitmap is upside-down, so we reverse dst walking in Y
582 uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
583 for (int y = 0; y < glyph.fHeight; y++) {
584 for (int i = 0; i < width; i++) {
585 dst[i] = rgb_to_lcd16(src[i]);
586 }
587 src = (const uint32_t*)((const char*)src + srcRB);
588 dst = (uint16_t*)((char*)dst - dstRB);
589 }
reed@google.comac6b9792011-03-11 15:42:51 +0000590 }
591
592 DeleteDC(dc);
593 DeleteObject(bm);
594 return;
595 }
596
597 GLYPHMETRICS gm;
598 memset(&gm, 0, sizeof(gm));
599 uint32_t bytecount = 0;
600 uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
601 if (GDI_ERROR != total_size && total_size > 0) {
602 uint8_t *pBuff = new uint8_t[total_size];
603 if (NULL != pBuff) {
604 total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, total_size, pBuff, &fMat22);
605
606 SkASSERT(total_size != GDI_ERROR);
607
608 SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
609 SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
610
611 uint8_t* dst = (uint8_t*)glyph.fImage;
612 uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
613 if (pitch != glyph.rowBytes()) {
614 SkASSERT(false); // glyph.fImage has different rowsize!?
615 }
616
617 for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
618 uint8_t* src = pBuff + pitch * y;
619
620 for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
621 if (*src > 63) {
622 *dst = 0xFF;
623 }
624 else {
625 *dst = *src << 2; // scale to 0-255
626 }
627 dst++;
628 src++;
629 bytecount++;
630 }
631 memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
632 dst += glyph.rowBytes() - glyph.fWidth;
633 }
634
635 delete[] pBuff;
636 }
637 }
638
639 SkASSERT(GDI_ERROR != total_size && total_size >= 0);
640
641}
642
643void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
644
645 SkAutoMutexAcquire ac(gFTMutex);
646
647 SkASSERT(&glyph && path);
648 SkASSERT(fDDC);
649
650 path->reset();
651
652#if 0
653 char buf[1024];
654 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
655 OutputDebugString(buf);
656#endif
657
658 GLYPHMETRICS gm;
659 uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
660
661 if (GDI_ERROR != total_size) {
662
663 const uint8_t* cur_glyph = glyphbuf;
664 const uint8_t* end_glyph = glyphbuf + total_size;
665
666 while(cur_glyph < end_glyph) {
667 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
668
669 const uint8_t* end_poly = cur_glyph + th->cb;
670 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
671
672 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
673
674 while(cur_poly < end_poly) {
675 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
676
677 if (pc->wType == TT_PRIM_LINE) {
678 for (uint16_t i = 0; i < pc->cpfx; i++) {
679 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
680 }
681 }
682
683 if (pc->wType == TT_PRIM_QSPLINE) {
684 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
685 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
686 POINTFX pnt_c = pc->apfx[u+1];
687
688 if (u < pc->cpfx - 2) { // If not on last spline, compute C
689 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
690 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
691 }
692
693 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
694 }
695 }
696 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
697 }
698 cur_glyph += th->cb;
699 path->close();
700 }
701 }
702 else {
703 SkASSERT(false);
704 }
705 //char buf[1024];
706 //sprintf(buf, "generatePath: count:%d\n", count);
707 //OutputDebugString(buf);
708}
709
710void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
711 SkASSERT(!"SkFontHost::Serialize unimplemented");
712}
713
714SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
715 SkASSERT(!"SkFontHost::Deserialize unimplemented");
716 return NULL;
717}
718
719static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
720 // Initialize the MAT2 structure to the identify transformation matrix.
721 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
722 SkScalarToFIXED(0), SkScalarToFIXED(1)};
723 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
724 GLYPHMETRICS gm;
725 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
726 return false;
727 }
728 SkASSERT(advance);
729 *advance = gm.gmCellIncX;
730 return true;
731}
732
733// static
734SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000735 uint32_t fontID,
736 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
reed@google.com59d2f632011-05-02 19:36:59 +0000737 LOGFONT lf;
738 GetLogFontByID(fontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000739 SkAdvancedTypefaceMetrics* info = NULL;
740
741 HDC hdc = CreateCompatibleDC(NULL);
742 HFONT font = CreateFontIndirect(&lf);
743 HFONT savefont = (HFONT)SelectObject(hdc, font);
744 HFONT designFont = NULL;
745
746 // To request design units, create a logical font whose height is specified
747 // as unitsPerEm.
748 OUTLINETEXTMETRIC otm;
749 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
750 !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
751 goto Error;
752 }
753 lf.lfHeight = -SkToS32(otm.otmEMSquare);
754 designFont = CreateFontIndirect(&lf);
755 SelectObject(hdc, designFont);
756 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
757 goto Error;
758 }
759 const unsigned glyphCount = calculateGlyphCount(hdc);
760
761 info = new SkAdvancedTypefaceMetrics;
762 info->fEmSize = otm.otmEMSquare;
763 info->fMultiMaster = false;
764 info->fLastGlyphID = SkToU16(glyphCount - 1);
765 info->fStyle = 0;
766#ifdef UNICODE
767 // Get the buffer size needed first.
768 size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
769 0, NULL, NULL);
770 // Allocate a buffer (str_len already has terminating null accounted for).
771 char *familyName = new char[str_len];
772 // Now actually convert the string.
773 WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
774 NULL, NULL);
775 info->fFontName.set(familyName);
776 delete [] familyName;
777#else
778 info->fFontName.set(lf.lfFaceName);
779#endif
780
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000781 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
782 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
783 }
784
reed@google.comac6b9792011-03-11 15:42:51 +0000785 if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
786 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
787 } else {
788 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
789 info->fItalicAngle = 0;
790 info->fAscent = 0;
791 info->fDescent = 0;
792 info->fStemV = 0;
793 info->fCapHeight = 0;
794 info->fBBox = SkIRect::MakeEmpty();
795 return info;
796 }
797
798 // If this bit is clear the font is a fixed pitch font.
799 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
800 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
801 }
802 if (otm.otmTextMetrics.tmItalic) {
803 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
804 }
805 // Setting symbolic style by default for now.
806 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
807 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
808 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
809 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
810 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
811 }
812
813 // The main italic angle of the font, in tenths of a degree counterclockwise
814 // from vertical.
815 info->fItalicAngle = otm.otmItalicAngle / 10;
816 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
817 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
818 // TODO(ctguil): Use alternate cap height calculation.
819 // MSDN says otmsCapEmHeight is not support but it is returning a value on
820 // my Win7 box.
821 info->fCapHeight = otm.otmsCapEmHeight;
822 info->fBBox =
823 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
824 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
825
826 // Figure out a good guess for StemV - Min width of i, I, !, 1.
827 // This probably isn't very good with an italic font.
828 int16_t min_width = SHRT_MAX;
829 info->fStemV = 0;
830 char stem_chars[] = {'i', 'I', '!', '1'};
831 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
832 ABC abcWidths;
833 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
834 int16_t width = abcWidths.abcB;
835 if (width > 0 && width < min_width) {
836 min_width = width;
837 info->fStemV = min_width;
838 }
839 }
840 }
841
842 // If bit 1 is set, the font may not be embedded in a document.
843 // If bit 1 is clear, the font can be embedded.
844 // If bit 2 is set, the embedding is read-only.
845 if (otm.otmfsType & 0x1) {
846 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
ctguil@chromium.org0e6dc0a2011-03-30 20:41:16 +0000847 } else if (perGlyphInfo &
848 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
reed@google.comac6b9792011-03-11 15:42:51 +0000849 info->fGlyphWidths.reset(
850 getAdvanceData(hdc, glyphCount, &getWidthAdvance));
851 }
852
853Error:
854 SelectObject(hdc, savefont);
855 DeleteObject(designFont);
856 DeleteObject(font);
857 DeleteDC(hdc);
858
859 return info;
860}
861
862SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
863
864 //Should not be used on Windows, keep linker happy
865 SkASSERT(false);
866 return SkCreateTypefaceFromLOGFONT(get_default_font());
867}
868
869SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +0000870 const DWORD kTTCTag =
871 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com59d2f632011-05-02 19:36:59 +0000872 LOGFONT lf;
873 GetLogFontByID(uniqueID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000874
875 HDC hdc = ::CreateCompatibleDC(NULL);
reed@google.com59d2f632011-05-02 19:36:59 +0000876 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000877 HFONT savefont = (HFONT)SelectObject(hdc, font);
878
vandebo@chromium.orgd6044812011-05-13 03:41:29 +0000879 SkMemoryStream* stream = NULL;
880 DWORD tables[2] = {kTTCTag, 0};
881 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
882 size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
883 if (bufferSize != GDI_ERROR) {
884 stream = new SkMemoryStream(bufferSize);
885 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(),
886 bufferSize)) {
887 break;
888 } else {
889 delete stream;
890 stream = NULL;
891 }
892 }
reed@google.comac6b9792011-03-11 15:42:51 +0000893 }
894
895 SelectObject(hdc, savefont);
896 DeleteObject(font);
897 DeleteDC(hdc);
898
899 return stream;
900}
901
902SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
903 return SkNEW_ARGS(SkScalerContext_Windows, (desc));
904}
905
906/** Return the closest matching typeface given either an existing family
907 (specified by a typeface in that family) or by a familyName, and a
908 requested style.
909 1) If familyFace is null, use famillyName.
910 2) If famillyName is null, use familyFace.
911 3) If both are null, return the default font that best matches style
912 This MUST not return NULL.
913 */
914
915SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
916 const char familyName[],
917 const void* data, size_t bytelength,
918 SkTypeface::Style style) {
reed@google.com59d2f632011-05-02 19:36:59 +0000919 LOGFONT lf;
reed@google.comac6b9792011-03-11 15:42:51 +0000920 if (NULL == familyFace && NULL == familyName) {
reed@google.com59d2f632011-05-02 19:36:59 +0000921 lf = get_default_font();
922 } else if (familyFace) {
923 LogFontTypeface* face = (LogFontTypeface*)familyFace;
924 lf = face->fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000925 } else {
reed@google.com59d2f632011-05-02 19:36:59 +0000926 memset(&lf, 0, sizeof(LOGFONT));
reed@google.comac6b9792011-03-11 15:42:51 +0000927#ifdef UNICODE
reed@google.com59d2f632011-05-02 19:36:59 +0000928 // Get the buffer size needed first.
929 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
930 -1, NULL, 0);
931 // Allocate a buffer (str_len already has terminating null
932 // accounted for).
933 wchar_t *wideFamilyName = new wchar_t[str_len];
934 // Now actually convert the string.
935 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
936 wideFamilyName, str_len);
937 ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
938 delete [] wideFamilyName;
reed@google.comac6b9792011-03-11 15:42:51 +0000939#else
reed@google.com59d2f632011-05-02 19:36:59 +0000940 ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
reed@google.comac6b9792011-03-11 15:42:51 +0000941#endif
reed@google.com59d2f632011-05-02 19:36:59 +0000942 lf.lfFaceName[LF_FACESIZE-1] = '\0';
reed@google.comac6b9792011-03-11 15:42:51 +0000943 }
reed@google.com59d2f632011-05-02 19:36:59 +0000944 setStyle(&lf, style);
945 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000946}
947
948size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
949 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
950 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
951 else
952 return 0; // nothing to do
953}
954
955int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
956 return 0;
957}
958
959void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
960 tables[0] = NULL; // black gamma (e.g. exp=1.4)
961 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
962}
963
964SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
965 printf("SkFontHost::CreateTypefaceFromFile unimplemented");
966 return NULL;
967}
968
969void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
970 // We don't control the hinting nor ClearType settings here
971 rec->setHinting(SkPaint::kNormal_Hinting);
972
973 // we do support LCD16
974 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
975 return;
976 }
977
978 if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
979 rec->fMaskFormat = SkMask::kA8_Format;
980 }
981}
982
983#endif // WIN32