blob: af44cf5266747c34c29665b7f3160f5de850179e [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 "SkFontHost.h"
18#include "SkDescriptor.h"
19
20// Give 1MB font cache budget
21#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
22
23const char* gDefaultfont = "Arial"; // hard code for now
24static SkMutex gFTMutex;
25
26inline SkPoint F32PtToSkPoint(const Float32Point p)
27{
28 SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };
29 return sp;
30}
31
32static inline uint32_t _rotl(uint32_t v, uint32_t r)
33{
34 return (v << r | v >> (32 - r));
35}
36
37// This will generate a unique ID based on the fontname + fontstyle
38// and also used by upper layer
39uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)
40{
41 if (!name) return style;
42
43 char* q = (char*)name;
44
45 // From "Performance in Practice of String Hashing Functions"
46 // Ramakrishna & Zobel
47 const uint32_t L = 5;
48 const uint32_t R = 2;
49
50 uint32_t h = 0x12345678;
51 while (*q) {
52 uint32_t ql = tolower(*q);
53 h ^= ((h << L) + (h >> R) + ql);
54 q ++;
55 }
56
57 // add style
58 h = _rotl(h, 3) ^ style;
59
60 return h;
61}
62
63#pragma mark -
64struct SkFaceRec {
65 SkFaceRec* fNext;
66 uint32_t fRefCnt;
67 ATSUFontID fFontID;
68 ATSUStyle fStyle;
69
70 SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};
71
72 ~SkFaceRec() {
73 if (fStyle) {
74 ::ATSUDisposeStyle(fStyle);
75 fStyle = NULL;
76 }
77 }
78
79 uint32_t ref() {
80 return ++fRefCnt;
81 }
82};
83
84// Font Face list
85static SkFaceRec* gFaceRecHead = NULL;
86
87static SkFaceRec* find_ft_face(const ATSUFontID fontID) {
88 SkFaceRec* rec = gFaceRecHead;
89 while (rec) {
90 if (rec->fFontID == fontID) {
91 return rec;
92 }
93 rec = rec->fNext;
94 }
95
96 return NULL;
97}
98
99static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {
100 SkFaceRec* rec = find_ft_face(afontID);
101 if (rec) {
102 return rec; // found?
103 }
104
105 rec = SkNEW(SkFaceRec);
106 rec->fFontID = afontID;
107 rec->fStyle = atsuStyle;
108 rec->fNext = gFaceRecHead;
109 gFaceRecHead = rec;
110
111 return rec;
112}
113
114static void unref_ft_face(const ATSUFontID fontID) {
115
116 SkFaceRec* rec = gFaceRecHead;
117 SkFaceRec* prev = NULL;
118 while (rec) {
119 SkFaceRec* next = rec->fNext;
120 if (rec->fFontID == fontID) {
121 if (--rec->fRefCnt == 0) {
122 if (prev)
123 prev->fNext = next;
124 else
125 gFaceRecHead = next;
126
127 SkDELETE(rec);
128 }
129 return;
130 }
131 prev = rec;
132 rec = next;
133 }
134 SkASSERT("shouldn't get here, face not in list");
135}
136
137#pragma mark -
138
139// have to do this because SkTypeface::SkTypeface() is protected
140class SkTypeface_Mac : public SkTypeface {
141public:
142 SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}
143
144 ~SkTypeface_Mac() {}
145};
146
147#pragma mark -
148
149static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {
150
151 OSStatus err;
152 ATSUStyle atsuStyle;
153 ::ATSUCreateStyle(&atsuStyle);
154 if (name != NULL) {
155 static const ATSUAttributeTag fontTag = kATSUFontTag;
156 static const ByteCount fontTagSize = sizeof(ATSUFontID);
157
158 ATSUFontID fontID = 0;
159#if 1
160 err = ::ATSUFindFontFromName(
161 name,strlen(name),kFontNoNameCode, /* instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */
162 kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);
163#else
164 CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII);
165 ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);
166 fontID = ::FMGetFontFromATSFontRef(fontRef);
167 CFRelease(cfontName);
168#endif
169 if (0 != fontID) {
170 const ATSUAttributeValuePtr values[] = { &fontID };
171 err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);
172 }
173 else {
174 }
175 }
176 if (style != SkTypeface::kNormal) {
177 Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
178 Boolean fontBold = ((style & SkTypeface::kBold) != 0);
179 const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
180 const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
181 const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
182 err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
183 }
184
185 uint32_t cs = FontFaceChecksum(name,style);
186 SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);
187
188 if (NULL == ptypeface) {
189 SkASSERT(false);
190 return NULL;
191 }
192
193 SkFaceRec* rec = insert_ft_face(cs, atsuStyle);
194 SkASSERT(rec);
195
196 return ptypeface;
197}
198
199static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {
200
201 OSStatus err;
202 ATSUStyle atsuStyle;
203 err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);
204
205 Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
206 Boolean fontBold = ((style & SkTypeface::kBold) != 0);
207 const ATSUAttributeTag tags[2] = { kATSUQDBoldfaceTag, kATSUQDItalicTag };
208 const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
209 const ByteCount sizes[2] = { sizeof(Boolean), sizeof(Boolean) };
210 err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
211
212 // get old font id and name
213 ATSUFontID fontID = 0;
214 ByteCount actual = 0;
215 err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);
216
217 ByteCount actualLength = 0;
218 char *fontname = NULL;
219 err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
220 kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );
221 if ( err == noErr)
222 {
223 actualLength += 1 ;
224 fontname = (char*)malloc( actualLength );
225 err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
226 kFontNoLanguageCode, actualLength, fontname , NULL, NULL);
227 }
228
229 SkTypeface_Mac* ptypeface = NULL;
230 if (fontname == NULL) {
231 ptypeface = new SkTypeface_Mac(style,rec->fFontID);
232 return ptypeface;
233 }
234 else {
235 uint32_t cs = FontFaceChecksum(fontname,style);
236 ptypeface = new SkTypeface_Mac(style, cs);
237
238 if (NULL == ptypeface) {
239 SkASSERT(false);
240 return NULL;
241 }
242
243 free(fontname);
244
245 insert_ft_face(cs,atsuStyle);
246 }
247 return ptypeface;
248}
249
250#pragma mark -
251
252class SkScalerContext_Mac : public SkScalerContext {
253public:
254 SkScalerContext_Mac(const SkDescriptor* desc);
255 virtual ~SkScalerContext_Mac();
256
257protected:
258 virtual unsigned generateGlyphCount() const;
259 virtual uint16_t generateCharToGlyph(SkUnichar uni);
260 virtual void generateAdvance(SkGlyph* glyph);
261 virtual void generateMetrics(SkGlyph* glyph);
262 virtual void generateImage(const SkGlyph& glyph);
263 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
264 virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
265 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
266 virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac
267
268private:
269 ATSUTextLayout fLayout;
270 ATSUStyle fStyle;
271
272 static OSStatus MoveTo(const Float32Point *pt, void *cb);
273 static OSStatus Line(const Float32Point *pt, void *cb);
274 static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
275 static OSStatus Close(void *cb);
276};
277
278SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
279 : SkScalerContext(desc), fLayout(0), fStyle(0)
280{
281 SkAutoMutexAcquire ac(gFTMutex);
282 OSStatus err;
283
284 SkFaceRec* rec = find_ft_face(fRec.fFontID);
285 if (rec) {
286 rec->ref();
287 err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);
288 }
289 else {
290 SkASSERT(false);
291 // create a default
292 err = ::ATSUCreateStyle(&fStyle);
293 }
294
295 uint32_t size = SkFixedFloor(fRec.fTextSize);
296 Fixed fixedSize = IntToFixed(size);
297 static const ATSUAttributeTag sizeTag = kATSUSizeTag;
298 static const ByteCount sizeTagSize = sizeof(Fixed);
299 const ATSUAttributeValuePtr values[] = { &fixedSize };
300 err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);
301
302 err = ::ATSUCreateTextLayout(&fLayout);
303}
304
305SkScalerContext_Mac::~SkScalerContext_Mac()
306{
307 unref_ft_face(fRec.fFontID);
308
309 ::ATSUDisposeTextLayout(fLayout);
310 ::ATSUDisposeStyle(fStyle);
311}
312
313unsigned SkScalerContext_Mac::generateGlyphCount() const
314{
315 return 0xFFFF;
316}
317
318uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
319{
320 SkAutoMutexAcquire ac(gFTMutex);
321
322 OSStatus err;
323 UniChar achar = uni;
324 err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
325 err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
326
327 ATSLayoutRecord *layoutPtr;
328 ItemCount count;
329 ATSGlyphRef glyph;
330
331 err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
332 glyph = layoutPtr->glyphID;
333 ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
334 return glyph;
335}
336
337void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
338 this->generateMetrics(glyph);
339}
340
341void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
342{
343 GlyphID glyphID = glyph->fID;
344 ATSGlyphScreenMetrics metrics= { 0 };
345
346 glyph->fRsbDelta = 0;
347 glyph->fLsbDelta = 0;
348
349 OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
350 if (err == noErr) {
351 glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
352 glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);
353 //glyph->fWidth = metrics.width;
354 //glyph->fHeight = metrics.height;
355 glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);
356 glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;
357
358 glyph->fTop = -metrics.topLeft.y;
359 glyph->fLeft = metrics.topLeft.x;
360 }
361}
362
363void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
364 //SkASSERT(false);
365 if (mx)
366 memset(mx, 0, sizeof(SkPaint::FontMetrics));
367 if (my)
368 memset(my, 0, sizeof(SkPaint::FontMetrics));
369 return;
370}
371
372void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
373{
374 SkAutoMutexAcquire ac(gFTMutex);
375
376 GlyphID glyphID = glyph.fID;
377 ATSGlyphScreenMetrics metrics= { 0 };
378
379 SkASSERT(fLayout);
380 OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
381
382// uint32_t w = metrics.width;
383// uint32_t h = metrics.height;
384// uint32_t pitch = (w + 3) & ~0x3;
385// if (pitch != glyph.rowBytes()) {
386// SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!
387// }
388
389 CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
390 CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);
391 if (!contextRef) {
392 SkASSERT(false);
393 return;
394 }
395
396 ::CGContextSetFillColorSpace(contextRef, greyColorSpace);
397 ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace);
398
399 ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);
400 ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));
401
402 ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
403 ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);
404 ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
405
406 ATSUAttributeTag tag = kATSUCGContextTag;
407 ByteCount size = sizeof(CGContextRef);
408 ATSUAttributeValuePtr value = &contextRef;
409 err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);
410 err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));
411 ::CGContextRelease(contextRef);
412}
413
414void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
415{
416 SkAutoMutexAcquire ac(gFTMutex);
417 OSStatus err,result;
418
419 err = ::ATSUGlyphGetCubicPaths(
420 fStyle,glyph.fID,
421 &SkScalerContext_Mac::MoveTo,
422 &SkScalerContext_Mac::Line,
423 &SkScalerContext_Mac::Curve,
424 &SkScalerContext_Mac::Close,
425 path,&result);
426 SkASSERT(err == noErr);
427}
428
429void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)
430{
431 ATSUTextMeasurement textAscent, textDescent;
432 ByteCount actual = 0;
433 OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);
434 ascent->set(0,textAscent);
435 err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);
436 descent->set(0,textDescent);
437}
438
439OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
440{
441 reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
442 return noErr;
443}
444
445OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
446{
447 reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
448 return noErr;
449}
450
451OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)
452{
453 reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));
454 return noErr;
455}
456
457OSStatus SkScalerContext_Mac::Close(void *cb)
458{
459 reinterpret_cast<SkPath*>(cb)->close();
460 return noErr;
461}
462
463#pragma mark -
464
465void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
466 SkASSERT(!"SkFontHost::Serialize unimplemented");
467}
468
469SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
470 SkASSERT(!"SkFontHost::Deserialize unimplemented");
471 return NULL;
472}
473
474SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
475
476 //Should not be used on Mac, keep linker happy
477 SkASSERT(false);
478 return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
479}
480
481SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
482{
483 return new SkScalerContext_Mac(desc);
484}
485
486SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
487{
488 SkAutoDescriptor ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));
489 SkDescriptor* desc = ad.getDesc();
490
491 desc->init();
492 SkScalerContext::Rec* newRec =
493 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
494
495 CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
496 newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);
497 desc->computeChecksum();
498
499 return SkFontHost::CreateScalerContext(desc);
500}
501
502
503 /** Return the closest matching typeface given either an existing family
504 (specified by a typeface in that family) or by a familyName, and a
505 requested style.
506 1) If familyFace is null, use famillyName.
507 2) If famillyName is null, use familyFace.
508 3) If both are null, return the default font that best matches style
509 This MUST not return NULL.
510 */
511
512SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
513
514 SkAutoMutexAcquire ac(gFTMutex);
515
516 // clip to legal style bits
517 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
518
519 SkTypeface* tf = NULL;
520
521 if (NULL == familyFace && NULL == familyName) {
522 tf = CreateTypeface_(gDefaultfont,style);
523 }
524 else {
525 if (NULL != familyFace) {
526 uint32_t id = familyFace->uniqueID();
527 SkFaceRec* rec = find_ft_face(id);
528 if (!rec) {
529 SkASSERT(false);
530 tf = CreateTypeface_(gDefaultfont,style);
531 }
532 else {
533 tf = CreateTypeface_(rec,style);
534 }
535 }
536 else {
537 tf = CreateTypeface_(familyName,style);
538 }
539 }
540
541 if (NULL == tf) {
542 tf = CreateTypeface_(gDefaultfont,style);
543 }
544 return tf;
545
546}
547
548size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
549 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
550 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
551 else
552 return 0; // nothing to do
553}
554
555int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
556 return 0;
557}
558
559void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
560 tables[0] = NULL; // black gamma (e.g. exp=1.4)
561 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
562}
563