blob: 5deae35491897a50ead974d9f7e5580d7a68a09a [file] [log] [blame]
reed@android.com0680d6c2008-12-19 19:46:15 +00001/*
reed@android.com8a1c16f2008-12-17 15:59:43 +00002 ** Copyright 2006, The Android Open Source Project
reed@android.com0680d6c2008-12-19 19:46:15 +00003 **
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
reed@android.com79b2cd12009-03-18 19:10:34 +000017#include <Carbon/Carbon.h>
reed@android.com0680d6c2008-12-19 19:46:15 +000018#include "SkFontHost.h"
19#include "SkDescriptor.h"
reed@android.com0bf64d42009-03-09 17:22:22 +000020#include "SkEndian.h"
reed@android.comf13c6e12009-01-19 19:10:24 +000021#include "SkPoint.h"
reed@android.com0680d6c2008-12-19 19:46:15 +000022
23// Give 1MB font cache budget
24#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
25
26const char* gDefaultfont = "Arial"; // hard code for now
27static SkMutex gFTMutex;
28
reed@android.com03ca3d12008-12-22 15:35:46 +000029static inline SkPoint F32PtToSkPoint(const Float32Point p) {
30 SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
reed@android.com0680d6c2008-12-19 19:46:15 +000031 return sp;
32}
33
reed@android.com03ca3d12008-12-22 15:35:46 +000034static inline uint32_t _rotl(uint32_t v, uint32_t r) {
reed@android.com0680d6c2008-12-19 19:46:15 +000035 return (v << r | v >> (32 - r));
36}
37
reed@android.com0680d6c2008-12-19 19:46:15 +000038class SkTypeface_Mac : public SkTypeface {
39public:
reed@android.comea446b92009-03-09 15:25:11 +000040 SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
41 : SkTypeface(style, id) {}
reed@android.com0680d6c2008-12-19 19:46:15 +000042};
43
44#pragma mark -
45
reed@android.comea446b92009-03-09 15:25:11 +000046static uint32_t find_from_name(const char name[]) {
47 CFStringRef str = CFStringCreateWithCString(NULL, name,
48 kCFStringEncodingUTF8);
49 uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
50 CFRelease(str);
51 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000052}
53
reed@android.comea446b92009-03-09 15:25:11 +000054static uint32_t find_default_fontID() {
55 static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
reed@android.com0680d6c2008-12-19 19:46:15 +000056
reed@android.comea446b92009-03-09 15:25:11 +000057 uint32_t fontID;
58 for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
59 fontID = find_from_name(gDefaultNames[i]);
60 if (fontID) {
61 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000062 }
reed@android.com0680d6c2008-12-19 19:46:15 +000063 }
reed@android.comea446b92009-03-09 15:25:11 +000064 sk_throw();
65 return 0;
66}
67
68static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
69 uint32_t fontID = 0;
70 if (NULL != name) {
71 fontID = find_from_name(name);
72 }
73 if (0 == fontID) {
74 fontID = find_default_fontID();
75 }
76 // we lie (for now) and report that we found the exact style bits
77 return new SkTypeface_Mac(style, fontID);
reed@android.com0680d6c2008-12-19 19:46:15 +000078}
79
80#pragma mark -
81
82class SkScalerContext_Mac : public SkScalerContext {
83public:
84 SkScalerContext_Mac(const SkDescriptor* desc);
85 virtual ~SkScalerContext_Mac();
86
87protected:
88 virtual unsigned generateGlyphCount() const;
89 virtual uint16_t generateCharToGlyph(SkUnichar uni);
90 virtual void generateAdvance(SkGlyph* glyph);
91 virtual void generateMetrics(SkGlyph* glyph);
92 virtual void generateImage(const SkGlyph& glyph);
93 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
reed@android.com0680d6c2008-12-19 19:46:15 +000094 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
reed@android.com0680d6c2008-12-19 19:46:15 +000095
96private:
97 ATSUTextLayout fLayout;
98 ATSUStyle fStyle;
reed@android.com76aa34b2008-12-23 01:27:39 +000099 CGColorSpaceRef fGrayColorSpace;
reed@android.com0bf64d42009-03-09 17:22:22 +0000100 CGAffineTransform fTransform;
reed@android.com0680d6c2008-12-19 19:46:15 +0000101
102 static OSStatus MoveTo(const Float32Point *pt, void *cb);
103 static OSStatus Line(const Float32Point *pt, void *cb);
104 static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
105 static OSStatus Close(void *cb);
106};
107
108SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
reed@android.com0bf64d42009-03-09 17:22:22 +0000109 : SkScalerContext(desc), fLayout(0), fStyle(0)
110{
reed@android.com0680d6c2008-12-19 19:46:15 +0000111 SkAutoMutexAcquire ac(gFTMutex);
112 OSStatus err;
113
reed@android.comea446b92009-03-09 15:25:11 +0000114 err = ::ATSUCreateStyle(&fStyle);
115 SkASSERT(0 == err);
reed@android.com0bf64d42009-03-09 17:22:22 +0000116
117 SkMatrix m;
118 fRec.getSingleMatrix(&m);
119
120 fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
121 SkScalarToFloat(m[SkMatrix::kMSkewX]),
122 SkScalarToFloat(m[SkMatrix::kMSkewY]),
123 SkScalarToFloat(m[SkMatrix::kMScaleY]),
124 SkScalarToFloat(m[SkMatrix::kMTransX]),
125 SkScalarToFloat(m[SkMatrix::kMTransY]));
126
reed@android.com590ef3f2009-03-10 04:02:30 +0000127 ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
128 switch (fRec.fHints) {
129 case kNo_Hints:
130 renderOpts |= kATSStyleNoHinting;
131 break;
132 case kSubpixel_Hints:
133 // hmmm, need to support subpixel... from path?
134 renderOpts |= kATSStyleNoHinting;
135 break;
136 case kNormal_Hints:
137 renderOpts |= kATSStyleApplyHints;
138 break;
139 }
140
141 ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
142 // we put everything in the matrix, so our pt size is just 1.0
143 Fixed fixedSize = SK_Fixed1;
144 static const ATSUAttributeTag tags[] = {
145 kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
146 };
147 static const ByteCount sizes[] = {
148 sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
149 };
150 const ATSUAttributeValuePtr values[] = {
151 &fontID, &fixedSize, &fTransform, &renderOpts
152 };
reed@android.com0bf64d42009-03-09 17:22:22 +0000153 err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
154 tags, sizes, values);
reed@android.comea446b92009-03-09 15:25:11 +0000155 SkASSERT(0 == err);
reed@android.com0680d6c2008-12-19 19:46:15 +0000156
157 err = ::ATSUCreateTextLayout(&fLayout);
reed@android.comea446b92009-03-09 15:25:11 +0000158 SkASSERT(0 == err);
reed@android.com76aa34b2008-12-23 01:27:39 +0000159
160 fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
reed@android.com0680d6c2008-12-19 19:46:15 +0000161}
162
reed@android.comea446b92009-03-09 15:25:11 +0000163SkScalerContext_Mac::~SkScalerContext_Mac() {
reed@android.com76aa34b2008-12-23 01:27:39 +0000164 ::CGColorSpaceRelease(fGrayColorSpace);
reed@android.com0680d6c2008-12-19 19:46:15 +0000165 ::ATSUDisposeTextLayout(fLayout);
166 ::ATSUDisposeStyle(fStyle);
167}
168
reed@android.com467a3dc2009-04-08 05:27:00 +0000169// man, we need to consider caching this, since it is just dependent on
170// fFontID, and not on any of the other settings like matrix or flags
reed@android.comea446b92009-03-09 15:25:11 +0000171unsigned SkScalerContext_Mac::generateGlyphCount() const {
reed@android.com467a3dc2009-04-08 05:27:00 +0000172 // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
173 uint16_t numGlyphs;
174 if (SkFontHost::GetTableData(fRec.fFontID,
175 SkSetFourByteTag('m', 'a', 'x', 'p'),
176 4, 2, &numGlyphs) != 2) {
177 return 0xFFFF;
178 }
179 return SkEndian_SwapBE16(numGlyphs);
reed@android.com0680d6c2008-12-19 19:46:15 +0000180}
181
182uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
183{
184 SkAutoMutexAcquire ac(gFTMutex);
185
186 OSStatus err;
187 UniChar achar = uni;
188 err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
189 err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
190
191 ATSLayoutRecord *layoutPtr;
192 ItemCount count;
193 ATSGlyphRef glyph;
194
195 err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
196 glyph = layoutPtr->glyphID;
197 ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
198 return glyph;
199}
200
reed@android.com76aa34b2008-12-23 01:27:39 +0000201static void set_glyph_metrics_on_error(SkGlyph* glyph) {
202 glyph->fRsbDelta = 0;
203 glyph->fLsbDelta = 0;
204 glyph->fWidth = 0;
205 glyph->fHeight = 0;
206 glyph->fTop = 0;
207 glyph->fLeft = 0;
208 glyph->fAdvanceX = 0;
209 glyph->fAdvanceY = 0;
210}
211
reed@android.com0680d6c2008-12-19 19:46:15 +0000212void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
213 this->generateMetrics(glyph);
214}
215
reed@android.com76aa34b2008-12-23 01:27:39 +0000216void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
217 GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
reed@android.com330578d2009-03-09 18:12:13 +0000218 ATSGlyphScreenMetrics screenMetrics;
219 ATSGlyphIdealMetrics idealMetrics;
reed@android.com76aa34b2008-12-23 01:27:39 +0000220
221 OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
reed@android.com330578d2009-03-09 18:12:13 +0000222 &screenMetrics);
reed@android.com76aa34b2008-12-23 01:27:39 +0000223 if (noErr != err) {
224 set_glyph_metrics_on_error(glyph);
reed@android.com330578d2009-03-09 18:12:13 +0000225 return;
reed@android.com0680d6c2008-12-19 19:46:15 +0000226 }
reed@android.com330578d2009-03-09 18:12:13 +0000227 err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
228 if (noErr != err) {
229 set_glyph_metrics_on_error(glyph);
230 return;
231 }
reed@android.com590ef3f2009-03-10 04:02:30 +0000232
reed@android.com330578d2009-03-09 18:12:13 +0000233 if (kNormal_Hints == fRec.fHints) {
234 glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
235 glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
236 } else {
237 glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
238 glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
239 }
240
241 // specify an extra 1-pixel border, go tive CG room for its antialiasing
242 // i.e. without this, I was seeing some edges chopped off!
243 glyph->fWidth = screenMetrics.width + 2;
244 glyph->fHeight = screenMetrics.height + 2;
245 glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
246 glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
247}
248
249void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
250{
251 SkAutoMutexAcquire ac(gFTMutex);
252 SkASSERT(fLayout);
253
reed@android.com4516f472009-06-29 16:25:36 +0000254 sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
reed@android.com330578d2009-03-09 18:12:13 +0000255 CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
256 glyph.fWidth, glyph.fHeight, 8,
257 glyph.rowBytes(), fGrayColorSpace,
258 kCGImageAlphaNone);
259 if (!contextRef) {
260 SkASSERT(false);
261 return;
262 }
263
264 ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
265 ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
266
reed@android.com590ef3f2009-03-10 04:02:30 +0000267 CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
reed@android.com330578d2009-03-09 18:12:13 +0000268 CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
269 CGContextSetFont(contextRef, fontRef);
270 CGContextSetFontSize(contextRef, 1);
271 CGContextSetTextMatrix(contextRef, fTransform);
272 CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
273 glyph.fTop + glyph.fHeight, &glyphID, 1);
274
275 ::CGContextRelease(contextRef);
reed@android.com0680d6c2008-12-19 19:46:15 +0000276}
277
reed@android.com04225dc2009-03-20 04:59:37 +0000278#if 0
reed@android.com0bf64d42009-03-09 17:22:22 +0000279static void convert_metrics(SkPaint::FontMetrics* dst,
280 const ATSFontMetrics& src) {
281 dst->fTop = -SkFloatToScalar(src.ascent);
282 dst->fAscent = -SkFloatToScalar(src.ascent);
283 dst->fDescent = SkFloatToScalar(src.descent);
284 dst->fBottom = SkFloatToScalar(src.descent);
285 dst->fLeading = SkFloatToScalar(src.leading);
286}
reed@android.com04225dc2009-03-20 04:59:37 +0000287#endif
reed@android.com0bf64d42009-03-09 17:22:22 +0000288
289static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
290 ByteCount size;
291 OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
292 if (err) {
293 return NULL;
294 }
295 void* data = sk_malloc_throw(size);
296 err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
297 if (err) {
298 sk_free(data);
299 data = NULL;
300 }
301 return data;
302}
303
304static int get_be16(const void* data, size_t offset) {
305 const char* ptr = reinterpret_cast<const char*>(data);
306 uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
307 int n = SkEndian_SwapBE16(value);
308 // now force it to be signed
309 return n << 16 >> 16;
310}
311
312#define SFNT_HEAD_UPEM_OFFSET 18
313#define SFNT_HEAD_YMIN_OFFSET 38
314#define SFNT_HEAD_YMAX_OFFSET 42
315#define SFNT_HEAD_STYLE_OFFSET 44
316
317#define SFNT_HHEA_ASCENT_OFFSET 4
318#define SFNT_HHEA_DESCENT_OFFSET 6
319#define SFNT_HHEA_LEADING_OFFSET 8
320
321static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
322 void* head = get_font_table(font, 'head');
323 if (NULL == head) {
324 return false;
325 }
326 void* hhea = get_font_table(font, 'hhea');
327 if (NULL == hhea) {
328 sk_free(head);
329 return false;
330 }
331
332 int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
333 int ys[5];
334
335 ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
336 ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
337 ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
338 ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
339 ys[4] = get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
340
341 // now do some cleanup, to ensure y[max,min] are really that
342 if (ys[0] > ys[1]) {
343 ys[0] = ys[1];
344 }
345 if (ys[3] < ys[2]) {
346 ys[3] = ys[2];
347 }
348
349 for (int i = 0; i < 5; i++) {
350 pts[i].set(0, SkIntToScalar(ys[i]) / upem);
351 }
352
353 sk_free(hhea);
354 sk_free(head);
355 return true;
356}
357
reed@android.com76aa34b2008-12-23 01:27:39 +0000358void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
359 SkPaint::FontMetrics* my) {
reed@android.com0bf64d42009-03-09 17:22:22 +0000360 SkPoint pts[5];
361
362 if (!init_vertical_metrics(fRec.fFontID, pts)) {
363 // these are not as accurate as init_vertical_metrics :(
364 ATSFontMetrics metrics;
365 ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
366 &metrics);
367 pts[0].set(0, -SkFloatToScalar(metrics.ascent));
368 pts[1].set(0, -SkFloatToScalar(metrics.ascent));
369 pts[2].set(0, -SkFloatToScalar(metrics.descent));
370 pts[3].set(0, -SkFloatToScalar(metrics.descent));
371 pts[4].set(0, SkFloatToScalar(metrics.leading)); //+ or -?
372 }
373
374 SkMatrix m;
375 fRec.getSingleMatrix(&m);
376 m.mapPoints(pts, 5);
377
378 if (mx) {
379 mx->fTop = pts[0].fX;
380 mx->fAscent = pts[1].fX;
381 mx->fDescent = pts[2].fX;
382 mx->fBottom = pts[3].fX;
383 mx->fLeading = pts[4].fX;
reed@android.comb2f92f02009-04-23 05:06:33 +0000384 // FIXME:
385 mx->fAvgCharWidth = 0;
386 mx->fXMin = 0;
387 mx->fXMax = 0;
388 mx->fXHeight = 0;
reed@android.com0bf64d42009-03-09 17:22:22 +0000389 }
390 if (my) {
391 my->fTop = pts[0].fY;
392 my->fAscent = pts[1].fY;
393 my->fDescent = pts[2].fY;
394 my->fBottom = pts[3].fY;
395 my->fLeading = pts[4].fY;
reed@android.comb2f92f02009-04-23 05:06:33 +0000396 // FIXME:
397 my->fAvgCharWidth = 0;
398 my->fXMin = 0;
399 my->fXMax = 0;
400 my->fXHeight = 0;
reed@android.com0bf64d42009-03-09 17:22:22 +0000401 }
reed@android.com0680d6c2008-12-19 19:46:15 +0000402}
403
reed@android.com0680d6c2008-12-19 19:46:15 +0000404void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
405{
406 SkAutoMutexAcquire ac(gFTMutex);
407 OSStatus err,result;
408
409 err = ::ATSUGlyphGetCubicPaths(
410 fStyle,glyph.fID,
411 &SkScalerContext_Mac::MoveTo,
412 &SkScalerContext_Mac::Line,
413 &SkScalerContext_Mac::Curve,
414 &SkScalerContext_Mac::Close,
415 path,&result);
416 SkASSERT(err == noErr);
417}
418
reed@android.com0680d6c2008-12-19 19:46:15 +0000419OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
420{
421 reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
422 return noErr;
423}
424
425OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
426{
427 reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
428 return noErr;
429}
430
reed@android.com03ca3d12008-12-22 15:35:46 +0000431OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
432 const Float32Point *pt2,
433 const Float32Point *pt3, void *cb)
reed@android.com0680d6c2008-12-19 19:46:15 +0000434{
reed@android.com03ca3d12008-12-22 15:35:46 +0000435 reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
436 F32PtToSkPoint(*pt2),
437 F32PtToSkPoint(*pt3));
reed@android.com0680d6c2008-12-19 19:46:15 +0000438 return noErr;
439}
440
441OSStatus SkScalerContext_Mac::Close(void *cb)
442{
443 reinterpret_cast<SkPath*>(cb)->close();
444 return noErr;
445}
446
447#pragma mark -
448
449void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
450 SkASSERT(!"SkFontHost::Serialize unimplemented");
451}
452
453SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
454 SkASSERT(!"SkFontHost::Deserialize unimplemented");
455 return NULL;
456}
457
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000458SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
reed@android.comea446b92009-03-09 15:25:11 +0000459 return NULL;
reed@android.com0680d6c2008-12-19 19:46:15 +0000460}
461
reed@android.com03ca3d12008-12-22 15:35:46 +0000462SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
reed@android.comea446b92009-03-09 15:25:11 +0000463 return NULL;
reed@android.com03ca3d12008-12-22 15:35:46 +0000464}
465
reed@android.comea446b92009-03-09 15:25:11 +0000466SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
reed@android.com0680d6c2008-12-19 19:46:15 +0000467 return new SkScalerContext_Mac(desc);
468}
469
reed@android.coma14ea0e2009-03-17 17:59:53 +0000470uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
471 uint32_t newFontID = find_default_fontID();
472 if (newFontID == fontID) {
473 newFontID = 0;
474 }
475 return newFontID;
reed@android.com0680d6c2008-12-19 19:46:15 +0000476}
477
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000478SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
479 const char familyName[], SkTypeface::Style style) {
reed@android.comea446b92009-03-09 15:25:11 +0000480 // todo: we don't know how to respect style bits
481 if (NULL == familyName && NULL != familyFace) {
482 familyFace->ref();
483 return const_cast<SkTypeface*>(familyFace);
484 } else {
485 return CreateTypeface_(familyName, style);
reed@android.com0680d6c2008-12-19 19:46:15 +0000486 }
reed@android.com0680d6c2008-12-19 19:46:15 +0000487}
488
489size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
490 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
491 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
492 else
493 return 0; // nothing to do
494}
495
496int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
497 return 0;
498}
499
500void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
501 tables[0] = NULL; // black gamma (e.g. exp=1.4)
502 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
503}
504
reed@android.comd6638e62009-04-08 05:03:52 +0000505///////////////////////////////////////////////////////////////////////////////
506
507struct SkSFNTHeader {
508 uint32_t fVersion;
509 uint16_t fNumTables;
510 uint16_t fSearchRange;
511 uint16_t fEntrySelector;
512 uint16_t fRangeShift;
513};
514
515struct SkSFNTDirEntry {
516 uint32_t fTag;
517 uint32_t fChecksum;
518 uint32_t fOffset;
519 uint32_t fLength;
520};
521
522struct SfntHeader {
523 SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
524 ByteCount size;
525 if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
526 return;
527 }
528
529 SkAutoMalloc storage(size);
530 SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
531 if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
532 return;
533 }
534
535 fCount = SkEndian_SwapBE16(header->fNumTables);
536 fData = header;
537 storage.detach();
538 }
539
540 ~SfntHeader() {
541 sk_free(fData);
542 }
543
544 int count() const { return fCount; }
545 const SkSFNTDirEntry* entries() const {
546 return reinterpret_cast<const SkSFNTDirEntry*>
547 (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
548 }
549
550private:
551 int fCount;
552 void* fData;
553};
554
555int SkFontHost::CountTables(SkFontID fontID) {
556 SfntHeader header(fontID, false);
557 return header.count();
558}
559
560int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
561 SfntHeader header(fontID, true);
562 int count = header.count();
563 const SkSFNTDirEntry* entry = header.entries();
564 for (int i = 0; i < count; i++) {
565 tags[i] = SkEndian_SwapBE32(entry[i].fTag);
566 }
567 return count;
568}
569
570size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
571 ByteCount size;
572 if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
573 return 0;
574 }
575 return size;
576}
577
578size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
579 size_t offset, size_t length, void* data) {
580 ByteCount size;
581 if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
582 return 0;
583 }
584 if (offset >= size) {
585 return 0;
586 }
587 if (offset + length > size) {
588 length = size - offset;
589 }
590 return length;
591}
592