blob: 1deb6b14459ead6b8bdaa03b73fd50f964c88413 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac6b9792011-03-11 15:42:51 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.comac6b9792011-03-11 15:42:51 +00007 */
8
reed@google.comac6b9792011-03-11 15:42:51 +00009#include "SkAdvancedTypefaceMetrics.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000010#include "SkBase64.h"
11#include "SkData.h"
12#include "SkDescriptor.h"
bungeman@google.come70f7982012-06-01 19:38:19 +000013#include "SkFontDescriptor.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000014#include "SkFontHost.h"
15#include "SkOTUtils.h"
reed@google.comac6b9792011-03-11 15:42:51 +000016#include "SkStream.h"
bungeman@google.coma5501992012-05-18 19:06:41 +000017#include "SkString.h"
reed@google.comac6b9792011-03-11 15:42:51 +000018#include "SkThread.h"
19#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000020#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000021#include "SkUtils.h"
22
bungeman@google.coma5501992012-05-18 19:06:41 +000023#include "SkTypes.h"
24#include <tchar.h>
25#include <usp10.h>
26#include <objbase.h>
reed@google.comac6b9792011-03-11 15:42:51 +000027
reed@google.comb8a5c612012-06-13 20:01:44 +000028static int compute_extra_height(const LOGFONT& lf) {
reed@google.com5a121ad2012-06-15 14:27:15 +000029
reed@google.comb8a5c612012-06-13 20:01:44 +000030 static const struct {
reed@google.com5a121ad2012-06-15 14:27:15 +000031 const char* fUCName; // UTF8 encoded, ascii is upper-case
reed@google.comb8a5c612012-06-13 20:01:44 +000032 int fExtraHeight;
33 } gData[] = {
reed@google.com5a121ad2012-06-15 14:27:15 +000034 // http://code.google.com/p/chromium/issues/detail?id=130842
35 { "DOTUM", 1 },
36 { "DOTUMCHE", 1 },
37 { "\xEB\x8F\x8B\xEC\x9B\x80", 1 },
38 { "\xEB\x8F\x8B\xEC\x9B\x80\xEC\xB2\xB4", 1 },
reed@google.comb8a5c612012-06-13 20:01:44 +000039 };
40
reed@google.com5a121ad2012-06-15 14:27:15 +000041 /**
42 * We convert the target name into upper-case (for ascii chars) UTF8.
43 * Our database is already stored in this fashion, and it allows us to
44 * search it with straight memcmp, since everyone is in this canonical
45 * form.
46 */
reed@google.comb8a5c612012-06-13 20:01:44 +000047
reed@google.com5a121ad2012-06-15 14:27:15 +000048 // temp storage is max # TCHARs * max expantion for UTF8 + null
49 char name[kMaxBytesInUTF8Sequence * LF_FACESIZE + 1];
50 int index = 0;
51 for (int i = 0; i < LF_FACESIZE; ++i) {
52 uint16_t c = lf.lfFaceName[i];
reed@google.comb8a5c612012-06-13 20:01:44 +000053 if (c >= 'a' && c <= 'z') {
54 c = c - 'a' + 'A';
55 }
reed@google.com5a121ad2012-06-15 14:27:15 +000056 size_t n = SkUTF16_ToUTF8(&c, 1, &name[index]);
57 index += n;
reed@google.comb8a5c612012-06-13 20:01:44 +000058 if (0 == c) {
59 break;
60 }
61 }
62
63 for (size_t j = 0; j < SK_ARRAY_COUNT(gData); ++j) {
64 if (!strcmp(gData[j].fUCName, name)) {
65 return gData[j].fExtraHeight;
66 }
67 }
68 return 0;
69}
70
reed@google.com6f5df482011-09-28 20:33:24 +000071// always packed xxRRGGBB
72typedef uint32_t SkGdiRGB;
73
74template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
75 return (T*)((char*)ptr + byteOffset);
76}
77
reed@google.coma767fa02011-08-05 21:40:26 +000078// define this in your Makefile or .gyp to enforce AA requests
79// which GDI ignores at small sizes. This flag guarantees AA
80// for rotated text, regardless of GDI's notions.
81//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
82
reed@google.comac6b9792011-03-11 15:42:51 +000083// client3d has to undefine this for now
84#define CAN_USE_LOGFONT_NAME
85
reed@google.com82a34d82011-07-26 19:33:08 +000086static bool isLCD(const SkScalerContext::Rec& rec) {
87 return SkMask::kLCD16_Format == rec.fMaskFormat ||
88 SkMask::kLCD32_Format == rec.fMaskFormat;
89}
90
reed@google.coma767fa02011-08-05 21:40:26 +000091static bool bothZero(SkScalar a, SkScalar b) {
92 return 0 == a && 0 == b;
93}
94
95// returns false if there is any non-90-rotation or skew
96static bool isAxisAligned(const SkScalerContext::Rec& rec) {
97 return 0 == rec.fPreSkewX &&
98 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
99 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
100}
101
102static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
103#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
104 // What we really want to catch is when GDI will ignore the AA request and give
105 // us BW instead. Smallish rotated text is one heuristic, so this code is just
106 // an approximation. We shouldn't need to do this for larger sizes, but at those
107 // sizes, the quality difference gets less and less between our general
108 // scanconverter and GDI's.
109 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
110 return true;
111 }
112#endif
113 // false means allow GDI to generate the bits
114 return false;
115}
116
reed@google.comac6b9792011-03-11 15:42:51 +0000117using namespace skia_advanced_typeface_metrics_utils;
118
reed@google.comac6b9792011-03-11 15:42:51 +0000119static const uint16_t BUFFERSIZE = (16384 - 32);
120static uint8_t glyphbuf[BUFFERSIZE];
121
reed@google.comac6b9792011-03-11 15:42:51 +0000122/**
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000123 * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
reed@google.comac6b9792011-03-11 15:42:51 +0000124 * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
125 * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
126 * actual requested size.
127 */
128static const int gCanonicalTextSize = 64;
129
130static void make_canonical(LOGFONT* lf) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000131 lf->lfHeight = -gCanonicalTextSize;
reed@google.com59d2f632011-05-02 19:36:59 +0000132 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
133 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +0000134// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +0000135}
136
bungeman@google.com90d812b2011-10-24 21:25:01 +0000137static SkTypeface::Style get_style(const LOGFONT& lf) {
reed@google.com59d2f632011-05-02 19:36:59 +0000138 unsigned style = 0;
139 if (lf.lfWeight >= FW_BOLD) {
140 style |= SkTypeface::kBold;
141 }
142 if (lf.lfItalic) {
143 style |= SkTypeface::kItalic;
144 }
bungeman@google.com90d812b2011-10-24 21:25:01 +0000145 return static_cast<SkTypeface::Style>(style);
reed@google.com59d2f632011-05-02 19:36:59 +0000146}
147
148static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
149 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
150 lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
reed@google.comac6b9792011-03-11 15:42:51 +0000151}
152
153static inline FIXED SkFixedToFIXED(SkFixed x) {
154 return *(FIXED*)(&x);
155}
bungeman@google.coma0319f62012-04-18 15:40:50 +0000156static inline SkFixed SkFIXEDToFixed(FIXED x) {
157 return *(SkFixed*)(&x);
158}
reed@google.comac6b9792011-03-11 15:42:51 +0000159
160static inline FIXED SkScalarToFIXED(SkScalar x) {
161 return SkFixedToFIXED(SkScalarToFixed(x));
162}
163
bungeman@google.coma0319f62012-04-18 15:40:50 +0000164static unsigned calculateOutlineGlyphCount(HDC hdc) {
reed@google.comac6b9792011-03-11 15:42:51 +0000165 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +0000166 const DWORD maxpTag =
167 SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
reed@google.comac6b9792011-03-11 15:42:51 +0000168 uint16_t glyphs;
169 if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
170 return SkEndian_SwapBE16(glyphs);
171 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000172
reed@google.comac6b9792011-03-11 15:42:51 +0000173 // Binary search for glyph count.
174 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
175 int32_t max = SK_MaxU16 + 1;
176 int32_t min = 0;
177 GLYPHMETRICS gm;
178 while (min < max) {
179 int32_t mid = min + ((max - min) / 2);
180 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
181 NULL, &mat2) == GDI_ERROR) {
182 max = mid;
183 } else {
184 min = mid + 1;
185 }
186 }
187 SkASSERT(min == max);
188 return min;
189}
190
reed@google.comac6b9792011-03-11 15:42:51 +0000191class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000192public:
reed@google.com59d2f632011-05-02 19:36:59 +0000193 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
bungeman@google.come70f7982012-06-01 19:38:19 +0000194 SkTypeface(style, fontID, false), fLogFont(lf), fSerializeAsStream(false) {}
reed@google.comac6b9792011-03-11 15:42:51 +0000195
reed@google.com59d2f632011-05-02 19:36:59 +0000196 LOGFONT fLogFont;
bungeman@google.come70f7982012-06-01 19:38:19 +0000197 bool fSerializeAsStream;
reed@google.comac6b9792011-03-11 15:42:51 +0000198
reed@google.com59d2f632011-05-02 19:36:59 +0000199 static LogFontTypeface* Create(const LOGFONT& lf) {
bungeman@google.com90d812b2011-10-24 21:25:01 +0000200 SkTypeface::Style style = get_style(lf);
reed@google.com59d2f632011-05-02 19:36:59 +0000201 SkFontID fontID = SkTypefaceCache::NewFontID();
202 return new LogFontTypeface(style, fontID, lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000203 }
204};
205
bungeman@google.coma5501992012-05-18 19:06:41 +0000206class FontMemResourceTypeface : public LogFontTypeface {
207public:
208 /**
209 * Takes ownership of fontMemResource.
210 */
211 FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
bungeman@google.come70f7982012-06-01 19:38:19 +0000212 LogFontTypeface(style, fontID, lf), fFontMemResource(fontMemResource) {
213 fSerializeAsStream = true;
214 }
bungeman@google.coma5501992012-05-18 19:06:41 +0000215
216 HANDLE fFontMemResource;
217
218 /**
219 * The created FontMemResourceTypeface takes ownership of fontMemResource.
220 */
221 static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
222 SkTypeface::Style style = get_style(lf);
223 SkFontID fontID = SkTypefaceCache::NewFontID();
224 return new FontMemResourceTypeface(style, fontID, lf, fontMemResource);
225 }
226
227protected:
228 virtual void weak_dispose() const SK_OVERRIDE {
229 RemoveFontMemResourceEx(fFontMemResource);
230 //SkTypefaceCache::Remove(this);
231 INHERITED::weak_dispose();
232 }
233
234private:
235 typedef LogFontTypeface INHERITED;
236};
237
reed@google.comac6b9792011-03-11 15:42:51 +0000238static const LOGFONT& get_default_font() {
239 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000240 return gDefaultFont;
241}
242
reed@google.com59d2f632011-05-02 19:36:59 +0000243static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000244 LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
reed@google.com59d2f632011-05-02 19:36:59 +0000245 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000246
bungeman@google.coma5501992012-05-18 19:06:41 +0000247 return lface &&
248 get_style(lface->fLogFont) == requestedStyle &&
reed@google.com59d2f632011-05-02 19:36:59 +0000249 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
250}
251
252/**
253 * This guy is public. It first searches the cache, and if a match is not found,
254 * it creates a new face.
255 */
256SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
257 LOGFONT lf = origLF;
258 make_canonical(&lf);
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000259 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
260 if (NULL == face) {
reed@google.com59d2f632011-05-02 19:36:59 +0000261 face = LogFontTypeface::Create(lf);
bungeman@google.com90d812b2011-10-24 21:25:01 +0000262 SkTypefaceCache::Add(face, get_style(lf));
reed@google.com59d2f632011-05-02 19:36:59 +0000263 }
264 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000265}
266
reed@google.comdb77a6a2011-07-19 19:08:33 +0000267/**
bungeman@google.coma5501992012-05-18 19:06:41 +0000268 * The created SkTypeface takes ownership of fontMemResource.
269 */
270SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
271 LOGFONT lf = origLF;
272 make_canonical(&lf);
273 FontMemResourceTypeface* face = FontMemResourceTypeface::Create(lf, fontMemResource);
274 SkTypefaceCache::Add(face, get_style(lf), false);
275 return face;
276}
277
278/**
reed@google.comdb77a6a2011-07-19 19:08:33 +0000279 * This guy is public
280 */
281void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
282 if (NULL == face) {
283 *lf = get_default_font();
284 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +0000285 *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
reed@google.comdb77a6a2011-07-19 19:08:33 +0000286 }
287}
288
reed@google.com7d26c592011-06-13 13:01:10 +0000289SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@google.comac6b9792011-03-11 15:42:51 +0000290 // Zero means that we don't have any fallback fonts for this fontID.
291 // This function is implemented on Android, but doesn't have much
292 // meaning here.
293 return 0;
294}
295
bungeman@google.com39698b12011-11-15 22:26:41 +0000296static void ensure_typeface_accessible(SkFontID fontID) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000297 LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
bungeman@google.comee51d1a2012-02-16 12:40:48 +0000298 if (face) {
299 SkFontHost::EnsureTypefaceAccessible(*face);
300 }
bungeman@google.com39698b12011-11-15 22:26:41 +0000301}
302
reed@google.com59d2f632011-05-02 19:36:59 +0000303static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
bungeman@google.coma5501992012-05-18 19:06:41 +0000304 LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
reed@google.com59d2f632011-05-02 19:36:59 +0000305 if (face) {
306 *lf = face->fLogFont;
307 } else {
308 sk_bzero(lf, sizeof(LOGFONT));
309 }
310}
311
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000312// Construct Glyph to Unicode table.
313// Unicode code points that require conjugate pairs in utf16 are not
314// supported.
315// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
316// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
317// of calling GetFontUnicodeRange().
318static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
319 SkTDArray<SkUnichar>* glyphToUnicode) {
320 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
321 if (!glyphSetBufferSize) {
322 return;
323 }
324
325 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
326 GLYPHSET* glyphSet =
327 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
328 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
329 return;
330 }
331
332 glyphToUnicode->setCount(glyphCount);
333 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
334 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
335 // There is no guarantee that within a Unicode range, the corresponding
336 // glyph id in a font file are continuous. So, even if we have ranges,
337 // we can't just use the first and last entry of the range to compute
338 // result. We need to enumerate them one by one.
339 int count = glyphSet->ranges[i].cGlyphs;
340 SkAutoTArray<WCHAR> chars(count + 1);
341 chars[count] = 0; // termintate string
342 SkAutoTArray<WORD> glyph(count);
343 for (USHORT j = 0; j < count; ++j) {
344 chars[j] = glyphSet->ranges[i].wcLow + j;
345 }
346 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
347 GGI_MARK_NONEXISTING_GLYPHS);
348 // If the glyph ID is valid, and the glyph is not mapped, then we will
349 // fill in the char id into the vector. If the glyph is mapped already,
350 // skip it.
351 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
352 // font cache, then generate this mapping table from there. It's
353 // unlikely to have collisions since glyph reuse happens mostly for
354 // different Unicode pages.
355 for (USHORT j = 0; j < count; ++j) {
356 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
357 (*glyphToUnicode)[glyph[j]] == 0) {
358 (*glyphToUnicode)[glyph[j]] = chars[j];
359 }
360 }
361 }
362}
363
reed@google.com99edd432011-09-09 14:59:59 +0000364//////////////////////////////////////////////////////////////////////////////////////
365
366static int alignTo32(int n) {
367 return (n + 31) & ~31;
368}
369
370struct MyBitmapInfo : public BITMAPINFO {
371 RGBQUAD fMoreSpaceForColors[1];
372};
373
374class HDCOffscreen {
375public:
376 HDCOffscreen() {
377 fFont = 0;
378 fDC = 0;
379 fBM = 0;
380 fBits = NULL;
381 fWidth = fHeight = 0;
382 fIsBW = false;
reed@google.com754e4eb2011-09-26 13:21:39 +0000383 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000384 }
385
386 ~HDCOffscreen() {
387 if (fDC) {
388 DeleteDC(fDC);
389 }
390 if (fBM) {
391 DeleteObject(fBM);
392 }
393 }
394
395 void init(HFONT font, const XFORM& xform) {
396 fFont = font;
397 fXform = xform;
398 }
399
reed@google.com6f5df482011-09-28 20:33:24 +0000400 const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
reed@google.com754e4eb2011-09-26 13:21:39 +0000401 size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000402
403private:
404 HDC fDC;
405 HBITMAP fBM;
406 HFONT fFont;
407 XFORM fXform;
408 void* fBits; // points into fBM
reed@google.com754e4eb2011-09-26 13:21:39 +0000409 COLORREF fColor;
reed@google.com99edd432011-09-09 14:59:59 +0000410 int fWidth;
411 int fHeight;
412 bool fIsBW;
reed@google.com754e4eb2011-09-26 13:21:39 +0000413
414 enum {
415 // will always trigger us to reset the color, since we
reed@google.com6f5df482011-09-28 20:33:24 +0000416 // should only store 0 or 0x00FFFFFF or gray (0x007F7F7F)
reed@google.com754e4eb2011-09-26 13:21:39 +0000417 kInvalid_Color = 12345
418 };
reed@google.com99edd432011-09-09 14:59:59 +0000419};
420
reed@google.com754e4eb2011-09-26 13:21:39 +0000421const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
reed@google.com6f5df482011-09-28 20:33:24 +0000422 SkGdiRGB fgColor, size_t* srcRBPtr) {
reed@google.com99edd432011-09-09 14:59:59 +0000423 if (0 == fDC) {
424 fDC = CreateCompatibleDC(0);
425 if (0 == fDC) {
426 return NULL;
427 }
428 SetGraphicsMode(fDC, GM_ADVANCED);
429 SetBkMode(fDC, TRANSPARENT);
430 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
431 SelectObject(fDC, fFont);
reed@google.com754e4eb2011-09-26 13:21:39 +0000432 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000433 }
434
435 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
436 DeleteObject(fBM);
437 fBM = 0;
438 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000439 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000440
reed@google.com6f5df482011-09-28 20:33:24 +0000441 COLORREF color = fgColor;
442 if (fIsBW) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000443 color = 0xFFFFFF;
reed@google.com99edd432011-09-09 14:59:59 +0000444 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000445 if (fColor != color) {
446 fColor = color;
447 COLORREF prev = SetTextColor(fDC, color);
448 SkASSERT(prev != CLR_INVALID);
449 }
450
reed@google.com99edd432011-09-09 14:59:59 +0000451 fWidth = SkMax32(fWidth, glyph.fWidth);
452 fHeight = SkMax32(fHeight, glyph.fHeight);
453
454 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
455
456 if (0 == fBM) {
457 MyBitmapInfo info;
458 sk_bzero(&info, sizeof(info));
459 if (isBW) {
460 RGBQUAD blackQuad = { 0, 0, 0, 0 };
461 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
462 info.bmiColors[0] = blackQuad;
463 info.bmiColors[1] = whiteQuad;
464 }
465 info.bmiHeader.biSize = sizeof(info.bmiHeader);
466 info.bmiHeader.biWidth = biWidth;
467 info.bmiHeader.biHeight = fHeight;
468 info.bmiHeader.biPlanes = 1;
469 info.bmiHeader.biBitCount = isBW ? 1 : 32;
470 info.bmiHeader.biCompression = BI_RGB;
471 if (isBW) {
472 info.bmiHeader.biClrUsed = 2;
473 }
474 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
475 if (0 == fBM) {
476 return NULL;
477 }
478 SelectObject(fDC, fBM);
479 }
480
481 // erase
482 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
483 size_t size = fHeight * srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +0000484 unsigned bg = (0 == color) ? 0xFF : 0;
485 memset(fBits, bg, size);
reed@google.com99edd432011-09-09 14:59:59 +0000486
487 XFORM xform = fXform;
488 xform.eDx = (float)-glyph.fLeft;
489 xform.eDy = (float)-glyph.fTop;
490 SetWorldTransform(fDC, &xform);
491
492 uint16_t glyphID = glyph.getGlyphID();
bungeman@google.com39698b12011-11-15 22:26:41 +0000493 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
reed@google.com99edd432011-09-09 14:59:59 +0000494 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000495 if (0 == ret) {
496 return NULL;
497 }
reed@google.com99edd432011-09-09 14:59:59 +0000498 *srcRBPtr = srcRB;
499 // offset to the start of the image
500 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
501}
502
reed@google.comb8a5c612012-06-13 20:01:44 +0000503//////////////////////////////////////////////////////////////////////////////
reed@google.com59d2f632011-05-02 19:36:59 +0000504
reed@google.comac6b9792011-03-11 15:42:51 +0000505class SkScalerContext_Windows : public SkScalerContext {
506public:
507 SkScalerContext_Windows(const SkDescriptor* desc);
508 virtual ~SkScalerContext_Windows();
509
510protected:
511 virtual unsigned generateGlyphCount();
512 virtual uint16_t generateCharToGlyph(SkUnichar uni);
513 virtual void generateAdvance(SkGlyph* glyph);
514 virtual void generateMetrics(SkGlyph* glyph);
515 virtual void generateImage(const SkGlyph& glyph);
516 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
reed@google.comb8a5c612012-06-13 20:01:44 +0000517 virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
518 SkPaint::FontMetrics* mY);
reed@google.com99edd432011-09-09 14:59:59 +0000519
reed@google.comac6b9792011-03-11 15:42:51 +0000520private:
reed@google.com99edd432011-09-09 14:59:59 +0000521 HDCOffscreen fOffscreen;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000522 SkScalar fScale; // to get from canonical size to real size
reed@google.comac6b9792011-03-11 15:42:51 +0000523 MAT2 fMat22;
524 XFORM fXform;
525 HDC fDDC;
526 HFONT fSavefont;
527 HFONT fFont;
528 SCRIPT_CACHE fSC;
529 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000530
reed@google.comb8a5c612012-06-13 20:01:44 +0000531 /**
532 * Some fonts need extra pixels added to avoid clipping, as the bounds
533 * returned by getOutlineMetrics does not match what GDI draws. Since
534 * this costs more RAM and therefore slower blits, we have a table to
535 * only do this for known "bad" fonts.
536 */
537 int fExtraHeightHack;
538
reed@google.com1dd17a12011-05-17 14:04:41 +0000539 HFONT fHiResFont;
540 MAT2 fMat22Identity;
541 SkMatrix fHiResMatrix;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000542 enum Type {
543 kTrueType_Type, kBitmap_Type,
544 } fType;
545 TEXTMETRIC fTM;
reed@google.comac6b9792011-03-11 15:42:51 +0000546};
547
548static float mul2float(SkScalar a, SkScalar b) {
549 return SkScalarToFloat(SkScalarMul(a, b));
550}
551
552static FIXED float2FIXED(float x) {
553 return SkFixedToFIXED(SkFloatToFixed(x));
554}
555
digit@google.com1771cbf2012-01-26 21:26:40 +0000556SK_DECLARE_STATIC_MUTEX(gFTMutex);
reed@google.com59d2f632011-05-02 19:36:59 +0000557
reed@google.com1dd17a12011-05-17 14:04:41 +0000558#define HIRES_TEXTSIZE 2048
559#define HIRES_SHIFT 11
560static inline SkFixed HiResToFixed(int value) {
561 return value << (16 - HIRES_SHIFT);
562}
563
564static bool needHiResMetrics(const SkScalar mat[2][2]) {
565 return mat[1][0] || mat[0][1];
566}
567
reed@google.com82a34d82011-07-26 19:33:08 +0000568static BYTE compute_quality(const SkScalerContext::Rec& rec) {
569 switch (rec.fMaskFormat) {
570 case SkMask::kBW_Format:
571 return NONANTIALIASED_QUALITY;
572 case SkMask::kLCD16_Format:
573 case SkMask::kLCD32_Format:
574 return CLEARTYPE_QUALITY;
575 default:
reed@google.com8351aab2012-01-18 17:06:35 +0000576 if (rec.fFlags & SkScalerContext::kGenA8FromLCD_Flag) {
577 return CLEARTYPE_QUALITY;
578 } else {
579 return ANTIALIASED_QUALITY;
580 }
reed@google.com82a34d82011-07-26 19:33:08 +0000581 }
582}
583
reed@google.comac6b9792011-03-11 15:42:51 +0000584SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
585 : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
586 , fGlyphCount(-1) {
587 SkAutoMutexAcquire ac(gFTMutex);
588
reed@google.comac6b9792011-03-11 15:42:51 +0000589 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com1dd17a12011-05-17 14:04:41 +0000590 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000591 SetBkMode(fDDC, TRANSPARENT);
592
593 // Scaling by the DPI is inconsistent with how Skia draws elsewhere
594 //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
reed@google.com59d2f632011-05-02 19:36:59 +0000595 LOGFONT lf;
596 GetLogFontByID(fRec.fFontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000597 lf.lfHeight = -gCanonicalTextSize;
reed@google.com82a34d82011-07-26 19:33:08 +0000598 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000599 fFont = CreateFontIndirect(&lf);
reed@google.com1dd17a12011-05-17 14:04:41 +0000600
reed@google.comb8a5c612012-06-13 20:01:44 +0000601 fExtraHeightHack = compute_extra_height(lf);
602
reed@google.com1dd17a12011-05-17 14:04:41 +0000603 // if we're rotated, or want fractional widths, create a hires font
604 fHiResFont = 0;
bungeman@google.comc84547a2012-01-05 20:18:06 +0000605 if (needHiResMetrics(fRec.fPost2x2)) {
reed@google.com1dd17a12011-05-17 14:04:41 +0000606 lf.lfHeight = -HIRES_TEXTSIZE;
607 fHiResFont = CreateFontIndirect(&lf);
608
609 fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
610 fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
611
612 // construct a matrix to go from HIRES logical units to our device units
613 fRec.getSingleMatrix(&fHiResMatrix);
614 SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
615 fHiResMatrix.preScale(scale, scale);
616 }
reed@google.comac6b9792011-03-11 15:42:51 +0000617 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000618
bungeman@google.coma0319f62012-04-18 15:40:50 +0000619 if (0 == GetTextMetrics(fDDC, &fTM)) {
620 ensure_typeface_accessible(fRec.fFontID);
621 if (0 == GetTextMetrics(fDDC, &fTM)) {
622 fTM.tmPitchAndFamily = TMPF_TRUETYPE;
623 }
624 }
625 // Used a logfont on a memory context, should never get a device font.
bungeman@google.com90b7e382012-04-20 15:26:28 +0000626 // Therefore all TMPF_DEVICE will be PostScript fonts.
627
628 // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE must be set,
629 // otherwise we have a vector FON, which we don't support.
630 // This was determined by testing with Type1 PFM/PFB and OpenTypeCFF OTF,
631 // as well as looking at Wine bugs and sources.
632 SkASSERT(!(fTM.tmPitchAndFamily & TMPF_VECTOR) ||
633 (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)));
634
635 if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000636 // Truetype or PostScript.
637 // Stroked FON also gets here (TMPF_VECTOR), but we don't handle it.
638 fType = SkScalerContext_Windows::kTrueType_Type;
639 fScale = fRec.fTextSize / gCanonicalTextSize;
640
641 fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
642 fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
643 fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
644 fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
645 fXform.eDx = 0;
646 fXform.eDy = 0;
647
648 fMat22.eM11 = float2FIXED(fXform.eM11);
649 fMat22.eM12 = float2FIXED(fXform.eM12);
650 fMat22.eM21 = float2FIXED(-fXform.eM21);
651 fMat22.eM22 = float2FIXED(-fXform.eM22);
652
653 if (needToRenderWithSkia(fRec)) {
654 this->forceGenerateImageFromPath();
655 }
656
657 } else {
658 // Assume bitmap
659 fType = SkScalerContext_Windows::kBitmap_Type;
660 fScale = SK_Scalar1;
661
662 fXform.eM11 = 1.0f;
663 fXform.eM12 = 0.0f;
664 fXform.eM21 = 0.0f;
665 fXform.eM22 = 1.0f;
666 fXform.eDx = 0.0f;
667 fXform.eDy = 0.0f;
668
669 fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
670 fMat22.eM12 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
671 fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
672 fMat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
673
674 lf.lfHeight = -SkScalarCeilToInt(fRec.fTextSize);
675 HFONT bitmapFont = CreateFontIndirect(&lf);
676 SelectObject(fDDC, bitmapFont);
677 ::DeleteObject(fFont);
678 fFont = bitmapFont;
679
680 if (0 == GetTextMetrics(fDDC, &fTM)) {
681 ensure_typeface_accessible(fRec.fFontID);
682 //if the following fails, we'll just draw at gCanonicalTextSize.
683 GetTextMetrics(fDDC, &fTM);
684 }
reed@google.coma767fa02011-08-05 21:40:26 +0000685 }
reed@google.com99edd432011-09-09 14:59:59 +0000686
687 fOffscreen.init(fFont, fXform);
reed@google.comac6b9792011-03-11 15:42:51 +0000688}
689
690SkScalerContext_Windows::~SkScalerContext_Windows() {
691 if (fDDC) {
692 ::SelectObject(fDDC, fSavefont);
693 ::DeleteDC(fDDC);
694 }
695 if (fFont) {
696 ::DeleteObject(fFont);
697 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000698 if (fHiResFont) {
699 ::DeleteObject(fHiResFont);
700 }
reed@google.comac6b9792011-03-11 15:42:51 +0000701 if (fSC) {
702 ::ScriptFreeCache(&fSC);
703 }
704}
705
706unsigned SkScalerContext_Windows::generateGlyphCount() {
707 if (fGlyphCount < 0) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000708 if (fType == SkScalerContext_Windows::kBitmap_Type) {
709 return fTM.tmLastChar;
710 }
711 fGlyphCount = calculateOutlineGlyphCount(fDDC);
reed@google.comac6b9792011-03-11 15:42:51 +0000712 }
713 return fGlyphCount;
714}
715
716uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
717 uint16_t index = 0;
718 WCHAR c[2];
719 // TODO(ctguil): Support characters that generate more than one glyph.
720 if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
721 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
722 SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
723 } else {
724 // Use uniscribe to detemine glyph index for non-BMP characters.
725 // Need to add extra item to SCRIPT_ITEM to work around a bug in older
726 // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
727 SCRIPT_ITEM si[2 + 1];
728 int items;
729 SkAssertResult(
730 SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
731
732 WORD log[2];
733 SCRIPT_VISATTR vsa;
734 int glyphs;
735 SkAssertResult(SUCCEEDED(ScriptShape(
736 fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
737 }
738 return index;
739}
740
741void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
742 this->generateMetrics(glyph);
743}
744
745void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
746
747 SkASSERT(fDDC);
748
bungeman@google.coma0319f62012-04-18 15:40:50 +0000749 if (fType == SkScalerContext_Windows::kBitmap_Type) {
750 SIZE size;
751 WORD glyphs = glyph->getGlyphID(0);
752 if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
753 glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
754 } else {
755 glyph->fWidth = SkToS16(size.cx);
756 }
757 glyph->fHeight = SkToS16(size.cy);
758
759 glyph->fTop = SkToS16(-fTM.tmAscent);
760 glyph->fLeft = SkToS16(0);
761 glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
762 glyph->fAdvanceY = 0;
763
764 //Apply matrix to values.
765 glyph->fAdvanceY = SkFixedMul(SkFIXEDToFixed(fMat22.eM21), glyph->fAdvanceX);
766 glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
767
reed@google.comb8a5c612012-06-13 20:01:44 +0000768 glyph->fHeight += fExtraHeightHack;
bungeman@google.coma0319f62012-04-18 15:40:50 +0000769 return;
770 }
771
reed@google.comac6b9792011-03-11 15:42:51 +0000772 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000773 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000774
775 glyph->fRsbDelta = 0;
776 glyph->fLsbDelta = 0;
777
778 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
779 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
780 uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
bungeman@google.com39698b12011-11-15 22:26:41 +0000781 if (GDI_ERROR == ret) {
782 ensure_typeface_accessible(fRec.fFontID);
783 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
784 }
reed@google.comac6b9792011-03-11 15:42:51 +0000785
786 if (GDI_ERROR != ret) {
787 if (ret == 0) {
788 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
789 gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
790 }
791 glyph->fWidth = gm.gmBlackBoxX;
792 glyph->fHeight = gm.gmBlackBoxY;
793 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
794 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
795 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
796 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
797
reed@google.comf8cead52011-09-02 20:08:11 +0000798 // we outset in all dimensions, since the image may bleed outside
reed@google.comac6b9792011-03-11 15:42:51 +0000799 // of the computed bounds returned by GetGlyphOutline.
800 // This was deduced by trial and error for small text (e.g. 8pt), so there
801 // maybe a more precise way to make this adjustment...
reed@google.comf8cead52011-09-02 20:08:11 +0000802 //
803 // This test shows us clipping the tops of some of the CJK fonts unless we
804 // increase the top of the box by 2, hence the height by 4. This seems to
805 // correspond to an embedded bitmap font, but not sure.
806 // LayoutTests/fast/text/backslash-to-yen-sign-euc.html
807 //
reed@google.com5e2df642011-09-21 18:42:09 +0000808 if (glyph->fWidth) { // don't outset an empty glyph
809 glyph->fWidth += 4;
810 glyph->fHeight += 4;
811 glyph->fTop -= 2;
812 glyph->fLeft -= 2;
reed@google.comb8a5c612012-06-13 20:01:44 +0000813
814 glyph->fHeight += fExtraHeightHack;
reed@google.com5e2df642011-09-21 18:42:09 +0000815 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000816
817 if (fHiResFont) {
818 SelectObject(fDDC, fHiResFont);
819 sk_bzero(&gm, sizeof(gm));
820 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
821 if (GDI_ERROR != ret) {
822 SkPoint advance;
823 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
824 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
825 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
826 }
827 SelectObject(fDDC, fFont);
828 }
reed@google.comac6b9792011-03-11 15:42:51 +0000829 } else {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000830 glyph->zeroMetrics();
reed@google.comac6b9792011-03-11 15:42:51 +0000831 }
832}
833
834void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
835// Note: This code was borrowed from generateLineHeight, which has a note
836// stating that it may be incorrect.
837 if (!(mx || my))
838 return;
839
840 SkASSERT(fDDC);
841
bungeman@google.coma0319f62012-04-18 15:40:50 +0000842 if (fType == SkScalerContext_Windows::kBitmap_Type) {
843 if (mx) {
844 mx->fTop = SkIntToScalar(-fTM.tmAscent);
845 mx->fAscent = SkIntToScalar(-fTM.tmAscent);
846 mx->fDescent = -SkIntToScalar(fTM.tmDescent);
847 mx->fBottom = SkIntToScalar(fTM.tmDescent);
848 mx->fLeading = SkIntToScalar(fTM.tmInternalLeading
849 + fTM.tmExternalLeading);
850 }
851
852 if (my) {
853 my->fTop = SkIntToScalar(-fTM.tmAscent);
854 my->fAscent = SkIntToScalar(-fTM.tmAscent);
855 my->fDescent = SkIntToScalar(-fTM.tmDescent);
856 my->fBottom = SkIntToScalar(fTM.tmDescent);
857 my->fLeading = SkIntToScalar(fTM.tmInternalLeading
858 + fTM.tmExternalLeading);
859 }
860 return;
861 }
862
reed@google.comac6b9792011-03-11 15:42:51 +0000863 OUTLINETEXTMETRIC otm;
864
865 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com39698b12011-11-15 22:26:41 +0000866 if (GDI_ERROR == ret) {
867 ensure_typeface_accessible(fRec.fFontID);
868 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
869 }
reed@google.comac6b9792011-03-11 15:42:51 +0000870 if (sizeof(otm) != ret) {
bungeman@google.coma0319f62012-04-18 15:40:50 +0000871 return;
reed@google.comac6b9792011-03-11 15:42:51 +0000872 }
873
874 if (mx) {
875 mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000876 mx->fAscent = -fScale * otm.otmAscent;
877 mx->fDescent = -fScale * otm.otmDescent;
878 mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
879 mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
880 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000881 }
882
883 if (my) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000884 my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
885 my->fAscent = -fScale * otm.otmAscent;
886 my->fDescent = -fScale * otm.otmDescent;
887 my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
888 my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
889 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000890 }
891}
892
reed@google.com7430a332011-10-03 14:37:38 +0000893////////////////////////////////////////////////////////////////////////////////////////
894
895static void build_power_table(uint8_t table[], float ee) {
896 for (int i = 0; i < 256; i++) {
897 float x = i / 255.f;
898 x = powf(x, ee);
899 int xx = SkScalarRound(SkFloatToScalar(x * 255));
900 table[i] = SkToU8(xx);
901 }
902}
903
904// This will invert the gamma applied by GDI, so we can sort-of get linear values.
905// Needed when we draw non-black, non-white text, and don't know how to bias it.
906static const uint8_t* getInverseGammaTable() {
907 static bool gInited;
908 static uint8_t gTable[256];
909 if (!gInited) {
910 UINT level = 0;
911 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
912 // can't get the data, so use a default
913 level = 1400;
914 }
915 build_power_table(gTable, level / 1000.0f);
916 gInited = true;
917 }
918 return gTable;
919}
920
reed@google.comac6b9792011-03-11 15:42:51 +0000921#include "SkColorPriv.h"
922
reed@google.com5e2df642011-09-21 18:42:09 +0000923// gdi's bitmap is upside-down, so we reverse dst walking in Y
924// whenever we copy it into skia's buffer
925
reed@google.com8351aab2012-01-18 17:06:35 +0000926static int compute_luminance(int r, int g, int b) {
927// return (r * 2 + g * 5 + b) >> 3;
928 return (r * 27 + g * 92 + b * 9) >> 7;
929}
930
reed@google.com5e2df642011-09-21 18:42:09 +0000931static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
reed@google.com6f5df482011-09-28 20:33:24 +0000932 int r = (rgb >> 16) & 0xFF;
933 int g = (rgb >> 8) & 0xFF;
934 int b = (rgb >> 0) & 0xFF;
reed@google.com8351aab2012-01-18 17:06:35 +0000935 return compute_luminance(r, g, b);
reed@google.com82a34d82011-07-26 19:33:08 +0000936}
937
reed@google.com5e2df642011-09-21 18:42:09 +0000938static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
reed@google.comac6b9792011-03-11 15:42:51 +0000939 int r = (rgb >> 16) & 0xFF;
940 int g = (rgb >> 8) & 0xFF;
941 int b = (rgb >> 0) & 0xFF;
reed@google.comac6b9792011-03-11 15:42:51 +0000942 return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
943}
944
reed@google.com754e4eb2011-09-26 13:21:39 +0000945static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb) {
946 int r = (rgb >> 16) & 0xFF;
947 int g = (rgb >> 8) & 0xFF;
948 int b = (rgb >> 0) & 0xFF;
949 int a = SkMax32(r, SkMax32(g, b));
950 return SkPackARGB32(a, r, g, b);
951}
952
reed@google.com82cff022011-09-22 14:33:40 +0000953// Is this GDI color neither black nor white? If so, we have to keep this
954// image as is, rather than smashing it down to a BW mask.
955//
956// returns int instead of bool, since we don't want/have to pay to convert
957// the zero/non-zero value into a bool
958static int is_not_black_or_white(SkGdiRGB c) {
959 // same as (but faster than)
960 // c &= 0x00FFFFFF;
961 // return 0 == c || 0x00FFFFFF == c;
962 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +0000963}
964
965static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int srcRB) {
966 for (int y = 0; y < height; ++y) {
967 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +0000968 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +0000969 return false;
970 }
971 }
reed@google.com6f5df482011-09-28 20:33:24 +0000972 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000973 }
974 return true;
975}
976
977static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +0000978 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +0000979 const int width = glyph.fWidth;
980 const size_t dstRB = (width + 7) >> 3;
981 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
982
983 int byteCount = width >> 3;
984 int bitCount = width & 7;
985
986 // adjust srcRB to skip the values in our byteCount loop,
987 // since we increment src locally there
988 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
989
990 for (int y = 0; y < glyph.fHeight; ++y) {
991 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +0000992 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +0000993 unsigned byte = 0;
reed@google.com754e4eb2011-09-26 13:21:39 +0000994 byte |= (src[0] ^ xorMask) & (1 << 7);
995 byte |= (src[1] ^ xorMask) & (1 << 6);
996 byte |= (src[2] ^ xorMask) & (1 << 5);
997 byte |= (src[3] ^ xorMask) & (1 << 4);
998 byte |= (src[4] ^ xorMask) & (1 << 3);
999 byte |= (src[5] ^ xorMask) & (1 << 2);
1000 byte |= (src[6] ^ xorMask) & (1 << 1);
1001 byte |= (src[7] ^ xorMask) & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +00001002 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +00001003 src += 8;
1004 }
1005 }
1006 if (bitCount > 0) {
1007 unsigned byte = 0;
1008 unsigned mask = 0x80;
1009 for (int i = 0; i < bitCount; i++) {
reed@google.com6a8f14d2011-09-27 12:54:24 +00001010 byte |= (src[i] ^ xorMask) & mask;
reed@google.com5e2df642011-09-21 18:42:09 +00001011 mask >>= 1;
1012 }
1013 dst[byteCount] = byte;
1014 }
reed@google.com6f5df482011-09-28 20:33:24 +00001015 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001016 dst -= dstRB;
1017 }
1018}
1019
1020static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +00001021 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +00001022 const size_t dstRB = glyph.rowBytes();
1023 const int width = glyph.fWidth;
1024 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1025
1026 for (int y = 0; y < glyph.fHeight; y++) {
1027 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001028 dst[i] = rgb_to_a8(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +00001029 }
reed@google.com6f5df482011-09-28 20:33:24 +00001030 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001031 dst -= dstRB;
1032 }
1033}
1034
1035static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +00001036 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +00001037 const size_t dstRB = glyph.rowBytes();
1038 const int width = glyph.fWidth;
1039 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1040
1041 for (int y = 0; y < glyph.fHeight; y++) {
1042 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001043 dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +00001044 }
reed@google.com6f5df482011-09-28 20:33:24 +00001045 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +00001046 dst = (uint16_t*)((char*)dst - dstRB);
1047 }
1048}
1049
reed@google.com754e4eb2011-09-26 13:21:39 +00001050static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
1051 const SkGlyph& glyph, int32_t xorMask) {
1052 const size_t dstRB = glyph.rowBytes();
1053 const int width = glyph.fWidth;
1054 SkPMColor* SK_RESTRICT dst = (SkPMColor*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1055
1056 for (int y = 0; y < glyph.fHeight; y++) {
1057 for (int i = 0; i < width; i++) {
1058 dst[i] = rgb_to_lcd32(src[i] ^ xorMask);
1059 }
reed@google.com6f5df482011-09-28 20:33:24 +00001060 src = SkTAddByteOffset(src, srcRB);
reed@google.com754e4eb2011-09-26 13:21:39 +00001061 dst = (SkPMColor*)((char*)dst - dstRB);
1062 }
1063}
1064
reed@google.com6f5df482011-09-28 20:33:24 +00001065static inline unsigned clamp255(unsigned x) {
1066 SkASSERT(x <= 256);
1067 return x - (x >> 8);
1068}
1069
reed@google.comffe49f52011-11-22 19:42:41 +00001070#define WHITE_LUMINANCE_LIMIT 0xA0
1071#define BLACK_LUMINANCE_LIMIT 0x40
1072
reed@google.comac6b9792011-03-11 15:42:51 +00001073void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +00001074 SkAutoMutexAcquire ac(gFTMutex);
1075
1076 SkASSERT(fDDC);
1077
reed@google.com62711172011-05-18 15:08:10 +00001078 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +00001079 const bool isAA = !isLCD(fRec);
reed@google.comffe49f52011-11-22 19:42:41 +00001080 bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
1081 bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
reed@google.com6f5df482011-09-28 20:33:24 +00001082
1083 SkGdiRGB fgColor;
1084 uint32_t rgbXOR;
reed@google.com7430a332011-10-03 14:37:38 +00001085 const uint8_t* table = NULL;
reed@google.com6f5df482011-09-28 20:33:24 +00001086 if (isBW || isWhite) {
1087 fgColor = 0x00FFFFFF;
1088 rgbXOR = 0;
1089 } else if (isBlack) {
1090 fgColor = 0;
1091 rgbXOR = ~0;
1092 } else {
reed@google.com7430a332011-10-03 14:37:38 +00001093 table = getInverseGammaTable();
1094 fgColor = 0x00FFFFFF;
reed@google.com6f5df482011-09-28 20:33:24 +00001095 rgbXOR = 0;
reed@google.com6f5df482011-09-28 20:33:24 +00001096 }
reed@google.comac6b9792011-03-11 15:42:51 +00001097
reed@google.com99edd432011-09-09 14:59:59 +00001098 size_t srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +00001099 const void* bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +00001100 if (NULL == bits) {
1101 ensure_typeface_accessible(fRec.fFontID);
1102 bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
1103 if (NULL == bits) {
1104 sk_bzero(glyph.fImage, glyph.computeImageSize());
1105 return;
1106 }
reed@google.com82a34d82011-07-26 19:33:08 +00001107 }
reed@google.comac6b9792011-03-11 15:42:51 +00001108
reed@google.com7430a332011-10-03 14:37:38 +00001109 if (table) {
reed@google.com6f5df482011-09-28 20:33:24 +00001110 SkGdiRGB* addr = (SkGdiRGB*)bits;
1111 for (int y = 0; y < glyph.fHeight; ++y) {
1112 for (int x = 0; x < glyph.fWidth; ++x) {
1113 int r = (addr[x] >> 16) & 0xFF;
1114 int g = (addr[x] >> 8) & 0xFF;
1115 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +00001116 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +00001117 }
1118 addr = SkTAddByteOffset(addr, srcRB);
1119 }
1120 }
1121
reed@google.com82a34d82011-07-26 19:33:08 +00001122 int width = glyph.fWidth;
1123 size_t dstRB = glyph.rowBytes();
1124 if (isBW) {
1125 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +00001126 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
1127 for (int y = 0; y < glyph.fHeight; y++) {
1128 memcpy(dst, src, dstRB);
1129 src += srcRB;
1130 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +00001131 }
reed@google.com82a34d82011-07-26 19:33:08 +00001132 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +00001133 // since the caller may require A8 for maskfilters, we can't check for BW
1134 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +00001135 const SkGdiRGB* src = (const SkGdiRGB*)bits;
reed@google.com6f5df482011-09-28 20:33:24 +00001136 rgb_to_a8(src, srcRB, glyph, rgbXOR);
reed@google.com82a34d82011-07-26 19:33:08 +00001137 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +00001138 const SkGdiRGB* src = (const SkGdiRGB*)bits;
1139 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001140 rgb_to_bw(src, srcRB, glyph, rgbXOR);
reed@google.com5e2df642011-09-21 18:42:09 +00001141 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
1142 } else {
reed@google.com754e4eb2011-09-26 13:21:39 +00001143 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
1144 rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
1145 } else {
1146 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
1147 rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
1148 }
reed@google.comac6b9792011-03-11 15:42:51 +00001149 }
1150 }
reed@google.comac6b9792011-03-11 15:42:51 +00001151}
1152
1153void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
1154
1155 SkAutoMutexAcquire ac(gFTMutex);
1156
1157 SkASSERT(&glyph && path);
1158 SkASSERT(fDDC);
1159
1160 path->reset();
1161
1162#if 0
1163 char buf[1024];
1164 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
1165 OutputDebugString(buf);
1166#endif
1167
1168 GLYPHMETRICS gm;
1169 uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
bungeman@google.com39698b12011-11-15 22:26:41 +00001170 if (GDI_ERROR == total_size) {
1171 ensure_typeface_accessible(fRec.fFontID);
1172 total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
1173 }
reed@google.comac6b9792011-03-11 15:42:51 +00001174
1175 if (GDI_ERROR != total_size) {
1176
1177 const uint8_t* cur_glyph = glyphbuf;
1178 const uint8_t* end_glyph = glyphbuf + total_size;
1179
1180 while(cur_glyph < end_glyph) {
1181 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
1182
1183 const uint8_t* end_poly = cur_glyph + th->cb;
1184 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
1185
1186 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
1187
1188 while(cur_poly < end_poly) {
1189 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
1190
1191 if (pc->wType == TT_PRIM_LINE) {
1192 for (uint16_t i = 0; i < pc->cpfx; i++) {
1193 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
1194 }
1195 }
1196
1197 if (pc->wType == TT_PRIM_QSPLINE) {
1198 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
1199 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
1200 POINTFX pnt_c = pc->apfx[u+1];
1201
1202 if (u < pc->cpfx - 2) { // If not on last spline, compute C
1203 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
1204 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
1205 }
1206
1207 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
1208 }
1209 }
1210 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
1211 }
1212 cur_glyph += th->cb;
1213 path->close();
1214 }
1215 }
1216 else {
1217 SkASSERT(false);
1218 }
1219 //char buf[1024];
1220 //sprintf(buf, "generatePath: count:%d\n", count);
1221 //OutputDebugString(buf);
1222}
1223
bungeman@google.come70f7982012-06-01 19:38:19 +00001224static void logfont_for_name(const char* familyName, LOGFONT& lf) {
1225 memset(&lf, 0, sizeof(LOGFONT));
1226#ifdef UNICODE
1227 // Get the buffer size needed first.
1228 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1229 -1, NULL, 0);
1230 // Allocate a buffer (str_len already has terminating null
1231 // accounted for).
1232 wchar_t *wideFamilyName = new wchar_t[str_len];
1233 // Now actually convert the string.
1234 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1235 wideFamilyName, str_len);
1236 ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE - 1);
1237 delete [] wideFamilyName;
1238 lf.lfFaceName[LF_FACESIZE-1] = L'\0';
1239#else
1240 ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE - 1);
1241 lf.lfFaceName[LF_FACESIZE - 1] = '\0';
1242#endif
1243}
1244
1245static void logfont_to_name(const LOGFONT& lf, SkString* s) {
1246#ifdef UNICODE
1247 // Get the buffer size needed first.
1248 size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
1249 0, NULL, NULL);
1250 // Allocate a buffer (str_len already has terminating null accounted for).
1251 s->resize(str_len);
1252 // Now actually convert the string.
1253 WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1,
1254 s->writable_str(), str_len,
1255 NULL, NULL);
1256#else
1257 s->set(lf.lfFaceName);
1258#endif
1259}
1260
1261void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
1262 const LogFontTypeface* face = static_cast<const LogFontTypeface*>(rawFace);
1263 SkFontDescriptor descriptor(face->style());
1264
1265 SkString familyName;
1266 logfont_to_name(face->fLogFont, &familyName);
1267 descriptor.setFamilyName(familyName.c_str());
1268 //TODO: FileName and PostScriptName currently unsupported.
1269
1270 descriptor.serialize(stream);
1271
1272 if (face->fSerializeAsStream) {
1273 // store the entire font in the fontData
1274 SkAutoTUnref<SkStream> fontStream(SkFontHost::OpenStream(face->uniqueID()));
1275 const uint32_t length = fontStream->getLength();
1276
1277 stream->writePackedUInt(length);
1278 stream->writeStream(fontStream, length);
1279 } else {
1280 stream->writePackedUInt(0);
1281 }
reed@google.comac6b9792011-03-11 15:42:51 +00001282}
1283
1284SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
bungeman@google.come70f7982012-06-01 19:38:19 +00001285 SkFontDescriptor descriptor(stream);
1286
1287 const uint32_t customFontDataLength = stream->readPackedUInt();
1288 if (customFontDataLength > 0) {
1289 // generate a new stream to store the custom typeface
1290 SkAutoTUnref<SkMemoryStream> fontStream(SkNEW_ARGS(SkMemoryStream, (customFontDataLength - 1)));
1291 stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
1292
1293 return CreateTypefaceFromStream(fontStream.get());
1294 }
1295
1296 return SkFontHost::CreateTypeface(NULL, descriptor.getFamilyName(), descriptor.getStyle());
reed@google.comac6b9792011-03-11 15:42:51 +00001297}
1298
1299static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
1300 // Initialize the MAT2 structure to the identify transformation matrix.
1301 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
1302 SkScalarToFIXED(0), SkScalarToFIXED(1)};
1303 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
1304 GLYPHMETRICS gm;
1305 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
1306 return false;
1307 }
1308 SkASSERT(advance);
1309 *advance = gm.gmCellIncX;
1310 return true;
1311}
1312
1313// static
1314SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +00001315 uint32_t fontID,
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001316 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1317 const uint32_t* glyphIDs,
1318 uint32_t glyphIDsCount) {
reed@google.com59d2f632011-05-02 19:36:59 +00001319 LOGFONT lf;
1320 GetLogFontByID(fontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001321 SkAdvancedTypefaceMetrics* info = NULL;
1322
1323 HDC hdc = CreateCompatibleDC(NULL);
1324 HFONT font = CreateFontIndirect(&lf);
1325 HFONT savefont = (HFONT)SelectObject(hdc, font);
1326 HFONT designFont = NULL;
1327
reed@google.com05b6f3a2011-11-28 15:30:28 +00001328 const char stem_chars[] = {'i', 'I', '!', '1'};
1329 int16_t min_width;
1330 unsigned glyphCount;
1331
reed@google.comac6b9792011-03-11 15:42:51 +00001332 // To request design units, create a logical font whose height is specified
1333 // as unitsPerEm.
1334 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001335 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1336 if (0 == otmRet) {
1337 ensure_typeface_accessible(fontID);
1338 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1339 }
1340 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001341 goto Error;
1342 }
1343 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1344 designFont = CreateFontIndirect(&lf);
1345 SelectObject(hdc, designFont);
1346 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1347 goto Error;
1348 }
bungeman@google.coma0319f62012-04-18 15:40:50 +00001349 glyphCount = calculateOutlineGlyphCount(hdc);
reed@google.comac6b9792011-03-11 15:42:51 +00001350
1351 info = new SkAdvancedTypefaceMetrics;
1352 info->fEmSize = otm.otmEMSquare;
1353 info->fMultiMaster = false;
1354 info->fLastGlyphID = SkToU16(glyphCount - 1);
1355 info->fStyle = 0;
bungeman@google.come70f7982012-06-01 19:38:19 +00001356 logfont_to_name(lf, &info->fFontName);
reed@google.comac6b9792011-03-11 15:42:51 +00001357
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001358 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1359 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1360 }
1361
vandebo@chromium.orgd41e70d2012-03-08 19:41:01 +00001362 if (glyphCount > 0 &&
1363 (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001364 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1365 } else {
1366 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1367 info->fItalicAngle = 0;
1368 info->fAscent = 0;
1369 info->fDescent = 0;
1370 info->fStemV = 0;
1371 info->fCapHeight = 0;
1372 info->fBBox = SkIRect::MakeEmpty();
1373 return info;
1374 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001375
reed@google.comac6b9792011-03-11 15:42:51 +00001376 // If this bit is clear the font is a fixed pitch font.
1377 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1378 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1379 }
1380 if (otm.otmTextMetrics.tmItalic) {
1381 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1382 }
1383 // Setting symbolic style by default for now.
1384 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
1385 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1386 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1387 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1388 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1389 }
1390
1391 // The main italic angle of the font, in tenths of a degree counterclockwise
1392 // from vertical.
1393 info->fItalicAngle = otm.otmItalicAngle / 10;
1394 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1395 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1396 // TODO(ctguil): Use alternate cap height calculation.
1397 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1398 // my Win7 box.
1399 info->fCapHeight = otm.otmsCapEmHeight;
1400 info->fBBox =
1401 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1402 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1403
1404 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1405 // This probably isn't very good with an italic font.
reed@google.com05b6f3a2011-11-28 15:30:28 +00001406 min_width = SHRT_MAX;
reed@google.comac6b9792011-03-11 15:42:51 +00001407 info->fStemV = 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001408 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1409 ABC abcWidths;
1410 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1411 int16_t width = abcWidths.abcB;
1412 if (width > 0 && width < min_width) {
1413 min_width = width;
1414 info->fStemV = min_width;
1415 }
1416 }
1417 }
1418
1419 // If bit 1 is set, the font may not be embedded in a document.
1420 // If bit 1 is clear, the font can be embedded.
1421 // If bit 2 is set, the embedding is read-only.
1422 if (otm.otmfsType & 0x1) {
1423 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
ctguil@chromium.org0e6dc0a2011-03-30 20:41:16 +00001424 } else if (perGlyphInfo &
1425 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001426 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1427 appendRange(&info->fGlyphWidths, 0);
1428 info->fGlyphWidths->fAdvance.append(1, &min_width);
1429 finishRange(info->fGlyphWidths.get(), 0,
1430 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1431 } else {
1432 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001433 getAdvanceData(hdc,
1434 glyphCount,
1435 glyphIDs,
1436 glyphIDsCount,
1437 &getWidthAdvance));
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001438 }
reed@google.comac6b9792011-03-11 15:42:51 +00001439 }
1440
1441Error:
1442 SelectObject(hdc, savefont);
1443 DeleteObject(designFont);
1444 DeleteObject(font);
1445 DeleteDC(hdc);
1446
1447 return info;
1448}
1449
bungeman@google.coma5501992012-05-18 19:06:41 +00001450//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
1451#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
1452//Length of GUID representation from create_id, including NULL terminator.
1453#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
reed@google.comac6b9792011-03-11 15:42:51 +00001454
bungeman@google.coma5501992012-05-18 19:06:41 +00001455SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
1456
1457/**
1458 NameID 6 Postscript names cannot have the character '/'.
1459 It would be easier to hex encode the GUID, but that is 32 bytes,
1460 and many systems have issues with names longer than 28 bytes.
1461 The following need not be any standard base64 encoding.
1462 The encoded value is never decoded.
1463*/
1464static const char postscript_safe_base64_encode[] =
1465 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1466 "abcdefghijklmnopqrstuvwxyz"
1467 "0123456789-_=";
1468
1469/**
1470 Formats a GUID into Base64 and places it into buffer.
1471 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1472 The string will always be null terminated.
1473 XXXXXXXXXXXXXXXXXXXXXXXX0
1474 */
1475static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
1476 SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
1477 size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
1478 SkASSERT(written < LF_FACESIZE);
1479 buffer[written] = '\0';
1480}
1481
1482/**
1483 Creates a Base64 encoded GUID and places it into buffer.
1484 buffer should have space for at least BASE64_GUID_ID_LEN characters.
1485 The string will always be null terminated.
1486 XXXXXXXXXXXXXXXXXXXXXXXX0
1487 */
1488static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
1489 GUID guid = {};
1490 if (FAILED(CoCreateGuid(&guid))) {
1491 return E_UNEXPECTED;
1492 }
1493 format_guid_b64(guid, buffer, bufferSize);
1494
1495 return S_OK;
1496}
1497
1498/**
1499 Introduces a font to GDI. On failure will return NULL. The returned handle
1500 should eventually be passed to RemoveFontMemResourceEx.
1501*/
1502static HANDLE activate_font(SkData* fontData) {
1503 DWORD numFonts = 0;
1504 //AddFontMemResourceEx just copies the data, but does not specify const.
1505 HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
1506 fontData->size(),
1507 0,
1508 &numFonts);
1509
1510 if (fontHandle != NULL && numFonts < 1) {
1511 RemoveFontMemResourceEx(fontHandle);
1512 return NULL;
1513 }
1514
1515 return fontHandle;
1516}
1517
bungeman@google.coma5501992012-05-18 19:06:41 +00001518SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1519 // Create a unique and unpredictable font name.
1520 // Avoids collisions and access from CSS.
1521 char familyName[BASE64_GUID_ID_LEN];
1522 const int familyNameSize = SK_ARRAY_COUNT(familyName);
1523 if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
1524 return NULL;
1525 }
1526
1527 // Change the name of the font.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001528 SkAutoTUnref<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
1529 if (NULL == rewrittenFontData.get()) {
bungeman@google.coma5501992012-05-18 19:06:41 +00001530 return NULL;
1531 }
bungeman@google.coma5501992012-05-18 19:06:41 +00001532
1533 // Register the font with GDI.
bungeman@google.come9bbee32012-05-21 13:46:13 +00001534 HANDLE fontReference = activate_font(rewrittenFontData.get());
bungeman@google.coma5501992012-05-18 19:06:41 +00001535 if (NULL == fontReference) {
1536 return NULL;
1537 }
1538
1539 // Create the typeface.
1540 LOGFONT lf;
1541 logfont_for_name(familyName, lf);
1542
1543 return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
reed@google.comac6b9792011-03-11 15:42:51 +00001544}
1545
1546SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001547 const DWORD kTTCTag =
1548 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com59d2f632011-05-02 19:36:59 +00001549 LOGFONT lf;
1550 GetLogFontByID(uniqueID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001551
1552 HDC hdc = ::CreateCompatibleDC(NULL);
reed@google.com59d2f632011-05-02 19:36:59 +00001553 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001554 HFONT savefont = (HFONT)SelectObject(hdc, font);
1555
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001556 SkMemoryStream* stream = NULL;
1557 DWORD tables[2] = {kTTCTag, 0};
1558 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
1559 size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001560 if (bufferSize == GDI_ERROR) {
1561 ensure_typeface_accessible(uniqueID);
1562 bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
1563 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001564 if (bufferSize != GDI_ERROR) {
1565 stream = new SkMemoryStream(bufferSize);
1566 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(),
1567 bufferSize)) {
1568 break;
1569 } else {
1570 delete stream;
1571 stream = NULL;
1572 }
1573 }
reed@google.comac6b9792011-03-11 15:42:51 +00001574 }
1575
1576 SelectObject(hdc, savefont);
1577 DeleteObject(font);
1578 DeleteDC(hdc);
1579
1580 return stream;
1581}
1582
1583SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
1584 return SkNEW_ARGS(SkScalerContext_Windows, (desc));
1585}
1586
1587/** Return the closest matching typeface given either an existing family
1588 (specified by a typeface in that family) or by a familyName, and a
1589 requested style.
bungeman@google.com90d812b2011-10-24 21:25:01 +00001590 1) If familyFace is null, use familyName.
1591 2) If familyName is null, use familyFace.
reed@google.comac6b9792011-03-11 15:42:51 +00001592 3) If both are null, return the default font that best matches style
1593 This MUST not return NULL.
1594 */
1595
1596SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
1597 const char familyName[],
reed@google.comac6b9792011-03-11 15:42:51 +00001598 SkTypeface::Style style) {
reed@google.com59d2f632011-05-02 19:36:59 +00001599 LOGFONT lf;
reed@google.comac6b9792011-03-11 15:42:51 +00001600 if (NULL == familyFace && NULL == familyName) {
reed@google.com59d2f632011-05-02 19:36:59 +00001601 lf = get_default_font();
1602 } else if (familyFace) {
1603 LogFontTypeface* face = (LogFontTypeface*)familyFace;
1604 lf = face->fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001605 } else {
bungeman@google.coma5501992012-05-18 19:06:41 +00001606 logfont_for_name(familyName, lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001607 }
reed@google.com59d2f632011-05-02 19:36:59 +00001608 setStyle(&lf, style);
1609 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001610}
1611
reed@google.comac6b9792011-03-11 15:42:51 +00001612SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1613 printf("SkFontHost::CreateTypefaceFromFile unimplemented");
1614 return NULL;
1615}
1616
1617void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
reed@google.come8fab012011-07-13 15:25:33 +00001618 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1619 SkScalerContext::kAutohinting_Flag |
1620 SkScalerContext::kEmbeddedBitmapText_Flag |
1621 SkScalerContext::kEmbolden_Flag |
bungeman@google.comc84547a2012-01-05 20:18:06 +00001622 SkScalerContext::kSubpixelPositioning_Flag |
reed@google.come8fab012011-07-13 15:25:33 +00001623 SkScalerContext::kLCD_BGROrder_Flag |
1624 SkScalerContext::kLCD_Vertical_Flag;
1625 rec->fFlags &= ~flagsWeDontSupport;
1626
reed@google.come8fab012011-07-13 15:25:33 +00001627 SkPaint::Hinting h = rec->getHinting();
reed@google.comda440672011-07-13 18:02:28 +00001628
1629 // I think we can support no-hinting, if we get hires outlines and just
1630 // use skia to rasterize into a gray-scale mask...
1631#if 0
reed@google.come8fab012011-07-13 15:25:33 +00001632 switch (h) {
1633 case SkPaint::kNo_Hinting:
1634 case SkPaint::kSlight_Hinting:
1635 h = SkPaint::kNo_Hinting;
1636 break;
1637 case SkPaint::kNormal_Hinting:
1638 case SkPaint::kFull_Hinting:
1639 h = SkPaint::kNormal_Hinting;
1640 break;
1641 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001642 SkDEBUGFAIL("unknown hinting");
reed@google.come8fab012011-07-13 15:25:33 +00001643 }
reed@google.comda440672011-07-13 18:02:28 +00001644#else
1645 h = SkPaint::kNormal_Hinting;
1646#endif
reed@google.come8fab012011-07-13 15:25:33 +00001647 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00001648
reed@google.comffe49f52011-11-22 19:42:41 +00001649 // for compatibility at the moment, discretize luminance to 3 settings
1650 // black, white, gray. This helps with fontcache utilization, since we
1651 // won't create multiple entries that in the end map to the same results.
1652 {
1653 unsigned lum = rec->getLuminanceByte();
1654 if (lum <= BLACK_LUMINANCE_LIMIT) {
1655 lum = 0;
1656 } else if (lum >= WHITE_LUMINANCE_LIMIT) {
1657 lum = SkScalerContext::kLuminance_Max;
1658 } else {
1659 lum = SkScalerContext::kLuminance_Max >> 1;
1660 }
1661 rec->setLuminanceBits(lum);
1662 }
1663
reed@google.com9181aa82011-08-05 14:28:31 +00001664// turn this off since GDI might turn A8 into BW! Need a bigger fix.
1665#if 0
reed@google.comda440672011-07-13 18:02:28 +00001666 // Disable LCD when rotated, since GDI's output is ugly
1667 if (isLCD(*rec) && !isAxisAligned(*rec)) {
1668 rec->fMaskFormat = SkMask::kA8_Format;
1669 }
reed@google.com9181aa82011-08-05 14:28:31 +00001670#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00001671
1672#if 0
1673 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
1674 rec->fMaskFormat = SkMask::kLCD32_Format;
1675 }
1676#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00001677}