blob: 0f509f1e22c6b353cf740acc88a71f57c59c96dd [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.comf13c6e12009-01-19 19:10:24 +000017#include <carbon/carbon.h>
reed@android.com0680d6c2008-12-19 19:46:15 +000018#include "SkFontHost.h"
19#include "SkDescriptor.h"
reed@android.comf13c6e12009-01-19 19:10:24 +000020#include "SkPoint.h"
reed@android.com0680d6c2008-12-19 19:46:15 +000021
22// Give 1MB font cache budget
23#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
24
25const char* gDefaultfont = "Arial"; // hard code for now
26static SkMutex gFTMutex;
27
reed@android.com03ca3d12008-12-22 15:35:46 +000028static inline SkPoint F32PtToSkPoint(const Float32Point p) {
29 SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
reed@android.com0680d6c2008-12-19 19:46:15 +000030 return sp;
31}
32
reed@android.com03ca3d12008-12-22 15:35:46 +000033static inline uint32_t _rotl(uint32_t v, uint32_t r) {
reed@android.com0680d6c2008-12-19 19:46:15 +000034 return (v << r | v >> (32 - r));
35}
36
reed@android.com0680d6c2008-12-19 19:46:15 +000037class SkTypeface_Mac : public SkTypeface {
38public:
reed@android.comea446b92009-03-09 15:25:11 +000039 SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
40 : SkTypeface(style, id) {}
reed@android.com0680d6c2008-12-19 19:46:15 +000041};
42
43#pragma mark -
44
reed@android.comea446b92009-03-09 15:25:11 +000045static uint32_t find_from_name(const char name[]) {
46 CFStringRef str = CFStringCreateWithCString(NULL, name,
47 kCFStringEncodingUTF8);
48 uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
49 CFRelease(str);
50 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000051}
52
reed@android.comea446b92009-03-09 15:25:11 +000053static uint32_t find_default_fontID() {
54 static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
reed@android.com0680d6c2008-12-19 19:46:15 +000055
reed@android.comea446b92009-03-09 15:25:11 +000056 uint32_t fontID;
57 for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
58 fontID = find_from_name(gDefaultNames[i]);
59 if (fontID) {
60 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000061 }
reed@android.com0680d6c2008-12-19 19:46:15 +000062 }
reed@android.comea446b92009-03-09 15:25:11 +000063 sk_throw();
64 return 0;
65}
66
67static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
68 uint32_t fontID = 0;
69 if (NULL != name) {
70 fontID = find_from_name(name);
71 }
72 if (0 == fontID) {
73 fontID = find_default_fontID();
74 }
75 // we lie (for now) and report that we found the exact style bits
76 return new SkTypeface_Mac(style, fontID);
reed@android.com0680d6c2008-12-19 19:46:15 +000077}
78
79#pragma mark -
80
81class SkScalerContext_Mac : public SkScalerContext {
82public:
83 SkScalerContext_Mac(const SkDescriptor* desc);
84 virtual ~SkScalerContext_Mac();
85
86protected:
87 virtual unsigned generateGlyphCount() const;
88 virtual uint16_t generateCharToGlyph(SkUnichar uni);
89 virtual void generateAdvance(SkGlyph* glyph);
90 virtual void generateMetrics(SkGlyph* glyph);
91 virtual void generateImage(const SkGlyph& glyph);
92 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
93 virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
94 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
95// virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac
96
97private:
98 ATSUTextLayout fLayout;
99 ATSUStyle fStyle;
reed@android.com76aa34b2008-12-23 01:27:39 +0000100 CGColorSpaceRef fGrayColorSpace;
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.comea446b92009-03-09 15:25:11 +0000109 : SkScalerContext(desc), fLayout(0), fStyle(0) {
reed@android.com0680d6c2008-12-19 19:46:15 +0000110 SkAutoMutexAcquire ac(gFTMutex);
111 OSStatus err;
112
reed@android.comea446b92009-03-09 15:25:11 +0000113 err = ::ATSUCreateStyle(&fStyle);
114 SkASSERT(0 == err);
reed@android.com0680d6c2008-12-19 19:46:15 +0000115
reed@android.comea446b92009-03-09 15:25:11 +0000116 Fixed fixedSize = SkScalarToFixed(fRec.fTextSize);
reed@android.com0680d6c2008-12-19 19:46:15 +0000117 static const ATSUAttributeTag sizeTag = kATSUSizeTag;
118 static const ByteCount sizeTagSize = sizeof(Fixed);
119 const ATSUAttributeValuePtr values[] = { &fixedSize };
120 err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);
reed@android.comea446b92009-03-09 15:25:11 +0000121 SkASSERT(0 == err);
reed@android.com0680d6c2008-12-19 19:46:15 +0000122
123 err = ::ATSUCreateTextLayout(&fLayout);
reed@android.comea446b92009-03-09 15:25:11 +0000124 SkASSERT(0 == err);
reed@android.com76aa34b2008-12-23 01:27:39 +0000125
126 fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
reed@android.com0680d6c2008-12-19 19:46:15 +0000127}
128
reed@android.comea446b92009-03-09 15:25:11 +0000129SkScalerContext_Mac::~SkScalerContext_Mac() {
reed@android.com76aa34b2008-12-23 01:27:39 +0000130 ::CGColorSpaceRelease(fGrayColorSpace);
reed@android.com0680d6c2008-12-19 19:46:15 +0000131 ::ATSUDisposeTextLayout(fLayout);
132 ::ATSUDisposeStyle(fStyle);
133}
134
reed@android.comea446b92009-03-09 15:25:11 +0000135unsigned SkScalerContext_Mac::generateGlyphCount() const {
reed@android.com0680d6c2008-12-19 19:46:15 +0000136 return 0xFFFF;
137}
138
139uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
140{
141 SkAutoMutexAcquire ac(gFTMutex);
142
143 OSStatus err;
144 UniChar achar = uni;
145 err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
146 err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
147
148 ATSLayoutRecord *layoutPtr;
149 ItemCount count;
150 ATSGlyphRef glyph;
151
152 err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
153 glyph = layoutPtr->glyphID;
154 ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
155 return glyph;
156}
157
reed@android.com76aa34b2008-12-23 01:27:39 +0000158static void set_glyph_metrics_on_error(SkGlyph* glyph) {
159 glyph->fRsbDelta = 0;
160 glyph->fLsbDelta = 0;
161 glyph->fWidth = 0;
162 glyph->fHeight = 0;
163 glyph->fTop = 0;
164 glyph->fLeft = 0;
165 glyph->fAdvanceX = 0;
166 glyph->fAdvanceY = 0;
167}
168
reed@android.com0680d6c2008-12-19 19:46:15 +0000169void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
170 this->generateMetrics(glyph);
171}
172
reed@android.com76aa34b2008-12-23 01:27:39 +0000173void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
174 GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
reed@android.com03ca3d12008-12-22 15:35:46 +0000175 ATSGlyphScreenMetrics metrics;
reed@android.com76aa34b2008-12-23 01:27:39 +0000176
177 OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
178 &metrics);
179 if (noErr != err) {
180 set_glyph_metrics_on_error(glyph);
181 } else {
reed@android.com0680d6c2008-12-19 19:46:15 +0000182 glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
reed@android.com03ca3d12008-12-22 15:35:46 +0000183 glyph->fAdvanceY = -SkFloatToFixed(metrics.deviceAdvance.y);
184 glyph->fWidth = metrics.width;
185 glyph->fHeight = metrics.height;
reed@android.com4df53b02008-12-22 22:12:55 +0000186 glyph->fLeft = sk_float_round2int(metrics.topLeft.x);
reed@android.com76aa34b2008-12-23 01:27:39 +0000187 glyph->fTop = -sk_float_round2int(metrics.topLeft.y);
reed@android.com0680d6c2008-12-19 19:46:15 +0000188 }
189}
190
reed@android.com76aa34b2008-12-23 01:27:39 +0000191void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
192 SkPaint::FontMetrics* my) {
reed@android.com03ca3d12008-12-22 15:35:46 +0000193#if 0
194 OSStatus ATSFontGetVerticalMetrics (
195 ATSFontRef iFont,
196 ATSOptionFlags iOptions,
197 ATSFontMetrics *oMetrics
198 );
199#endif
reed@android.com0680d6c2008-12-19 19:46:15 +0000200 //SkASSERT(false);
201 if (mx)
202 memset(mx, 0, sizeof(SkPaint::FontMetrics));
203 if (my)
204 memset(my, 0, sizeof(SkPaint::FontMetrics));
205 return;
206}
207
208void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
209{
210 SkAutoMutexAcquire ac(gFTMutex);
reed@android.com0680d6c2008-12-19 19:46:15 +0000211 SkASSERT(fLayout);
reed@android.com0680d6c2008-12-19 19:46:15 +0000212
reed@android.com76aa34b2008-12-23 01:27:39 +0000213 bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
214 CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
215 glyph.fWidth, glyph.fHeight, 8,
216 glyph.rowBytes(), fGrayColorSpace,
217 kCGImageAlphaNone);
reed@android.com0680d6c2008-12-19 19:46:15 +0000218 if (!contextRef) {
219 SkASSERT(false);
220 return;
221 }
reed@android.com76aa34b2008-12-23 01:27:39 +0000222
reed@android.com0680d6c2008-12-19 19:46:15 +0000223 ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
reed@android.com0680d6c2008-12-19 19:46:15 +0000224 ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
225
reed@android.com60dfdbc2009-03-09 14:51:39 +0000226 CGGlyph glyphID = glyph.getGlyphID();
227 CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
228 CGContextSetFont(contextRef, fontRef);
229 CGContextSetFontSize(contextRef, SkScalarToFloat(fRec.fTextSize));
230 CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
231 glyph.fTop + glyph.fHeight, &glyphID, 1);
reed@android.comea446b92009-03-09 15:25:11 +0000232
reed@android.com0680d6c2008-12-19 19:46:15 +0000233 ::CGContextRelease(contextRef);
234}
235
236void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
237{
238 SkAutoMutexAcquire ac(gFTMutex);
239 OSStatus err,result;
240
241 err = ::ATSUGlyphGetCubicPaths(
242 fStyle,glyph.fID,
243 &SkScalerContext_Mac::MoveTo,
244 &SkScalerContext_Mac::Line,
245 &SkScalerContext_Mac::Curve,
246 &SkScalerContext_Mac::Close,
247 path,&result);
248 SkASSERT(err == noErr);
249}
250
251void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)
252{
253 ATSUTextMeasurement textAscent, textDescent;
254 ByteCount actual = 0;
255 OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);
256 ascent->set(0,textAscent);
257 err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);
258 descent->set(0,textDescent);
259}
260
261OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
262{
263 reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
264 return noErr;
265}
266
267OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
268{
269 reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
270 return noErr;
271}
272
reed@android.com03ca3d12008-12-22 15:35:46 +0000273OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
274 const Float32Point *pt2,
275 const Float32Point *pt3, void *cb)
reed@android.com0680d6c2008-12-19 19:46:15 +0000276{
reed@android.com03ca3d12008-12-22 15:35:46 +0000277 reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
278 F32PtToSkPoint(*pt2),
279 F32PtToSkPoint(*pt3));
reed@android.com0680d6c2008-12-19 19:46:15 +0000280 return noErr;
281}
282
283OSStatus SkScalerContext_Mac::Close(void *cb)
284{
285 reinterpret_cast<SkPath*>(cb)->close();
286 return noErr;
287}
288
289#pragma mark -
290
291void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
292 SkASSERT(!"SkFontHost::Serialize unimplemented");
293}
294
295SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
296 SkASSERT(!"SkFontHost::Deserialize unimplemented");
297 return NULL;
298}
299
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000300SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
reed@android.comea446b92009-03-09 15:25:11 +0000301 return NULL;
reed@android.com0680d6c2008-12-19 19:46:15 +0000302}
303
reed@android.com03ca3d12008-12-22 15:35:46 +0000304SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
reed@android.comea446b92009-03-09 15:25:11 +0000305 return NULL;
reed@android.com03ca3d12008-12-22 15:35:46 +0000306}
307
reed@android.comea446b92009-03-09 15:25:11 +0000308SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
reed@android.com0680d6c2008-12-19 19:46:15 +0000309 return new SkScalerContext_Mac(desc);
310}
311
312SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
313{
reed@android.comea446b92009-03-09 15:25:11 +0000314 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
reed@android.com0680d6c2008-12-19 19:46:15 +0000315 SkDescriptor* desc = ad.getDesc();
reed@android.comea446b92009-03-09 15:25:11 +0000316
reed@android.com0680d6c2008-12-19 19:46:15 +0000317 desc->init();
318 SkScalerContext::Rec* newRec =
reed@android.comea446b92009-03-09 15:25:11 +0000319 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
320 sizeof(rec), &rec);
321 newRec->fFontID = find_default_fontID();
reed@android.com0680d6c2008-12-19 19:46:15 +0000322 desc->computeChecksum();
reed@android.comea446b92009-03-09 15:25:11 +0000323
reed@android.com0680d6c2008-12-19 19:46:15 +0000324 return SkFontHost::CreateScalerContext(desc);
325}
326
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000327SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
328 const char familyName[], SkTypeface::Style style) {
reed@android.comea446b92009-03-09 15:25:11 +0000329 // todo: we don't know how to respect style bits
330 if (NULL == familyName && NULL != familyFace) {
331 familyFace->ref();
332 return const_cast<SkTypeface*>(familyFace);
333 } else {
334 return CreateTypeface_(familyName, style);
reed@android.com0680d6c2008-12-19 19:46:15 +0000335 }
reed@android.com0680d6c2008-12-19 19:46:15 +0000336}
337
338size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
339 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
340 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
341 else
342 return 0; // nothing to do
343}
344
345int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
346 return 0;
347}
348
349void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
350 tables[0] = NULL; // black gamma (e.g. exp=1.4)
351 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
352}
353