blob: bce43a1e453d274ed211d136cc5462577954649a [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
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@google.com6f5df482011-09-28 20:33:24 +000010#include "SkColorFilter.h"
reed@google.comac6b9792011-03-11 15:42:51 +000011#include "SkString.h"
reed@google.comac6b9792011-03-11 15:42:51 +000012#include "SkEndian.h"
13#include "SkFontHost.h"
14#include "SkDescriptor.h"
15#include "SkAdvancedTypefaceMetrics.h"
16#include "SkStream.h"
17#include "SkThread.h"
18#include "SkTypeface_win.h"
reed@google.com59d2f632011-05-02 19:36:59 +000019#include "SkTypefaceCache.h"
reed@google.comac6b9792011-03-11 15:42:51 +000020#include "SkUtils.h"
21
22#ifdef WIN32
23#include "windows.h"
24#include "tchar.h"
25#include "Usp10.h"
26
reed@google.com6f5df482011-09-28 20:33:24 +000027// always packed xxRRGGBB
28typedef uint32_t SkGdiRGB;
29
30template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
31 return (T*)((char*)ptr + byteOffset);
32}
33
reed@google.coma767fa02011-08-05 21:40:26 +000034// define this in your Makefile or .gyp to enforce AA requests
35// which GDI ignores at small sizes. This flag guarantees AA
36// for rotated text, regardless of GDI's notions.
37//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
38
reed@google.comac6b9792011-03-11 15:42:51 +000039// client3d has to undefine this for now
40#define CAN_USE_LOGFONT_NAME
41
reed@google.com82a34d82011-07-26 19:33:08 +000042static bool isLCD(const SkScalerContext::Rec& rec) {
43 return SkMask::kLCD16_Format == rec.fMaskFormat ||
44 SkMask::kLCD32_Format == rec.fMaskFormat;
45}
46
reed@google.coma767fa02011-08-05 21:40:26 +000047static bool bothZero(SkScalar a, SkScalar b) {
48 return 0 == a && 0 == b;
49}
50
51// returns false if there is any non-90-rotation or skew
52static bool isAxisAligned(const SkScalerContext::Rec& rec) {
53 return 0 == rec.fPreSkewX &&
54 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
55 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
56}
57
58static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) {
59#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS
60 // What we really want to catch is when GDI will ignore the AA request and give
61 // us BW instead. Smallish rotated text is one heuristic, so this code is just
62 // an approximation. We shouldn't need to do this for larger sizes, but at those
63 // sizes, the quality difference gets less and less between our general
64 // scanconverter and GDI's.
65 if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) {
66 return true;
67 }
68#endif
69 // false means allow GDI to generate the bits
70 return false;
71}
72
reed@google.comac6b9792011-03-11 15:42:51 +000073using namespace skia_advanced_typeface_metrics_utils;
74
reed@google.comac6b9792011-03-11 15:42:51 +000075static const uint16_t BUFFERSIZE = (16384 - 32);
76static uint8_t glyphbuf[BUFFERSIZE];
77
reed@google.comac6b9792011-03-11 15:42:51 +000078/**
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +000079 * Since LOGFONT wants its textsize as an int, and we support fractional sizes,
reed@google.comac6b9792011-03-11 15:42:51 +000080 * and since we have a cache of LOGFONTs for our tyepfaces, we always set the
81 * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the
82 * actual requested size.
83 */
84static const int gCanonicalTextSize = 64;
85
86static void make_canonical(LOGFONT* lf) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +000087 lf->lfHeight = -gCanonicalTextSize;
reed@google.com59d2f632011-05-02 19:36:59 +000088 lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
89 lf->lfCharSet = DEFAULT_CHARSET;
reed@google.com82a34d82011-07-26 19:33:08 +000090// lf->lfClipPrecision = 64;
reed@google.com59d2f632011-05-02 19:36:59 +000091}
92
bungeman@google.com90d812b2011-10-24 21:25:01 +000093static SkTypeface::Style get_style(const LOGFONT& lf) {
reed@google.com59d2f632011-05-02 19:36:59 +000094 unsigned style = 0;
95 if (lf.lfWeight >= FW_BOLD) {
96 style |= SkTypeface::kBold;
97 }
98 if (lf.lfItalic) {
99 style |= SkTypeface::kItalic;
100 }
bungeman@google.com90d812b2011-10-24 21:25:01 +0000101 return static_cast<SkTypeface::Style>(style);
reed@google.com59d2f632011-05-02 19:36:59 +0000102}
103
104static void setStyle(LOGFONT* lf, SkTypeface::Style style) {
105 lf->lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
106 lf->lfItalic = ((style & SkTypeface::kItalic) != 0);
reed@google.comac6b9792011-03-11 15:42:51 +0000107}
108
109static inline FIXED SkFixedToFIXED(SkFixed x) {
110 return *(FIXED*)(&x);
111}
112
113static inline FIXED SkScalarToFIXED(SkScalar x) {
114 return SkFixedToFIXED(SkScalarToFixed(x));
115}
116
117static unsigned calculateGlyphCount(HDC hdc) {
118 // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +0000119 const DWORD maxpTag =
120 SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
reed@google.comac6b9792011-03-11 15:42:51 +0000121 uint16_t glyphs;
122 if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
123 return SkEndian_SwapBE16(glyphs);
124 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000125
reed@google.comac6b9792011-03-11 15:42:51 +0000126 // Binary search for glyph count.
127 static const MAT2 mat2 = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
128 int32_t max = SK_MaxU16 + 1;
129 int32_t min = 0;
130 GLYPHMETRICS gm;
131 while (min < max) {
132 int32_t mid = min + ((max - min) / 2);
133 if (GetGlyphOutlineW(hdc, mid, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0,
134 NULL, &mat2) == GDI_ERROR) {
135 max = mid;
136 } else {
137 min = mid + 1;
138 }
139 }
140 SkASSERT(min == max);
141 return min;
142}
143
reed@google.comac6b9792011-03-11 15:42:51 +0000144class LogFontTypeface : public SkTypeface {
reed@google.comac6b9792011-03-11 15:42:51 +0000145public:
reed@google.com59d2f632011-05-02 19:36:59 +0000146 LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
147 SkTypeface(style, fontID, false), fLogFont(lf) {}
reed@google.comac6b9792011-03-11 15:42:51 +0000148
reed@google.com59d2f632011-05-02 19:36:59 +0000149 LOGFONT fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000150
reed@google.com59d2f632011-05-02 19:36:59 +0000151 static LogFontTypeface* Create(const LOGFONT& lf) {
bungeman@google.com90d812b2011-10-24 21:25:01 +0000152 SkTypeface::Style style = get_style(lf);
reed@google.com59d2f632011-05-02 19:36:59 +0000153 SkFontID fontID = SkTypefaceCache::NewFontID();
154 return new LogFontTypeface(style, fontID, lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000155 }
156};
157
reed@google.comac6b9792011-03-11 15:42:51 +0000158static const LOGFONT& get_default_font() {
159 static LOGFONT gDefaultFont;
reed@google.comac6b9792011-03-11 15:42:51 +0000160 return gDefaultFont;
161}
162
reed@google.com59d2f632011-05-02 19:36:59 +0000163static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
164 LogFontTypeface* lface = reinterpret_cast<LogFontTypeface*>(face);
165 const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
reed@google.comac6b9792011-03-11 15:42:51 +0000166
bungeman@google.com90d812b2011-10-24 21:25:01 +0000167 return get_style(lface->fLogFont) == requestedStyle &&
reed@google.com59d2f632011-05-02 19:36:59 +0000168 !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
169}
170
171/**
172 * This guy is public. It first searches the cache, and if a match is not found,
173 * it creates a new face.
174 */
175SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
176 LOGFONT lf = origLF;
177 make_canonical(&lf);
178 SkTypeface* face = SkTypefaceCache::FindByProc(FindByLogFont, &lf);
179 if (face) {
180 face->ref();
reed@google.comac6b9792011-03-11 15:42:51 +0000181 } else {
reed@google.com59d2f632011-05-02 19:36:59 +0000182 face = LogFontTypeface::Create(lf);
bungeman@google.com90d812b2011-10-24 21:25:01 +0000183 SkTypefaceCache::Add(face, get_style(lf));
reed@google.com59d2f632011-05-02 19:36:59 +0000184 }
185 return face;
reed@google.comac6b9792011-03-11 15:42:51 +0000186}
187
reed@google.comdb77a6a2011-07-19 19:08:33 +0000188/**
189 * This guy is public
190 */
191void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
192 if (NULL == face) {
193 *lf = get_default_font();
194 } else {
195 *lf = ((const LogFontTypeface*)face)->fLogFont;
196 }
197}
198
reed@google.com7d26c592011-06-13 13:01:10 +0000199SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@google.comac6b9792011-03-11 15:42:51 +0000200 // Zero means that we don't have any fallback fonts for this fontID.
201 // This function is implemented on Android, but doesn't have much
202 // meaning here.
203 return 0;
204}
205
bungeman@google.com39698b12011-11-15 22:26:41 +0000206static void ensure_typeface_accessible(SkFontID fontID) {
207 LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
208 SkFontHost::EnsureTypefaceAccessible(*face);
209}
210
reed@google.com59d2f632011-05-02 19:36:59 +0000211static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
212 LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
213 if (face) {
214 *lf = face->fLogFont;
215 } else {
216 sk_bzero(lf, sizeof(LOGFONT));
217 }
218}
219
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000220// Construct Glyph to Unicode table.
221// Unicode code points that require conjugate pairs in utf16 are not
222// supported.
223// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
224// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
225// of calling GetFontUnicodeRange().
226static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount,
227 SkTDArray<SkUnichar>* glyphToUnicode) {
228 DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, NULL);
229 if (!glyphSetBufferSize) {
230 return;
231 }
232
233 SkAutoTDeleteArray<BYTE> glyphSetBuffer(new BYTE[glyphSetBufferSize]);
234 GLYPHSET* glyphSet =
235 reinterpret_cast<LPGLYPHSET>(glyphSetBuffer.get());
236 if (GetFontUnicodeRanges(fontHdc, glyphSet) != glyphSetBufferSize) {
237 return;
238 }
239
240 glyphToUnicode->setCount(glyphCount);
241 memset(glyphToUnicode->begin(), 0, glyphCount * sizeof(SkUnichar));
242 for (DWORD i = 0; i < glyphSet->cRanges; ++i) {
243 // There is no guarantee that within a Unicode range, the corresponding
244 // glyph id in a font file are continuous. So, even if we have ranges,
245 // we can't just use the first and last entry of the range to compute
246 // result. We need to enumerate them one by one.
247 int count = glyphSet->ranges[i].cGlyphs;
248 SkAutoTArray<WCHAR> chars(count + 1);
249 chars[count] = 0; // termintate string
250 SkAutoTArray<WORD> glyph(count);
251 for (USHORT j = 0; j < count; ++j) {
252 chars[j] = glyphSet->ranges[i].wcLow + j;
253 }
254 GetGlyphIndicesW(fontHdc, chars.get(), count, glyph.get(),
255 GGI_MARK_NONEXISTING_GLYPHS);
256 // If the glyph ID is valid, and the glyph is not mapped, then we will
257 // fill in the char id into the vector. If the glyph is mapped already,
258 // skip it.
259 // TODO(arthurhsu): better improve this. e.g. Get all used char ids from
260 // font cache, then generate this mapping table from there. It's
261 // unlikely to have collisions since glyph reuse happens mostly for
262 // different Unicode pages.
263 for (USHORT j = 0; j < count; ++j) {
264 if (glyph[j] != 0xffff && glyph[j] < glyphCount &&
265 (*glyphToUnicode)[glyph[j]] == 0) {
266 (*glyphToUnicode)[glyph[j]] = chars[j];
267 }
268 }
269 }
270}
271
reed@google.com99edd432011-09-09 14:59:59 +0000272//////////////////////////////////////////////////////////////////////////////////////
273
274static int alignTo32(int n) {
275 return (n + 31) & ~31;
276}
277
278struct MyBitmapInfo : public BITMAPINFO {
279 RGBQUAD fMoreSpaceForColors[1];
280};
281
282class HDCOffscreen {
283public:
284 HDCOffscreen() {
285 fFont = 0;
286 fDC = 0;
287 fBM = 0;
288 fBits = NULL;
289 fWidth = fHeight = 0;
290 fIsBW = false;
reed@google.com754e4eb2011-09-26 13:21:39 +0000291 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000292 }
293
294 ~HDCOffscreen() {
295 if (fDC) {
296 DeleteDC(fDC);
297 }
298 if (fBM) {
299 DeleteObject(fBM);
300 }
301 }
302
303 void init(HFONT font, const XFORM& xform) {
304 fFont = font;
305 fXform = xform;
306 }
307
reed@google.com6f5df482011-09-28 20:33:24 +0000308 const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
reed@google.com754e4eb2011-09-26 13:21:39 +0000309 size_t* srcRBPtr);
reed@google.com99edd432011-09-09 14:59:59 +0000310
311private:
312 HDC fDC;
313 HBITMAP fBM;
314 HFONT fFont;
315 XFORM fXform;
316 void* fBits; // points into fBM
reed@google.com754e4eb2011-09-26 13:21:39 +0000317 COLORREF fColor;
reed@google.com99edd432011-09-09 14:59:59 +0000318 int fWidth;
319 int fHeight;
320 bool fIsBW;
reed@google.com754e4eb2011-09-26 13:21:39 +0000321
322 enum {
323 // will always trigger us to reset the color, since we
reed@google.com6f5df482011-09-28 20:33:24 +0000324 // should only store 0 or 0x00FFFFFF or gray (0x007F7F7F)
reed@google.com754e4eb2011-09-26 13:21:39 +0000325 kInvalid_Color = 12345
326 };
reed@google.com99edd432011-09-09 14:59:59 +0000327};
328
reed@google.com754e4eb2011-09-26 13:21:39 +0000329const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
reed@google.com6f5df482011-09-28 20:33:24 +0000330 SkGdiRGB fgColor, size_t* srcRBPtr) {
reed@google.com99edd432011-09-09 14:59:59 +0000331 if (0 == fDC) {
332 fDC = CreateCompatibleDC(0);
333 if (0 == fDC) {
334 return NULL;
335 }
336 SetGraphicsMode(fDC, GM_ADVANCED);
337 SetBkMode(fDC, TRANSPARENT);
338 SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
339 SelectObject(fDC, fFont);
reed@google.com754e4eb2011-09-26 13:21:39 +0000340 fColor = kInvalid_Color;
reed@google.com99edd432011-09-09 14:59:59 +0000341 }
342
343 if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
344 DeleteObject(fBM);
345 fBM = 0;
346 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000347 fIsBW = isBW;
reed@google.com99edd432011-09-09 14:59:59 +0000348
reed@google.com6f5df482011-09-28 20:33:24 +0000349 COLORREF color = fgColor;
350 if (fIsBW) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000351 color = 0xFFFFFF;
reed@google.com99edd432011-09-09 14:59:59 +0000352 }
reed@google.com754e4eb2011-09-26 13:21:39 +0000353 if (fColor != color) {
354 fColor = color;
355 COLORREF prev = SetTextColor(fDC, color);
356 SkASSERT(prev != CLR_INVALID);
357 }
358
reed@google.com99edd432011-09-09 14:59:59 +0000359 fWidth = SkMax32(fWidth, glyph.fWidth);
360 fHeight = SkMax32(fHeight, glyph.fHeight);
361
362 int biWidth = isBW ? alignTo32(fWidth) : fWidth;
363
364 if (0 == fBM) {
365 MyBitmapInfo info;
366 sk_bzero(&info, sizeof(info));
367 if (isBW) {
368 RGBQUAD blackQuad = { 0, 0, 0, 0 };
369 RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
370 info.bmiColors[0] = blackQuad;
371 info.bmiColors[1] = whiteQuad;
372 }
373 info.bmiHeader.biSize = sizeof(info.bmiHeader);
374 info.bmiHeader.biWidth = biWidth;
375 info.bmiHeader.biHeight = fHeight;
376 info.bmiHeader.biPlanes = 1;
377 info.bmiHeader.biBitCount = isBW ? 1 : 32;
378 info.bmiHeader.biCompression = BI_RGB;
379 if (isBW) {
380 info.bmiHeader.biClrUsed = 2;
381 }
382 fBM = CreateDIBSection(fDC, &info, DIB_RGB_COLORS, &fBits, 0, 0);
383 if (0 == fBM) {
384 return NULL;
385 }
386 SelectObject(fDC, fBM);
387 }
388
389 // erase
390 size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
391 size_t size = fHeight * srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +0000392 unsigned bg = (0 == color) ? 0xFF : 0;
393 memset(fBits, bg, size);
reed@google.com99edd432011-09-09 14:59:59 +0000394
395 XFORM xform = fXform;
396 xform.eDx = (float)-glyph.fLeft;
397 xform.eDy = (float)-glyph.fTop;
398 SetWorldTransform(fDC, &xform);
399
400 uint16_t glyphID = glyph.getGlyphID();
bungeman@google.com39698b12011-11-15 22:26:41 +0000401 BOOL ret = ExtTextOutW(fDC, 0, 0, ETO_GLYPH_INDEX, NULL, reinterpret_cast<LPCWSTR>(&glyphID), 1, NULL);
reed@google.com99edd432011-09-09 14:59:59 +0000402 GdiFlush();
bungeman@google.com39698b12011-11-15 22:26:41 +0000403 if (0 == ret) {
404 return NULL;
405 }
reed@google.com99edd432011-09-09 14:59:59 +0000406 *srcRBPtr = srcRB;
407 // offset to the start of the image
408 return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
409}
410
411//////////////////////////////////////////////////////////////////////////////////////
reed@google.com59d2f632011-05-02 19:36:59 +0000412
reed@google.comac6b9792011-03-11 15:42:51 +0000413class SkScalerContext_Windows : public SkScalerContext {
414public:
415 SkScalerContext_Windows(const SkDescriptor* desc);
416 virtual ~SkScalerContext_Windows();
417
418protected:
419 virtual unsigned generateGlyphCount();
420 virtual uint16_t generateCharToGlyph(SkUnichar uni);
421 virtual void generateAdvance(SkGlyph* glyph);
422 virtual void generateMetrics(SkGlyph* glyph);
423 virtual void generateImage(const SkGlyph& glyph);
424 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
425 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
reed@google.com99edd432011-09-09 14:59:59 +0000426
reed@google.comac6b9792011-03-11 15:42:51 +0000427private:
reed@google.com99edd432011-09-09 14:59:59 +0000428 HDCOffscreen fOffscreen;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000429 SkScalar fScale; // to get from canonical size to real size
reed@google.comac6b9792011-03-11 15:42:51 +0000430 MAT2 fMat22;
431 XFORM fXform;
432 HDC fDDC;
433 HFONT fSavefont;
434 HFONT fFont;
435 SCRIPT_CACHE fSC;
436 int fGlyphCount;
reed@google.com1dd17a12011-05-17 14:04:41 +0000437
438 HFONT fHiResFont;
439 MAT2 fMat22Identity;
440 SkMatrix fHiResMatrix;
reed@google.comac6b9792011-03-11 15:42:51 +0000441};
442
443static float mul2float(SkScalar a, SkScalar b) {
444 return SkScalarToFloat(SkScalarMul(a, b));
445}
446
447static FIXED float2FIXED(float x) {
448 return SkFixedToFIXED(SkFloatToFixed(x));
449}
450
reed@google.com59d2f632011-05-02 19:36:59 +0000451static SkMutex gFTMutex;
452
reed@google.com1dd17a12011-05-17 14:04:41 +0000453#define HIRES_TEXTSIZE 2048
454#define HIRES_SHIFT 11
455static inline SkFixed HiResToFixed(int value) {
456 return value << (16 - HIRES_SHIFT);
457}
458
459static bool needHiResMetrics(const SkScalar mat[2][2]) {
460 return mat[1][0] || mat[0][1];
461}
462
reed@google.com82a34d82011-07-26 19:33:08 +0000463static BYTE compute_quality(const SkScalerContext::Rec& rec) {
464 switch (rec.fMaskFormat) {
465 case SkMask::kBW_Format:
466 return NONANTIALIASED_QUALITY;
467 case SkMask::kLCD16_Format:
468 case SkMask::kLCD32_Format:
469 return CLEARTYPE_QUALITY;
470 default:
reed@google.comb6bb5cb2011-11-21 19:32:29 +0000471 return ANTIALIASED_QUALITY;
reed@google.com82a34d82011-07-26 19:33:08 +0000472 }
473}
474
reed@google.comac6b9792011-03-11 15:42:51 +0000475SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
476 : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
477 , fGlyphCount(-1) {
478 SkAutoMutexAcquire ac(gFTMutex);
479
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000480 fScale = fRec.fTextSize / gCanonicalTextSize;
reed@google.comac6b9792011-03-11 15:42:51 +0000481
482 fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
483 fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
484 fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
485 fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
486 fXform.eDx = 0;
487 fXform.eDy = 0;
488
489 fMat22.eM11 = float2FIXED(fXform.eM11);
490 fMat22.eM12 = float2FIXED(fXform.eM12);
491 fMat22.eM21 = float2FIXED(-fXform.eM21);
492 fMat22.eM22 = float2FIXED(-fXform.eM22);
493
494 fDDC = ::CreateCompatibleDC(NULL);
reed@google.com1dd17a12011-05-17 14:04:41 +0000495 SetGraphicsMode(fDDC, GM_ADVANCED);
reed@google.comac6b9792011-03-11 15:42:51 +0000496 SetBkMode(fDDC, TRANSPARENT);
497
498 // Scaling by the DPI is inconsistent with how Skia draws elsewhere
499 //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
reed@google.com59d2f632011-05-02 19:36:59 +0000500 LOGFONT lf;
501 GetLogFontByID(fRec.fFontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +0000502 lf.lfHeight = -gCanonicalTextSize;
reed@google.com82a34d82011-07-26 19:33:08 +0000503 lf.lfQuality = compute_quality(fRec);
reed@google.comac6b9792011-03-11 15:42:51 +0000504 fFont = CreateFontIndirect(&lf);
reed@google.com1dd17a12011-05-17 14:04:41 +0000505
506 // if we're rotated, or want fractional widths, create a hires font
507 fHiResFont = 0;
508 if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) {
509 lf.lfHeight = -HIRES_TEXTSIZE;
510 fHiResFont = CreateFontIndirect(&lf);
511
512 fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
513 fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
514
515 // construct a matrix to go from HIRES logical units to our device units
516 fRec.getSingleMatrix(&fHiResMatrix);
517 SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
518 fHiResMatrix.preScale(scale, scale);
519 }
reed@google.comac6b9792011-03-11 15:42:51 +0000520 fSavefont = (HFONT)SelectObject(fDDC, fFont);
reed@google.coma767fa02011-08-05 21:40:26 +0000521
522 if (needToRenderWithSkia(fRec)) {
523 this->forceGenerateImageFromPath();
524 }
reed@google.com99edd432011-09-09 14:59:59 +0000525
526 fOffscreen.init(fFont, fXform);
reed@google.comac6b9792011-03-11 15:42:51 +0000527}
528
529SkScalerContext_Windows::~SkScalerContext_Windows() {
530 if (fDDC) {
531 ::SelectObject(fDDC, fSavefont);
532 ::DeleteDC(fDDC);
533 }
534 if (fFont) {
535 ::DeleteObject(fFont);
536 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000537 if (fHiResFont) {
538 ::DeleteObject(fHiResFont);
539 }
reed@google.comac6b9792011-03-11 15:42:51 +0000540 if (fSC) {
541 ::ScriptFreeCache(&fSC);
542 }
543}
544
545unsigned SkScalerContext_Windows::generateGlyphCount() {
546 if (fGlyphCount < 0) {
547 fGlyphCount = calculateGlyphCount(fDDC);
548 }
549 return fGlyphCount;
550}
551
552uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
553 uint16_t index = 0;
554 WCHAR c[2];
555 // TODO(ctguil): Support characters that generate more than one glyph.
556 if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
557 // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
558 SkAssertResult(GetGlyphIndicesW(fDDC, c, 1, &index, 0));
559 } else {
560 // Use uniscribe to detemine glyph index for non-BMP characters.
561 // Need to add extra item to SCRIPT_ITEM to work around a bug in older
562 // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
563 SCRIPT_ITEM si[2 + 1];
564 int items;
565 SkAssertResult(
566 SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
567
568 WORD log[2];
569 SCRIPT_VISATTR vsa;
570 int glyphs;
571 SkAssertResult(SUCCEEDED(ScriptShape(
572 fDDC, &fSC, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
573 }
574 return index;
575}
576
577void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
578 this->generateMetrics(glyph);
579}
580
581void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
582
583 SkASSERT(fDDC);
584
585 GLYPHMETRICS gm;
reed@google.com1dd17a12011-05-17 14:04:41 +0000586 sk_bzero(&gm, sizeof(gm));
reed@google.comac6b9792011-03-11 15:42:51 +0000587
588 glyph->fRsbDelta = 0;
589 glyph->fLsbDelta = 0;
590
591 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
592 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
593 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 +0000594 if (GDI_ERROR == ret) {
595 ensure_typeface_accessible(fRec.fFontID);
596 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
597 }
reed@google.comac6b9792011-03-11 15:42:51 +0000598
599 if (GDI_ERROR != ret) {
600 if (ret == 0) {
601 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
602 gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
603 }
604 glyph->fWidth = gm.gmBlackBoxX;
605 glyph->fHeight = gm.gmBlackBoxY;
606 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
607 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
608 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
609 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
610
reed@google.comf8cead52011-09-02 20:08:11 +0000611 // we outset in all dimensions, since the image may bleed outside
reed@google.comac6b9792011-03-11 15:42:51 +0000612 // of the computed bounds returned by GetGlyphOutline.
613 // This was deduced by trial and error for small text (e.g. 8pt), so there
614 // maybe a more precise way to make this adjustment...
reed@google.comf8cead52011-09-02 20:08:11 +0000615 //
616 // This test shows us clipping the tops of some of the CJK fonts unless we
617 // increase the top of the box by 2, hence the height by 4. This seems to
618 // correspond to an embedded bitmap font, but not sure.
619 // LayoutTests/fast/text/backslash-to-yen-sign-euc.html
620 //
reed@google.com5e2df642011-09-21 18:42:09 +0000621 if (glyph->fWidth) { // don't outset an empty glyph
622 glyph->fWidth += 4;
623 glyph->fHeight += 4;
624 glyph->fTop -= 2;
625 glyph->fLeft -= 2;
626 }
reed@google.com1dd17a12011-05-17 14:04:41 +0000627
628 if (fHiResFont) {
629 SelectObject(fDDC, fHiResFont);
630 sk_bzero(&gm, sizeof(gm));
631 ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
632 if (GDI_ERROR != ret) {
633 SkPoint advance;
634 fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
635 glyph->fAdvanceX = SkScalarToFixed(advance.fX);
636 glyph->fAdvanceY = SkScalarToFixed(advance.fY);
637 }
638 SelectObject(fDDC, fFont);
639 }
reed@google.comac6b9792011-03-11 15:42:51 +0000640 } else {
641 glyph->fWidth = 0;
642 }
643}
644
645void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
646// Note: This code was borrowed from generateLineHeight, which has a note
647// stating that it may be incorrect.
648 if (!(mx || my))
649 return;
650
651 SkASSERT(fDDC);
652
653 OUTLINETEXTMETRIC otm;
654
655 uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
bungeman@google.com39698b12011-11-15 22:26:41 +0000656 if (GDI_ERROR == ret) {
657 ensure_typeface_accessible(fRec.fFontID);
658 ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
659 }
reed@google.comac6b9792011-03-11 15:42:51 +0000660 if (sizeof(otm) != ret) {
661 return;
662 }
663
664 if (mx) {
665 mx->fTop = -fScale * otm.otmTextMetrics.tmAscent;
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000666 mx->fAscent = -fScale * otm.otmAscent;
667 mx->fDescent = -fScale * otm.otmDescent;
668 mx->fBottom = fScale * otm.otmTextMetrics.tmDescent;
669 mx->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
670 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000671 }
672
673 if (my) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +0000674 my->fTop = -fScale * otm.otmTextMetrics.tmAscent;
675 my->fAscent = -fScale * otm.otmAscent;
676 my->fDescent = -fScale * otm.otmDescent;
677 my->fBottom = fScale * otm.otmTextMetrics.tmDescent;
678 my->fLeading = fScale * (otm.otmTextMetrics.tmInternalLeading
679 + otm.otmTextMetrics.tmExternalLeading);
reed@google.comac6b9792011-03-11 15:42:51 +0000680 }
681}
682
reed@google.com7430a332011-10-03 14:37:38 +0000683////////////////////////////////////////////////////////////////////////////////////////
684
685static void build_power_table(uint8_t table[], float ee) {
686 for (int i = 0; i < 256; i++) {
687 float x = i / 255.f;
688 x = powf(x, ee);
689 int xx = SkScalarRound(SkFloatToScalar(x * 255));
690 table[i] = SkToU8(xx);
691 }
692}
693
694// This will invert the gamma applied by GDI, so we can sort-of get linear values.
695// Needed when we draw non-black, non-white text, and don't know how to bias it.
696static const uint8_t* getInverseGammaTable() {
697 static bool gInited;
698 static uint8_t gTable[256];
699 if (!gInited) {
700 UINT level = 0;
701 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
702 // can't get the data, so use a default
703 level = 1400;
704 }
705 build_power_table(gTable, level / 1000.0f);
706 gInited = true;
707 }
708 return gTable;
709}
710
reed@google.comac6b9792011-03-11 15:42:51 +0000711#include "SkColorPriv.h"
712
reed@google.com5e2df642011-09-21 18:42:09 +0000713// gdi's bitmap is upside-down, so we reverse dst walking in Y
714// whenever we copy it into skia's buffer
715
716static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
reed@google.com6f5df482011-09-28 20:33:24 +0000717 int r = (rgb >> 16) & 0xFF;
718 int g = (rgb >> 8) & 0xFF;
719 int b = (rgb >> 0) & 0xFF;
720
reed@google.com6fc3c1f2011-09-30 20:31:25 +0000721 return (r * 2 + g * 5 + b) >> 3; // luminance
reed@google.com82a34d82011-07-26 19:33:08 +0000722}
723
reed@google.com5e2df642011-09-21 18:42:09 +0000724static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
reed@google.comac6b9792011-03-11 15:42:51 +0000725 int r = (rgb >> 16) & 0xFF;
726 int g = (rgb >> 8) & 0xFF;
727 int b = (rgb >> 0) & 0xFF;
reed@google.comac6b9792011-03-11 15:42:51 +0000728 return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
729}
730
reed@google.com754e4eb2011-09-26 13:21:39 +0000731static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb) {
732 int r = (rgb >> 16) & 0xFF;
733 int g = (rgb >> 8) & 0xFF;
734 int b = (rgb >> 0) & 0xFF;
735 int a = SkMax32(r, SkMax32(g, b));
736 return SkPackARGB32(a, r, g, b);
737}
738
reed@google.com82cff022011-09-22 14:33:40 +0000739// Is this GDI color neither black nor white? If so, we have to keep this
740// image as is, rather than smashing it down to a BW mask.
741//
742// returns int instead of bool, since we don't want/have to pay to convert
743// the zero/non-zero value into a bool
744static int is_not_black_or_white(SkGdiRGB c) {
745 // same as (but faster than)
746 // c &= 0x00FFFFFF;
747 // return 0 == c || 0x00FFFFFF == c;
748 return (c + (c & 1)) & 0x00FFFFFF;
reed@google.com5e2df642011-09-21 18:42:09 +0000749}
750
751static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int srcRB) {
752 for (int y = 0; y < height; ++y) {
753 for (int x = 0; x < width; ++x) {
reed@google.com82cff022011-09-22 14:33:40 +0000754 if (is_not_black_or_white(src[x])) {
reed@google.com5e2df642011-09-21 18:42:09 +0000755 return false;
756 }
757 }
reed@google.com6f5df482011-09-28 20:33:24 +0000758 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000759 }
760 return true;
761}
762
763static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +0000764 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +0000765 const int width = glyph.fWidth;
766 const size_t dstRB = (width + 7) >> 3;
767 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
768
769 int byteCount = width >> 3;
770 int bitCount = width & 7;
771
772 // adjust srcRB to skip the values in our byteCount loop,
773 // since we increment src locally there
774 srcRB -= byteCount * 8 * sizeof(SkGdiRGB);
775
776 for (int y = 0; y < glyph.fHeight; ++y) {
777 if (byteCount > 0) {
reed@google.com5e2df642011-09-21 18:42:09 +0000778 for (int i = 0; i < byteCount; ++i) {
reed@google.com7a230142011-09-27 14:07:21 +0000779 unsigned byte = 0;
reed@google.com754e4eb2011-09-26 13:21:39 +0000780 byte |= (src[0] ^ xorMask) & (1 << 7);
781 byte |= (src[1] ^ xorMask) & (1 << 6);
782 byte |= (src[2] ^ xorMask) & (1 << 5);
783 byte |= (src[3] ^ xorMask) & (1 << 4);
784 byte |= (src[4] ^ xorMask) & (1 << 3);
785 byte |= (src[5] ^ xorMask) & (1 << 2);
786 byte |= (src[6] ^ xorMask) & (1 << 1);
787 byte |= (src[7] ^ xorMask) & (1 << 0);
reed@google.com6a8f14d2011-09-27 12:54:24 +0000788 dst[i] = byte;
reed@google.com5e2df642011-09-21 18:42:09 +0000789 src += 8;
790 }
791 }
792 if (bitCount > 0) {
793 unsigned byte = 0;
794 unsigned mask = 0x80;
795 for (int i = 0; i < bitCount; i++) {
reed@google.com6a8f14d2011-09-27 12:54:24 +0000796 byte |= (src[i] ^ xorMask) & mask;
reed@google.com5e2df642011-09-21 18:42:09 +0000797 mask >>= 1;
798 }
799 dst[byteCount] = byte;
800 }
reed@google.com6f5df482011-09-28 20:33:24 +0000801 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000802 dst -= dstRB;
803 }
804}
805
806static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +0000807 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +0000808 const size_t dstRB = glyph.rowBytes();
809 const int width = glyph.fWidth;
810 uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
811
812 for (int y = 0; y < glyph.fHeight; y++) {
813 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000814 dst[i] = rgb_to_a8(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +0000815 }
reed@google.com6f5df482011-09-28 20:33:24 +0000816 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000817 dst -= dstRB;
818 }
819}
820
821static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
reed@google.com754e4eb2011-09-26 13:21:39 +0000822 const SkGlyph& glyph, int32_t xorMask) {
reed@google.com5e2df642011-09-21 18:42:09 +0000823 const size_t dstRB = glyph.rowBytes();
824 const int width = glyph.fWidth;
825 uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
826
827 for (int y = 0; y < glyph.fHeight; y++) {
828 for (int i = 0; i < width; i++) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000829 dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
reed@google.com5e2df642011-09-21 18:42:09 +0000830 }
reed@google.com6f5df482011-09-28 20:33:24 +0000831 src = SkTAddByteOffset(src, srcRB);
reed@google.com5e2df642011-09-21 18:42:09 +0000832 dst = (uint16_t*)((char*)dst - dstRB);
833 }
834}
835
reed@google.com754e4eb2011-09-26 13:21:39 +0000836static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
837 const SkGlyph& glyph, int32_t xorMask) {
838 const size_t dstRB = glyph.rowBytes();
839 const int width = glyph.fWidth;
840 SkPMColor* SK_RESTRICT dst = (SkPMColor*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
841
842 for (int y = 0; y < glyph.fHeight; y++) {
843 for (int i = 0; i < width; i++) {
844 dst[i] = rgb_to_lcd32(src[i] ^ xorMask);
845 }
reed@google.com6f5df482011-09-28 20:33:24 +0000846 src = SkTAddByteOffset(src, srcRB);
reed@google.com754e4eb2011-09-26 13:21:39 +0000847 dst = (SkPMColor*)((char*)dst - dstRB);
848 }
849}
850
reed@google.com6f5df482011-09-28 20:33:24 +0000851static inline unsigned clamp255(unsigned x) {
852 SkASSERT(x <= 256);
853 return x - (x >> 8);
854}
855
reed@google.comac6b9792011-03-11 15:42:51 +0000856void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
reed@google.comac6b9792011-03-11 15:42:51 +0000857 SkAutoMutexAcquire ac(gFTMutex);
858
859 SkASSERT(fDDC);
860
reed@google.com62711172011-05-18 15:08:10 +0000861 const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
reed@google.com82a34d82011-07-26 19:33:08 +0000862 const bool isAA = !isLCD(fRec);
reed@google.com6f5df482011-09-28 20:33:24 +0000863
864 bool isWhite = SkToBool(fRec.fFlags & SkScalerContext::kGammaForWhite_Flag);
865 bool isBlack = SkToBool(fRec.fFlags & SkScalerContext::kGammaForBlack_Flag);
866 SkASSERT(!(isWhite && isBlack));
867 SkASSERT(!isBW || (!isWhite && !isBlack));
868
869 SkGdiRGB fgColor;
870 uint32_t rgbXOR;
reed@google.com7430a332011-10-03 14:37:38 +0000871 const uint8_t* table = NULL;
reed@google.com6f5df482011-09-28 20:33:24 +0000872 if (isBW || isWhite) {
873 fgColor = 0x00FFFFFF;
874 rgbXOR = 0;
875 } else if (isBlack) {
876 fgColor = 0;
877 rgbXOR = ~0;
878 } else {
reed@google.com7430a332011-10-03 14:37:38 +0000879 table = getInverseGammaTable();
880 fgColor = 0x00FFFFFF;
reed@google.com6f5df482011-09-28 20:33:24 +0000881 rgbXOR = 0;
reed@google.com6f5df482011-09-28 20:33:24 +0000882 }
reed@google.comac6b9792011-03-11 15:42:51 +0000883
reed@google.com99edd432011-09-09 14:59:59 +0000884 size_t srcRB;
reed@google.com6f5df482011-09-28 20:33:24 +0000885 const void* bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
bungeman@google.com39698b12011-11-15 22:26:41 +0000886 if (NULL == bits) {
887 ensure_typeface_accessible(fRec.fFontID);
888 bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
889 if (NULL == bits) {
890 sk_bzero(glyph.fImage, glyph.computeImageSize());
891 return;
892 }
reed@google.com82a34d82011-07-26 19:33:08 +0000893 }
reed@google.comac6b9792011-03-11 15:42:51 +0000894
reed@google.com7430a332011-10-03 14:37:38 +0000895 if (table) {
reed@google.com6f5df482011-09-28 20:33:24 +0000896 SkGdiRGB* addr = (SkGdiRGB*)bits;
897 for (int y = 0; y < glyph.fHeight; ++y) {
898 for (int x = 0; x < glyph.fWidth; ++x) {
899 int r = (addr[x] >> 16) & 0xFF;
900 int g = (addr[x] >> 8) & 0xFF;
901 int b = (addr[x] >> 0) & 0xFF;
reed@google.com7430a332011-10-03 14:37:38 +0000902 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
reed@google.com6f5df482011-09-28 20:33:24 +0000903 }
904 addr = SkTAddByteOffset(addr, srcRB);
905 }
906 }
907
reed@google.com82a34d82011-07-26 19:33:08 +0000908 int width = glyph.fWidth;
909 size_t dstRB = glyph.rowBytes();
910 if (isBW) {
911 const uint8_t* src = (const uint8_t*)bits;
reed@google.com82a34d82011-07-26 19:33:08 +0000912 uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
913 for (int y = 0; y < glyph.fHeight; y++) {
914 memcpy(dst, src, dstRB);
915 src += srcRB;
916 dst -= dstRB;
reed@google.comac6b9792011-03-11 15:42:51 +0000917 }
reed@google.com82a34d82011-07-26 19:33:08 +0000918 } else if (isAA) {
reed@google.com6f5df482011-09-28 20:33:24 +0000919 // since the caller may require A8 for maskfilters, we can't check for BW
920 // ... until we have the caller tell us that explicitly
reed@google.com5e2df642011-09-21 18:42:09 +0000921 const SkGdiRGB* src = (const SkGdiRGB*)bits;
reed@google.com6f5df482011-09-28 20:33:24 +0000922 rgb_to_a8(src, srcRB, glyph, rgbXOR);
reed@google.com82a34d82011-07-26 19:33:08 +0000923 } else { // LCD16
reed@google.com5e2df642011-09-21 18:42:09 +0000924 const SkGdiRGB* src = (const SkGdiRGB*)bits;
925 if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
reed@google.com754e4eb2011-09-26 13:21:39 +0000926 rgb_to_bw(src, srcRB, glyph, rgbXOR);
reed@google.com5e2df642011-09-21 18:42:09 +0000927 ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
928 } else {
reed@google.com754e4eb2011-09-26 13:21:39 +0000929 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
930 rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
931 } else {
932 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
933 rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
934 }
reed@google.comac6b9792011-03-11 15:42:51 +0000935 }
936 }
reed@google.comac6b9792011-03-11 15:42:51 +0000937}
938
939void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
940
941 SkAutoMutexAcquire ac(gFTMutex);
942
943 SkASSERT(&glyph && path);
944 SkASSERT(fDDC);
945
946 path->reset();
947
948#if 0
949 char buf[1024];
950 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
951 OutputDebugString(buf);
952#endif
953
954 GLYPHMETRICS gm;
955 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 +0000956 if (GDI_ERROR == total_size) {
957 ensure_typeface_accessible(fRec.fFontID);
958 total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
959 }
reed@google.comac6b9792011-03-11 15:42:51 +0000960
961 if (GDI_ERROR != total_size) {
962
963 const uint8_t* cur_glyph = glyphbuf;
964 const uint8_t* end_glyph = glyphbuf + total_size;
965
966 while(cur_glyph < end_glyph) {
967 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
968
969 const uint8_t* end_poly = cur_glyph + th->cb;
970 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
971
972 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
973
974 while(cur_poly < end_poly) {
975 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
976
977 if (pc->wType == TT_PRIM_LINE) {
978 for (uint16_t i = 0; i < pc->cpfx; i++) {
979 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
980 }
981 }
982
983 if (pc->wType == TT_PRIM_QSPLINE) {
984 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
985 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
986 POINTFX pnt_c = pc->apfx[u+1];
987
988 if (u < pc->cpfx - 2) { // If not on last spline, compute C
989 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
990 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
991 }
992
993 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
994 }
995 }
996 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
997 }
998 cur_glyph += th->cb;
999 path->close();
1000 }
1001 }
1002 else {
1003 SkASSERT(false);
1004 }
1005 //char buf[1024];
1006 //sprintf(buf, "generatePath: count:%d\n", count);
1007 //OutputDebugString(buf);
1008}
1009
1010void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
1011 SkASSERT(!"SkFontHost::Serialize unimplemented");
1012}
1013
1014SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
1015 SkASSERT(!"SkFontHost::Deserialize unimplemented");
1016 return NULL;
1017}
1018
1019static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
1020 // Initialize the MAT2 structure to the identify transformation matrix.
1021 static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
1022 SkScalarToFIXED(0), SkScalarToFIXED(1)};
1023 int flags = GGO_METRICS | GGO_GLYPH_INDEX;
1024 GLYPHMETRICS gm;
1025 if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
1026 return false;
1027 }
1028 SkASSERT(advance);
1029 *advance = gm.gmCellIncX;
1030 return true;
1031}
1032
1033// static
1034SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +00001035 uint32_t fontID,
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001036 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1037 const uint32_t* glyphIDs,
1038 uint32_t glyphIDsCount) {
reed@google.com59d2f632011-05-02 19:36:59 +00001039 LOGFONT lf;
1040 GetLogFontByID(fontID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001041 SkAdvancedTypefaceMetrics* info = NULL;
1042
1043 HDC hdc = CreateCompatibleDC(NULL);
1044 HFONT font = CreateFontIndirect(&lf);
1045 HFONT savefont = (HFONT)SelectObject(hdc, font);
1046 HFONT designFont = NULL;
1047
1048 // To request design units, create a logical font whose height is specified
1049 // as unitsPerEm.
1050 OUTLINETEXTMETRIC otm;
bungeman@google.com39698b12011-11-15 22:26:41 +00001051 unsigned int otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1052 if (0 == otmRet) {
1053 ensure_typeface_accessible(fontID);
1054 otmRet = GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
1055 }
1056 if (!otmRet || !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
reed@google.comac6b9792011-03-11 15:42:51 +00001057 goto Error;
1058 }
1059 lf.lfHeight = -SkToS32(otm.otmEMSquare);
1060 designFont = CreateFontIndirect(&lf);
1061 SelectObject(hdc, designFont);
1062 if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
1063 goto Error;
1064 }
1065 const unsigned glyphCount = calculateGlyphCount(hdc);
1066
1067 info = new SkAdvancedTypefaceMetrics;
1068 info->fEmSize = otm.otmEMSquare;
1069 info->fMultiMaster = false;
1070 info->fLastGlyphID = SkToU16(glyphCount - 1);
1071 info->fStyle = 0;
1072#ifdef UNICODE
1073 // Get the buffer size needed first.
1074 size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
1075 0, NULL, NULL);
1076 // Allocate a buffer (str_len already has terminating null accounted for).
1077 char *familyName = new char[str_len];
1078 // Now actually convert the string.
1079 WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
1080 NULL, NULL);
1081 info->fFontName.set(familyName);
1082 delete [] familyName;
1083#else
1084 info->fFontName.set(lf.lfFaceName);
1085#endif
1086
vandebo@chromium.org6744d492011-05-09 18:13:47 +00001087 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1088 populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
1089 }
1090
reed@google.comac6b9792011-03-11 15:42:51 +00001091 if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
1092 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1093 } else {
1094 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1095 info->fItalicAngle = 0;
1096 info->fAscent = 0;
1097 info->fDescent = 0;
1098 info->fStemV = 0;
1099 info->fCapHeight = 0;
1100 info->fBBox = SkIRect::MakeEmpty();
1101 return info;
1102 }
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001103
reed@google.comac6b9792011-03-11 15:42:51 +00001104 // If this bit is clear the font is a fixed pitch font.
1105 if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1106 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1107 }
1108 if (otm.otmTextMetrics.tmItalic) {
1109 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1110 }
1111 // Setting symbolic style by default for now.
1112 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
1113 if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
1114 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1115 } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
1116 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1117 }
1118
1119 // The main italic angle of the font, in tenths of a degree counterclockwise
1120 // from vertical.
1121 info->fItalicAngle = otm.otmItalicAngle / 10;
1122 info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
1123 info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
1124 // TODO(ctguil): Use alternate cap height calculation.
1125 // MSDN says otmsCapEmHeight is not support but it is returning a value on
1126 // my Win7 box.
1127 info->fCapHeight = otm.otmsCapEmHeight;
1128 info->fBBox =
1129 SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
1130 otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
1131
1132 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1133 // This probably isn't very good with an italic font.
1134 int16_t min_width = SHRT_MAX;
1135 info->fStemV = 0;
1136 char stem_chars[] = {'i', 'I', '!', '1'};
1137 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
1138 ABC abcWidths;
1139 if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
1140 int16_t width = abcWidths.abcB;
1141 if (width > 0 && width < min_width) {
1142 min_width = width;
1143 info->fStemV = min_width;
1144 }
1145 }
1146 }
1147
1148 // If bit 1 is set, the font may not be embedded in a document.
1149 // If bit 1 is clear, the font can be embedded.
1150 // If bit 2 is set, the embedding is read-only.
1151 if (otm.otmfsType & 0x1) {
1152 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
ctguil@chromium.org0e6dc0a2011-03-30 20:41:16 +00001153 } else if (perGlyphInfo &
1154 SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001155 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1156 appendRange(&info->fGlyphWidths, 0);
1157 info->fGlyphWidths->fAdvance.append(1, &min_width);
1158 finishRange(info->fGlyphWidths.get(), 0,
1159 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1160 } else {
1161 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +00001162 getAdvanceData(hdc,
1163 glyphCount,
1164 glyphIDs,
1165 glyphIDsCount,
1166 &getWidthAdvance));
ctguil@chromium.org5aa937b2011-08-04 01:01:24 +00001167 }
reed@google.comac6b9792011-03-11 15:42:51 +00001168 }
1169
1170Error:
1171 SelectObject(hdc, savefont);
1172 DeleteObject(designFont);
1173 DeleteObject(font);
1174 DeleteDC(hdc);
1175
1176 return info;
1177}
1178
1179SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1180
1181 //Should not be used on Windows, keep linker happy
1182 SkASSERT(false);
1183 return SkCreateTypefaceFromLOGFONT(get_default_font());
1184}
1185
1186SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
ctguil@chromium.orgf4c26222011-05-16 22:00:05 +00001187 const DWORD kTTCTag =
1188 SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
reed@google.com59d2f632011-05-02 19:36:59 +00001189 LOGFONT lf;
1190 GetLogFontByID(uniqueID, &lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001191
1192 HDC hdc = ::CreateCompatibleDC(NULL);
reed@google.com59d2f632011-05-02 19:36:59 +00001193 HFONT font = CreateFontIndirect(&lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001194 HFONT savefont = (HFONT)SelectObject(hdc, font);
1195
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001196 SkMemoryStream* stream = NULL;
1197 DWORD tables[2] = {kTTCTag, 0};
1198 for (int i = 0; i < SK_ARRAY_COUNT(tables); i++) {
1199 size_t bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
bungeman@google.com39698b12011-11-15 22:26:41 +00001200 if (bufferSize == GDI_ERROR) {
1201 ensure_typeface_accessible(uniqueID);
1202 bufferSize = GetFontData(hdc, tables[i], 0, NULL, 0);
1203 }
vandebo@chromium.orgd6044812011-05-13 03:41:29 +00001204 if (bufferSize != GDI_ERROR) {
1205 stream = new SkMemoryStream(bufferSize);
1206 if (GetFontData(hdc, tables[i], 0, (void*)stream->getMemoryBase(),
1207 bufferSize)) {
1208 break;
1209 } else {
1210 delete stream;
1211 stream = NULL;
1212 }
1213 }
reed@google.comac6b9792011-03-11 15:42:51 +00001214 }
1215
1216 SelectObject(hdc, savefont);
1217 DeleteObject(font);
1218 DeleteDC(hdc);
1219
1220 return stream;
1221}
1222
1223SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
1224 return SkNEW_ARGS(SkScalerContext_Windows, (desc));
1225}
1226
1227/** Return the closest matching typeface given either an existing family
1228 (specified by a typeface in that family) or by a familyName, and a
1229 requested style.
bungeman@google.com90d812b2011-10-24 21:25:01 +00001230 1) If familyFace is null, use familyName.
1231 2) If familyName is null, use familyFace.
reed@google.comac6b9792011-03-11 15:42:51 +00001232 3) If both are null, return the default font that best matches style
1233 This MUST not return NULL.
1234 */
1235
1236SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
1237 const char familyName[],
1238 const void* data, size_t bytelength,
1239 SkTypeface::Style style) {
reed@google.com59d2f632011-05-02 19:36:59 +00001240 LOGFONT lf;
reed@google.comac6b9792011-03-11 15:42:51 +00001241 if (NULL == familyFace && NULL == familyName) {
reed@google.com59d2f632011-05-02 19:36:59 +00001242 lf = get_default_font();
1243 } else if (familyFace) {
1244 LogFontTypeface* face = (LogFontTypeface*)familyFace;
1245 lf = face->fLogFont;
reed@google.comac6b9792011-03-11 15:42:51 +00001246 } else {
reed@google.com59d2f632011-05-02 19:36:59 +00001247 memset(&lf, 0, sizeof(LOGFONT));
reed@google.comac6b9792011-03-11 15:42:51 +00001248#ifdef UNICODE
reed@google.com59d2f632011-05-02 19:36:59 +00001249 // Get the buffer size needed first.
1250 size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
1251 -1, NULL, 0);
1252 // Allocate a buffer (str_len already has terminating null
1253 // accounted for).
1254 wchar_t *wideFamilyName = new wchar_t[str_len];
1255 // Now actually convert the string.
1256 ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
1257 wideFamilyName, str_len);
1258 ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
1259 delete [] wideFamilyName;
reed@google.comac6b9792011-03-11 15:42:51 +00001260#else
reed@google.com59d2f632011-05-02 19:36:59 +00001261 ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
reed@google.comac6b9792011-03-11 15:42:51 +00001262#endif
reed@google.com59d2f632011-05-02 19:36:59 +00001263 lf.lfFaceName[LF_FACESIZE-1] = '\0';
reed@google.comac6b9792011-03-11 15:42:51 +00001264 }
reed@google.com59d2f632011-05-02 19:36:59 +00001265 setStyle(&lf, style);
1266 return SkCreateTypefaceFromLOGFONT(lf);
reed@google.comac6b9792011-03-11 15:42:51 +00001267}
1268
reed@google.comac6b9792011-03-11 15:42:51 +00001269SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1270 printf("SkFontHost::CreateTypefaceFromFile unimplemented");
1271 return NULL;
1272}
1273
1274void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
reed@google.come8fab012011-07-13 15:25:33 +00001275 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1276 SkScalerContext::kAutohinting_Flag |
1277 SkScalerContext::kEmbeddedBitmapText_Flag |
1278 SkScalerContext::kEmbolden_Flag |
1279 SkScalerContext::kLCD_BGROrder_Flag |
1280 SkScalerContext::kLCD_Vertical_Flag;
1281 rec->fFlags &= ~flagsWeDontSupport;
1282
reed@google.come8fab012011-07-13 15:25:33 +00001283 SkPaint::Hinting h = rec->getHinting();
reed@google.comda440672011-07-13 18:02:28 +00001284
1285 // I think we can support no-hinting, if we get hires outlines and just
1286 // use skia to rasterize into a gray-scale mask...
1287#if 0
reed@google.come8fab012011-07-13 15:25:33 +00001288 switch (h) {
1289 case SkPaint::kNo_Hinting:
1290 case SkPaint::kSlight_Hinting:
1291 h = SkPaint::kNo_Hinting;
1292 break;
1293 case SkPaint::kNormal_Hinting:
1294 case SkPaint::kFull_Hinting:
1295 h = SkPaint::kNormal_Hinting;
1296 break;
1297 default:
1298 SkASSERT(!"unknown hinting");
1299 }
reed@google.comda440672011-07-13 18:02:28 +00001300#else
1301 h = SkPaint::kNormal_Hinting;
1302#endif
reed@google.come8fab012011-07-13 15:25:33 +00001303 rec->setHinting(h);
reed@google.comda440672011-07-13 18:02:28 +00001304
reed@google.com9181aa82011-08-05 14:28:31 +00001305// turn this off since GDI might turn A8 into BW! Need a bigger fix.
1306#if 0
reed@google.comda440672011-07-13 18:02:28 +00001307 // Disable LCD when rotated, since GDI's output is ugly
1308 if (isLCD(*rec) && !isAxisAligned(*rec)) {
1309 rec->fMaskFormat = SkMask::kA8_Format;
1310 }
reed@google.com9181aa82011-08-05 14:28:31 +00001311#endif
reed@google.com754e4eb2011-09-26 13:21:39 +00001312
1313#if 0
1314 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
1315 rec->fMaskFormat = SkMask::kLCD32_Format;
1316 }
1317#endif
reed@google.com6fc3c1f2011-09-30 20:31:25 +00001318 // don't specify gamma if we BW (perhaps caller should do this check)
1319 if (SkMask::kBW_Format == rec->fMaskFormat) {
1320 rec->fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
1321 SkScalerContext::kGammaForWhite_Flag);
1322 }
reed@google.com754e4eb2011-09-26 13:21:39 +00001323}
1324
1325//////////////////////////////////////////////////////////////////////////
1326
1327void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
1328 tables[0] = NULL;
1329 tables[1] = NULL;
1330}
1331
reed@google.com6f5df482011-09-28 20:33:24 +00001332static bool justAColor(const SkPaint& paint, SkColor* color) {
1333 if (paint.getShader()) {
1334 return false;
1335 }
1336 SkColor c = paint.getColor();
1337 if (paint.getColorFilter()) {
1338 c = paint.getColorFilter()->filterColor(c);
1339 }
1340 if (color) {
1341 *color = c;
1342 }
1343 return true;
1344}
reed@google.com754e4eb2011-09-26 13:21:39 +00001345
reed@google.com6f5df482011-09-28 20:33:24 +00001346#define BLACK_GAMMA_THRESHOLD 0x40
reed@google.com754e4eb2011-09-26 13:21:39 +00001347#define WHITE_GAMMA_THRESHOLD 0xA0
1348
1349int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
reed@google.com6f5df482011-09-28 20:33:24 +00001350 SkColor c;
1351 if (justAColor(paint, &c)) {
reed@google.com754e4eb2011-09-26 13:21:39 +00001352 int r = SkColorGetR(c);
1353 int g = SkColorGetG(c);
1354 int b = SkColorGetB(c);
1355 int luminance = (r * 2 + g * 5 + b) >> 3;
1356
reed@google.com754e4eb2011-09-26 13:21:39 +00001357 if (luminance <= BLACK_GAMMA_THRESHOLD) {
1358 return SkScalerContext::kGammaForBlack_Flag;
1359 }
reed@google.com754e4eb2011-09-26 13:21:39 +00001360 if (luminance >= WHITE_GAMMA_THRESHOLD) {
1361 return SkScalerContext::kGammaForWhite_Flag;
1362 }
1363 }
1364 return 0;
reed@google.comac6b9792011-03-11 15:42:51 +00001365}
1366
1367#endif // WIN32