blob: c2ee51e379ee2652f8aa4d05d4595483b1495f62 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 ** Copyright 2006, The Android Open Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16
17#include "SkString.h"
18//#include "SkStream.h"
19
20#include "SkFontHost.h"
21#include "SkDescriptor.h"
22#include "SkThread.h"
23
24#ifdef WIN32
25#include "windows.h"
26#include "tchar.h"
27
28// client3d has to undefine this for now
29#define CAN_USE_LOGFONT_NAME
30
31static SkMutex gFTMutex;
32
33// these globals are loaded (once) by get_default_font()
34static LOGFONT gDefaultFont = {0};
35
36static const uint16_t BUFFERSIZE = (16384 - 32);
37static uint8_t glyphbuf[BUFFERSIZE];
38
39// Give 1MB font cache budget
40#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
41
42static inline FIXED SkFixedToFIXED(SkFixed x) {
43 return *(FIXED*)(&x);
44}
45
46static inline FIXED SkScalarToFIXED(SkScalar x) {
47 return SkFixedToFIXED(SkScalarToFixed(x));
48}
49
50// This will generate a unique ID based on the fontname + fontstyle
51// and also used by upper layer
52uint32_t FontFaceChecksum(const TCHAR *q, SkTypeface::Style style)
53{
54 if (!q) return style;
55
56 // From "Performance in Practice of String Hashing Functions"
57 // Ramakrishna & Zobel
58 const uint32_t L = 5;
59 const uint32_t R = 2;
60
61 uint32_t h = 0x12345678;
62 while (*q) {
63 //uint32_t ql = tolower(*q);
64 h ^= ((h << L) + (h >> R) + *q);
65 q ++;
66 }
67
68 // add style
69 h = _rotl(h, 3) ^ style;
70
71 return h;
72}
73
74static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
75 int style = SkTypeface::kNormal;
76 if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
77 style |= SkTypeface::kBold;
78 if (lf.lfItalic)
79 style |= SkTypeface::kItalic;
80
81 return (SkTypeface::Style)style;
82}
83
84struct SkFaceRec {
85 SkFaceRec* fNext;
86 uint32_t fRefCnt;
87 uint32_t fFontID; // checksum of fFace
88 LOGFONT fFace;
89
90 SkFaceRec() : fFontID(-1), fRefCnt(0) {
91 memset(&fFace, 0, sizeof(LOGFONT));
92 }
93 ~SkFaceRec() {}
94
95 uint32_t ref() {
96 return ++fRefCnt;
97 }
98};
99
100// Font Face list
101static SkFaceRec* gFaceRecHead = NULL;
102
103static SkFaceRec* find_ft_face(uint32_t fontID) {
104 SkFaceRec* rec = gFaceRecHead;
105 while (rec) {
106 if (rec->fFontID == fontID) {
107 return rec;
108 }
109 rec = rec->fNext;
110 }
111
112 return NULL;
113}
114
115static SkFaceRec* insert_ft_face(const LOGFONT& lf) {
116 // need a const char*
117 uint32_t id = FontFaceChecksum(&(lf.lfFaceName[0]), GetFontStyle(lf));
118 SkFaceRec* rec = find_ft_face(id);
119 if (rec) {
120 return rec; // found?
121 }
122
123 rec = SkNEW(SkFaceRec);
124 rec->fFontID = id;
125 memcpy(&(rec->fFace), &lf, sizeof(LOGFONT));
126 rec->fNext = gFaceRecHead;
127 gFaceRecHead = rec;
128
129 return rec;
130}
131
132static void unref_ft_face(uint32_t fontID) {
133
134 SkFaceRec* rec = gFaceRecHead;
135 SkFaceRec* prev = NULL;
136 while (rec) {
137 SkFaceRec* next = rec->fNext;
138 if (rec->fFontID == fontID) {
139 if (--rec->fRefCnt == 0) {
140 if (prev)
141 prev->fNext = next;
142 else
143 gFaceRecHead = next;
144
145 SkDELETE(rec);
146 }
147 return;
148 }
149 prev = rec;
150 rec = next;
151 }
152 SkASSERT("shouldn't get here, face not in list");
153}
154
155// have to do this because SkTypeface::SkTypeface() is protected
156class FontFaceRec_Typeface : public SkTypeface {
157public:
158
159 FontFaceRec_Typeface(Style style, uint32_t id) : SkTypeface(style, id) {};
160
161 virtual ~FontFaceRec_Typeface() {};
162};
163
164static const LOGFONT* get_default_font() {
165 // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
166 // and the user could change too
167
168 if (gDefaultFont.lfFaceName[0] != 0) {
169 return &gDefaultFont;
170 }
171
172 NONCLIENTMETRICS ncm;
173 ncm.cbSize = sizeof(NONCLIENTMETRICS);
174 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
175
176 memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
177
178 return &gDefaultFont;
179}
180
181static SkTypeface* CreateTypeface_(const LOGFONT& lf) {
182
183 SkTypeface::Style style = GetFontStyle(lf);
184 FontFaceRec_Typeface* ptypeface = new FontFaceRec_Typeface(style, FontFaceChecksum(lf.lfFaceName, style));
185
186 if (NULL == ptypeface) {
187 SkASSERT(false);
188 return NULL;
189 }
190
191 SkFaceRec* rec = insert_ft_face(lf);
192 SkASSERT(rec);
193
194 return ptypeface;
195}
196
197class SkScalerContext_Windows : public SkScalerContext {
198public:
199 SkScalerContext_Windows(const SkDescriptor* desc);
200 virtual ~SkScalerContext_Windows();
201
202protected:
203 virtual unsigned generateGlyphCount() const;
204 virtual uint16_t generateCharToGlyph(SkUnichar uni);
205 virtual void generateAdvance(SkGlyph* glyph);
206 virtual void generateMetrics(SkGlyph* glyph);
207 virtual void generateImage(const SkGlyph& glyph);
208 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
209 virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
210 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
211 //virtual SkDeviceContext getDC() {return ddc;}
212private:
213 uint32_t fFontID;
214 LOGFONT lf;
215 MAT2 mat22;
216 HDC ddc;
217 HFONT savefont;
218 HFONT font;
219};
220
221SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {
222 SkAutoMutexAcquire ac(gFTMutex);
223
224 fFontID = fRec.fFontID;
225 SkFaceRec* rec = find_ft_face(fRec.fFontID);
226 if (rec) {
227 rec->ref();
228 memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
229 }
230 else {
231 SkASSERT(false);
232 memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
233 }
234
235 mat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
236 mat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
237 mat22.eM21 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
238 mat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
239
240 ddc = ::CreateCompatibleDC(NULL);
241 SetBkMode(ddc, TRANSPARENT);
242
243 lf.lfHeight = SkScalarFloor(fRec.fTextSize);
244 font = CreateFontIndirect(&lf);
245 savefont = (HFONT)SelectObject(ddc, font);
246}
247
248SkScalerContext_Windows::~SkScalerContext_Windows() {
249 unref_ft_face(fFontID);
250
251 if (ddc) {
252 ::SelectObject(ddc, savefont);
253 ::DeleteDC(ddc);
254 ddc = NULL;
255 }
256 if (font) {
257 ::DeleteObject(font);
258 }
259}
260
261unsigned SkScalerContext_Windows::generateGlyphCount() const {
262 return 0xFFFF;
263 // return fFace->num_glyphs;
264}
265
266uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
267
268 //uint16_t index = 0;
269 //GetGlyphIndicesW(ddc, &(uint16_t&)uni, 1, &index, 0);
270 //return index;
271
272 // let's just use the uni as index on Windows
273 return SkToU16(uni);
274}
275
276void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
277 this->generateMetrics(glyph);
278}
279
280void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
281
282 SkASSERT(ddc);
283
284 GLYPHMETRICS gm;
285 memset(&gm, 0, sizeof(gm));
286
287 glyph->fRsbDelta = 0;
288 glyph->fLsbDelta = 0;
289
290 UINT glyphIndexFlag = 0; //glyph->fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
291 // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
292 // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
293 // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
294 uint32_t ret = GetGlyphOutlineW(ddc, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);
295
296 if (GDI_ERROR != ret) {
297 if (ret == 0) {
298 // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
299 gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
300 }
301 glyph->fWidth = gm.gmBlackBoxX;
302 glyph->fHeight = gm.gmBlackBoxY;
303 glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
304 glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
305 glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
306 glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
307 } else {
308 glyph->fWidth = 0;
309 }
310
311#if 0
312 char buf[1024];
313 sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);
314 OutputDebugString(buf);
315#endif
316}
317
318void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
319 //SkASSERT(false);
320 if (mx)
321 memset(mx, 0, sizeof(SkPaint::FontMetrics));
322 if (my)
323 memset(my, 0, sizeof(SkPaint::FontMetrics));
324 return;
325}
326
327void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
328
329 SkAutoMutexAcquire ac(gFTMutex);
330
331 SkASSERT(ddc);
332
333 GLYPHMETRICS gm;
334 memset(&gm, 0, sizeof(gm));
335
336#if 0
337 char buf[1024];
338 sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
339 OutputDebugString(buf);
340#endif
341
342 uint32_t bytecount = 0;
343 UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
344 // UINT glyphIndexFlag = GGO_GLYPH_INDEX;
345 uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);
346 if (GDI_ERROR != total_size && total_size > 0) {
347 uint8_t *pBuff = new uint8_t[total_size];
348 if (NULL != pBuff) {
349 total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, total_size, pBuff, &mat22);
350
351 SkASSERT(total_size != GDI_ERROR);
352
353 SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
354 SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
355
356 uint8_t* dst = (uint8_t*)glyph.fImage;
357 uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
358 if (pitch != glyph.rowBytes()) {
359 SkASSERT(false); // glyph.fImage has different rowsize!?
360 }
361
362 for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
363 uint8_t* src = pBuff + pitch * y;
364
365 for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
366 if (*src > 63) {
367 *dst = 0xFF;
368 }
369 else {
370 *dst = *src << 2; // scale to 0-255
371 }
372 dst++;
373 src++;
374 bytecount++;
375 }
376 memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
377 dst += glyph.rowBytes() - glyph.fWidth;
378 }
379
380 delete[] pBuff;
381 }
382 }
383
384 SkASSERT(GDI_ERROR != total_size && total_size >= 0);
385
386}
387
388void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
389
390 SkAutoMutexAcquire ac(gFTMutex);
391
392 SkASSERT(&glyph && path);
393 SkASSERT(ddc);
394
395 path->reset();
396
397#if 0
398 char buf[1024];
399 sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
400 OutputDebugString(buf);
401#endif
402
403 GLYPHMETRICS gm;
404 UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;
405 uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_NATIVE | glyphIndexFlag, &gm, BUFFERSIZE, glyphbuf, &mat22);
406
407 if (GDI_ERROR != total_size) {
408
409 const uint8_t* cur_glyph = glyphbuf;
410 const uint8_t* end_glyph = glyphbuf + total_size;
411
412 while(cur_glyph < end_glyph) {
413 const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
414
415 const uint8_t* end_poly = cur_glyph + th->cb;
416 const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
417
418 path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
419
420 while(cur_poly < end_poly) {
421 const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
422
423 if (pc->wType == TT_PRIM_LINE) {
424 for (uint16_t i = 0; i < pc->cpfx; i++) {
425 path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
426 }
427 }
428
429 if (pc->wType == TT_PRIM_QSPLINE) {
430 for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
431 POINTFX pnt_b = pc->apfx[u]; // B is always the current point
432 POINTFX pnt_c = pc->apfx[u+1];
433
434 if (u < pc->cpfx - 2) { // If not on last spline, compute C
435 pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
436 pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
437 }
438
439 path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
440 }
441 }
442 cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
443 }
444 cur_glyph += th->cb;
445 path->close();
446 }
447 }
448 else {
449 SkASSERT(false);
450 }
451 //char buf[1024];
452 //sprintf(buf, "generatePath: count:%d\n", count);
453 //OutputDebugString(buf);
454}
455
456
457// Note: not sure this is the correct implementation
458void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) {
459
460 SkASSERT(ddc);
461
462 OUTLINETEXTMETRIC otm;
463
464 uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm);
465
466 if (sizeof(otm) == ret) {
467 if (ascent)
468 ascent->iset(0, otm.otmAscent);
469 if (descent)
470 descent->iset(0, otm.otmDescent);
471 }
472
473 return;
474}
475
476void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
477 SkASSERT(!"SkFontHost::Serialize unimplemented");
478}
479
480SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
481 SkASSERT(!"SkFontHost::Deserialize unimplemented");
482 return NULL;
483}
484
485SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
486
487 //Should not be used on Windows, keep linker happy
488 SkASSERT(false);
489 get_default_font();
490 return CreateTypeface_(gDefaultFont);
491}
492
493SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
494 return SkNEW_ARGS(SkScalerContext_Windows, (desc));
495}
496
497SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) {
498 get_default_font();
499
500 SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultFont) + SkDescriptor::ComputeOverhead(2));
501 SkDescriptor* desc = ad.getDesc();
502
503 desc->init();
504 SkScalerContext::Rec* newRec =
505 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
506
507 get_default_font();
508 CreateTypeface_(gDefaultFont);
509 newRec->fFontID = FontFaceChecksum(gDefaultFont.lfFaceName, GetFontStyle(gDefaultFont));
510 desc->computeChecksum();
511
512 return SkFontHost::CreateScalerContext(desc);
513}
514
515/** Return the closest matching typeface given either an existing family
516 (specified by a typeface in that family) or by a familyName, and a
517 requested style.
518 1) If familyFace is null, use famillyName.
519 2) If famillyName is null, use familyFace.
520 3) If both are null, return the default font that best matches style
521 This MUST not return NULL.
522 */
523
524SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
525
526 SkAutoMutexAcquire ac(gFTMutex);
527
528#ifndef CAN_USE_LOGFONT_NAME
529 familyName = NULL;
530 familyFace = NULL;
531#endif
532
533 // clip to legal style bits
534 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
535
536 SkTypeface* tf = NULL;
537 if (NULL == familyFace && NULL == familyName) {
538 LOGFONT lf;
539 get_default_font();
540 memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
541 lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
542 lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
543 tf = CreateTypeface_(lf);
544 } else {
545#ifdef CAN_USE_LOGFONT_NAME
546 LOGFONT lf;
547 if (NULL != familyFace) {
548 uint32_t id = familyFace->uniqueID();
549 SkFaceRec* rec = find_ft_face(id);
550 if (!rec) {
551 SkASSERT(false);
552 get_default_font();
553 memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));
554 }
555 else {
556 memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));
557 }
558 }
559 else {
560 memset(&lf, 0, sizeof(LOGFONT));
561
562 lf.lfHeight = -11; // default
563 lf.lfQuality = PROOF_QUALITY;
564 lf.lfCharSet = DEFAULT_CHARSET;
565
566 _tcsncpy(lf.lfFaceName, familyName, LF_FACESIZE);
567 lf.lfFaceName[LF_FACESIZE-1] = '\0';
568 }
569
570 // use the style desired
571 lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
572 lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
573 tf = CreateTypeface_(lf);
574#endif
575 }
576
577 if (NULL == tf) {
578 get_default_font();
579 tf = CreateTypeface_(gDefaultFont);
580 }
581 return tf;
582}
583
584size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
585 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
586 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
587 else
588 return 0; // nothing to do
589}
590
591int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
592 return 0;
593}
594
595void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
596 tables[0] = NULL; // black gamma (e.g. exp=1.4)
597 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
598}
599
600#endif // WIN32
601