blob: e7d4c594c90aefba22c3a87234f35f276b12239b [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/ports/SkFontHost_FreeType.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkBitmap.h"
19#include "SkCanvas.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000020#include "SkColorPriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkDescriptor.h"
22#include "SkFDot6.h"
23#include "SkFontHost.h"
24#include "SkMask.h"
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +000025#include "SkAdvancedTypefaceMetrics.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000026#include "SkScalerContext.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkStream.h"
28#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkTemplates.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000030#include "SkThread.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
32#include <ft2build.h>
33#include FT_FREETYPE_H
34#include FT_OUTLINE_H
35#include FT_SIZES_H
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000036#include FT_TRUETYPE_TABLES_H
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000037#include FT_TYPE1_TABLES_H
agl@chromium.orge76073b2010-06-04 20:31:17 +000038#include FT_BITMAP_H
agl@chromium.org36bb6972010-06-04 20:57:16 +000039// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
40#include FT_SYNTHESIS_H
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000041#include FT_XFREE86_H
agl@chromium.org309485b2009-07-21 17:41:32 +000042#include FT_LCD_FILTER_H
agl@chromium.org309485b2009-07-21 17:41:32 +000043
reed@android.com8a1c16f2008-12-17 15:59:43 +000044#ifdef FT_ADVANCES_H
45#include FT_ADVANCES_H
46#endif
47
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000048#if 0
49// Also include the files by name for build tools which require this.
50#include <freetype/freetype.h>
51#include <freetype/ftoutln.h>
52#include <freetype/ftsizes.h>
53#include <freetype/tttables.h>
54#include <freetype/ftadvanc.h>
agl@chromium.org309485b2009-07-21 17:41:32 +000055#include <freetype/ftlcdfil.h>
agl@chromium.orge76073b2010-06-04 20:31:17 +000056#include <freetype/ftbitmap.h>
agl@chromium.org36bb6972010-06-04 20:57:16 +000057#include <freetype/ftsynth.h>
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000058#endif
59
reed@android.com8a1c16f2008-12-17 15:59:43 +000060//#define ENABLE_GLYPH_SPEW // for tracing calls
61//#define DUMP_STRIKE_CREATION
62
63#ifdef SK_DEBUG
64 #define SkASSERT_CONTINUE(pred) \
65 do { \
66 if (!(pred)) \
67 SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \
68 } while (false)
69#else
70 #define SkASSERT_CONTINUE(pred)
71#endif
72
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +000073using namespace skia_advanced_typeface_metrics_utils;
74
reed@google.combde3c8e2011-05-18 11:58:10 +000075// SK_FREETYPE_LCD_LERP should be 0...256
76// 0 means no color reduction (e.g. just as returned from FreeType)
77// 256 means 100% color reduction (e.g. gray)
reed@google.comc5181342011-05-17 20:52:46 +000078//
79#ifndef SK_FREETYPE_LCD_LERP
reed@google.combde3c8e2011-05-18 11:58:10 +000080 #define SK_FREETYPE_LCD_LERP 96
reed@google.comc5181342011-05-17 20:52:46 +000081#endif
82
reed@google.comeffc5012011-06-27 16:44:46 +000083static bool isLCD(const SkScalerContext::Rec& rec) {
84 switch (rec.fMaskFormat) {
85 case SkMask::kLCD16_Format:
86 case SkMask::kLCD32_Format:
87 return true;
88 default:
89 return false;
90 }
91}
92
reed@android.com8a1c16f2008-12-17 15:59:43 +000093//////////////////////////////////////////////////////////////////////////
94
95struct SkFaceRec;
96
97static SkMutex gFTMutex;
98static int gFTCount;
99static FT_Library gFTLibrary;
100static SkFaceRec* gFaceRecHead;
agl@chromium.orgf18d8762009-07-28 18:38:08 +0000101static bool gLCDSupportValid; // true iff |gLCDSupport| has been set.
102static bool gLCDSupport; // true iff LCD is supported by the runtime.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103
104/////////////////////////////////////////////////////////////////////////
105
agl@chromium.orge76073b2010-06-04 20:31:17 +0000106// See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
107// This value was chosen by eyeballing the result in Firefox and trying to match it.
108static const FT_Pos kBitmapEmboldenStrength = 1 << 6;
109
agl@chromium.org309485b2009-07-21 17:41:32 +0000110static bool
111InitFreetype() {
112 FT_Error err = FT_Init_FreeType(&gFTLibrary);
reed@google.comea2333d2011-03-14 16:44:56 +0000113 if (err) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000114 return false;
reed@google.comea2333d2011-03-14 16:44:56 +0000115 }
agl@chromium.org309485b2009-07-21 17:41:32 +0000116
agl@chromium.org309485b2009-07-21 17:41:32 +0000117 // Setup LCD filtering. This reduces colour fringes for LCD rendered
118 // glyphs.
119 err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
agl@chromium.orgf18d8762009-07-28 18:38:08 +0000120 gLCDSupport = err == 0;
reed@android.com61608aa2009-07-31 14:52:54 +0000121 gLCDSupportValid = true;
agl@chromium.org309485b2009-07-21 17:41:32 +0000122
123 return true;
124}
125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126class SkScalerContext_FreeType : public SkScalerContext {
127public:
128 SkScalerContext_FreeType(const SkDescriptor* desc);
129 virtual ~SkScalerContext_FreeType();
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000130
reed@android.com62900b42009-02-11 15:07:19 +0000131 bool success() const {
reed@android.coma0f5d152009-06-22 17:38:10 +0000132 return fFaceRec != NULL &&
133 fFTSize != NULL &&
134 fFace != NULL;
reed@android.com62900b42009-02-11 15:07:19 +0000135 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136
137protected:
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000138 virtual unsigned generateGlyphCount();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 virtual uint16_t generateCharToGlyph(SkUnichar uni);
140 virtual void generateAdvance(SkGlyph* glyph);
141 virtual void generateMetrics(SkGlyph* glyph);
142 virtual void generateImage(const SkGlyph& glyph);
143 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
144 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
145 SkPaint::FontMetrics* my);
reed@android.com9d3a9852010-01-08 14:07:42 +0000146 virtual SkUnichar generateGlyphToChar(uint16_t glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147
148private:
149 SkFaceRec* fFaceRec;
150 FT_Face fFace; // reference to shared face in gFaceRecHead
151 FT_Size fFTSize; // our own copy
152 SkFixed fScaleX, fScaleY;
153 FT_Matrix fMatrix22;
154 uint32_t fLoadGlyphFlags;
155
156 FT_Error setupSize();
senorblanco@chromium.org4526a842010-02-05 23:08:20 +0000157 void emboldenOutline(FT_Outline* outline);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158};
159
160///////////////////////////////////////////////////////////////////////////
161///////////////////////////////////////////////////////////////////////////
162
163#include "SkStream.h"
164
165struct SkFaceRec {
166 SkFaceRec* fNext;
167 FT_Face fFace;
168 FT_StreamRec fFTStream;
169 SkStream* fSkStream;
170 uint32_t fRefCnt;
171 uint32_t fFontID;
172
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000173 // assumes ownership of the stream, will call unref() when its done
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 SkFaceRec(SkStream* strm, uint32_t fontID);
175 ~SkFaceRec() {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000176 fSkStream->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 }
178};
179
180extern "C" {
181 static unsigned long sk_stream_read(FT_Stream stream,
182 unsigned long offset,
183 unsigned char* buffer,
184 unsigned long count ) {
185 SkStream* str = (SkStream*)stream->descriptor.pointer;
186
187 if (count) {
188 if (!str->rewind()) {
189 return 0;
190 } else {
191 unsigned long ret;
192 if (offset) {
193 ret = str->read(NULL, offset);
194 if (ret != offset) {
195 return 0;
196 }
197 }
198 ret = str->read(buffer, count);
199 if (ret != count) {
200 return 0;
201 }
202 count = ret;
203 }
204 }
205 return count;
206 }
207
208 static void sk_stream_close( FT_Stream stream) {}
209}
210
211SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
212 : fSkStream(strm), fFontID(fontID) {
213// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
214
reed@android.com4516f472009-06-29 16:25:36 +0000215 sk_bzero(&fFTStream, sizeof(fFTStream));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fFTStream.size = fSkStream->getLength();
217 fFTStream.descriptor.pointer = fSkStream;
218 fFTStream.read = sk_stream_read;
219 fFTStream.close = sk_stream_close;
220}
221
reed@android.com62900b42009-02-11 15:07:19 +0000222// Will return 0 on failure
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223static SkFaceRec* ref_ft_face(uint32_t fontID) {
224 SkFaceRec* rec = gFaceRecHead;
225 while (rec) {
226 if (rec->fFontID == fontID) {
227 SkASSERT(rec->fFace);
228 rec->fRefCnt += 1;
229 return rec;
230 }
231 rec = rec->fNext;
232 }
233
234 SkStream* strm = SkFontHost::OpenStream(fontID);
235 if (NULL == strm) {
236 SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 return 0;
238 }
239
240 // this passes ownership of strm to the rec
241 rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
242
243 FT_Open_Args args;
244 memset(&args, 0, sizeof(args));
245 const void* memoryBase = strm->getMemoryBase();
246
247 if (NULL != memoryBase) {
248//printf("mmap(%s)\n", keyString.c_str());
249 args.flags = FT_OPEN_MEMORY;
250 args.memory_base = (const FT_Byte*)memoryBase;
251 args.memory_size = strm->getLength();
252 } else {
253//printf("fopen(%s)\n", keyString.c_str());
254 args.flags = FT_OPEN_STREAM;
255 args.stream = &rec->fFTStream;
256 }
257
agl@chromium.org61a678a2010-08-06 18:08:18 +0000258 int face_index;
259 int length = SkFontHost::GetFileName(fontID, NULL, 0, &face_index);
260 FT_Error err = FT_Open_Face(gFTLibrary, &args, length ? face_index : 0,
261 &rec->fFace);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262
263 if (err) { // bad filename, try the default font
264 fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID);
265 SkDELETE(rec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 return 0;
267 } else {
268 SkASSERT(rec->fFace);
269 //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
270 rec->fNext = gFaceRecHead;
271 gFaceRecHead = rec;
272 rec->fRefCnt = 1;
273 return rec;
274 }
275}
276
277static void unref_ft_face(FT_Face face) {
278 SkFaceRec* rec = gFaceRecHead;
279 SkFaceRec* prev = NULL;
280 while (rec) {
281 SkFaceRec* next = rec->fNext;
282 if (rec->fFace == face) {
283 if (--rec->fRefCnt == 0) {
284 if (prev) {
285 prev->fNext = next;
286 } else {
287 gFaceRecHead = next;
288 }
289 FT_Done_Face(face);
290 SkDELETE(rec);
291 }
292 return;
293 }
294 prev = rec;
295 rec = next;
296 }
297 SkASSERT("shouldn't get here, face not in list");
298}
299
300///////////////////////////////////////////////////////////////////////////
301
vandebo@chromium.org16be6b82011-01-28 21:28:56 +0000302// Work around for old versions of freetype.
303static FT_Error getAdvances(FT_Face face, FT_UInt start, FT_UInt count,
304 FT_Int32 loadFlags, FT_Fixed* advances) {
305#ifdef FT_ADVANCES_H
306 return FT_Get_Advances(face, start, count, loadFlags, advances);
307#else
308 if (!face || start >= face->num_glyphs ||
309 start + count > face->num_glyphs || loadFlags != FT_LOAD_NO_SCALE) {
310 return 6; // "Invalid argument."
311 }
312 if (count == 0)
313 return 0;
314
315 for (int i = 0; i < count; i++) {
316 FT_Error err = FT_Load_Glyph(face, start + i, FT_LOAD_NO_SCALE);
317 if (err)
318 return err;
319 advances[i] = face->glyph->advance.x;
320 }
321
322 return 0;
323#endif
324}
325
326static bool canEmbed(FT_Face face) {
327#ifdef FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING
328 FT_UShort fsType = FT_Get_FSType_Flags(face);
329 return (fsType & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING |
330 FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0;
331#else
332 // No embedding is 0x2 and bitmap embedding only is 0x200.
333 TT_OS2* os2_table;
334 if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
335 return (os2_table->fsType & 0x202) == 0;
336 }
337 return false; // We tried, fail safe.
338#endif
339}
340
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000341static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) {
342 const FT_UInt glyph_id = FT_Get_Char_Index(face, letter);
343 if (!glyph_id)
344 return false;
345 FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE);
346 FT_Outline_Get_CBox(&face->glyph->outline, bbox);
347 return true;
348}
349
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000350static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000351 FT_Fixed advance = 0;
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000352 if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) {
353 return false;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000354 }
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000355 SkASSERT(data);
356 *data = advance;
357 return true;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000358}
359
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000360static void populate_glyph_to_unicode(FT_Face& face,
361 SkTDArray<SkUnichar>* glyphToUnicode) {
362 // Check and see if we have Unicode cmaps.
363 for (int i = 0; i < face->num_charmaps; ++i) {
364 // CMaps known to support Unicode:
365 // Platform ID Encoding ID Name
366 // ----------- ----------- -----------------------------------
367 // 0 0,1 Apple Unicode
368 // 0 3 Apple Unicode 2.0 (preferred)
369 // 3 1 Microsoft Unicode UCS-2
370 // 3 10 Microsoft Unicode UCS-4 (preferred)
371 //
372 // See Apple TrueType Reference Manual
373 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html
374 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html#ID
375 // Microsoft OpenType Specification
376 // http://www.microsoft.com/typography/otspec/cmap.htm
377
378 FT_UShort platformId = face->charmaps[i]->platform_id;
379 FT_UShort encodingId = face->charmaps[i]->encoding_id;
380
381 if (platformId != 0 && platformId != 3) {
382 continue;
383 }
384 if (platformId == 3 && encodingId != 1 && encodingId != 10) {
385 continue;
386 }
387 bool preferredMap = ((platformId == 3 && encodingId == 10) ||
388 (platformId == 0 && encodingId == 3));
389
390 FT_Set_Charmap(face, face->charmaps[i]);
391 if (glyphToUnicode->isEmpty()) {
392 glyphToUnicode->setCount(face->num_glyphs);
393 memset(glyphToUnicode->begin(), 0,
394 sizeof(SkUnichar) * face->num_glyphs);
395 }
396
397 // Iterate through each cmap entry.
398 FT_UInt glyphIndex;
399 for (SkUnichar charCode = FT_Get_First_Char(face, &glyphIndex);
400 glyphIndex != 0;
401 charCode = FT_Get_Next_Char(face, charCode, &glyphIndex)) {
402 if (charCode &&
403 ((*glyphToUnicode)[glyphIndex] == 0 || preferredMap)) {
404 (*glyphToUnicode)[glyphIndex] = charCode;
405 }
406 }
407 }
408}
409
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000410// static
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000411SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000412 uint32_t fontID,
413 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000414#if defined(SK_BUILD_FOR_MAC) || defined(ANDROID)
reed@google.com8a5d6922011-03-14 15:08:03 +0000415 return NULL;
416#else
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000417 SkAutoMutexAcquire ac(gFTMutex);
418 FT_Library libInit = NULL;
419 if (gFTCount == 0) {
420 if (!InitFreetype())
421 sk_throw();
422 libInit = gFTLibrary;
423 }
424 SkAutoTCallIProc<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit);
425 SkFaceRec* rec = ref_ft_face(fontID);
426 if (NULL == rec)
427 return NULL;
428 FT_Face face = rec->fFace;
429
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000430 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
431 info->fFontName.set(FT_Get_Postscript_Name(face));
432 info->fMultiMaster = FT_HAS_MULTIPLE_MASTERS(face);
433 info->fLastGlyphID = face->num_glyphs - 1;
434 info->fEmSize = 1000;
435
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000436 bool cid = false;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000437 const char* fontType = FT_Get_X11_Font_Format(face);
vandebo@chromium.orgc3a2ae52011-02-03 21:48:23 +0000438 if (strcmp(fontType, "Type 1") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000439 info->fType = SkAdvancedTypefaceMetrics::kType1_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000440 } else if (strcmp(fontType, "CID Type 1") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000441 info->fType = SkAdvancedTypefaceMetrics::kType1CID_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000442 cid = true;
443 } else if (strcmp(fontType, "CFF") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000444 info->fType = SkAdvancedTypefaceMetrics::kCFF_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000445 } else if (strcmp(fontType, "TrueType") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000446 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000447 cid = true;
448 TT_Header* ttHeader;
449 if ((ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face,
450 ft_sfnt_head)) != NULL) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000451 info->fEmSize = ttHeader->Units_Per_EM;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000452 }
453 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000454
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000455 info->fStyle = 0;
456 if (FT_IS_FIXED_WIDTH(face))
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000457 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000458 if (face->style_flags & FT_STYLE_FLAG_ITALIC)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000459 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000460 // We should set either Symbolic or Nonsymbolic; Nonsymbolic if the font's
461 // character set is a subset of 'Adobe standard Latin.'
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000462 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
463
464 PS_FontInfoRec ps_info;
465 TT_Postscript* tt_info;
466 if (FT_Get_PS_Font_Info(face, &ps_info) == 0) {
467 info->fItalicAngle = ps_info.italic_angle;
468 } else if ((tt_info =
469 (TT_Postscript*)FT_Get_Sfnt_Table(face,
470 ft_sfnt_post)) != NULL) {
471 info->fItalicAngle = SkFixedToScalar(tt_info->italicAngle);
472 } else {
473 info->fItalicAngle = 0;
474 }
475
476 info->fAscent = face->ascender;
477 info->fDescent = face->descender;
478
479 // Figure out a good guess for StemV - Min width of i, I, !, 1.
480 // This probably isn't very good with an italic font.
481 int16_t min_width = SHRT_MAX;
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000482 info->fStemV = 0;
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000483 char stem_chars[] = {'i', 'I', '!', '1'};
484 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
485 FT_BBox bbox;
486 if (GetLetterCBox(face, stem_chars[i], &bbox)) {
487 int16_t width = bbox.xMax - bbox.xMin;
488 if (width > 0 && width < min_width) {
489 min_width = width;
490 info->fStemV = min_width;
491 }
492 }
493 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000494
495 TT_PCLT* pclt_info;
496 TT_OS2* os2_table;
497 if ((pclt_info = (TT_PCLT*)FT_Get_Sfnt_Table(face, ft_sfnt_pclt)) != NULL) {
498 info->fCapHeight = pclt_info->CapHeight;
499 uint8_t serif_style = pclt_info->SerifStyle & 0x3F;
500 if (serif_style >= 2 && serif_style <= 6)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000501 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000502 else if (serif_style >= 9 && serif_style <= 12)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000503 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000504 } else if ((os2_table =
505 (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
506 info->fCapHeight = os2_table->sCapHeight;
507 } else {
508 // Figure out a good guess for CapHeight: average the height of M and X.
509 FT_BBox m_bbox, x_bbox;
510 bool got_m, got_x;
511 got_m = GetLetterCBox(face, 'M', &m_bbox);
512 got_x = GetLetterCBox(face, 'X', &x_bbox);
513 if (got_m && got_x) {
514 info->fCapHeight = (m_bbox.yMax - m_bbox.yMin + x_bbox.yMax -
515 x_bbox.yMin) / 2;
516 } else if (got_m && !got_x) {
517 info->fCapHeight = m_bbox.yMax - m_bbox.yMin;
518 } else if (!got_m && got_x) {
519 info->fCapHeight = x_bbox.yMax - x_bbox.yMin;
520 }
521 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000522
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000523 info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax,
524 face->bbox.xMax, face->bbox.yMin);
525
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000526 if (!canEmbed(face) || !FT_IS_SCALABLE(face) ||
527 info->fType == SkAdvancedTypefaceMetrics::kOther_Font) {
528 perGlyphInfo = SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo;
529 }
530
531 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000532 if (FT_IS_FIXED_WIDTH(face)) {
533 appendRange(&info->fGlyphWidths, 0);
534 int16_t advance = face->max_advance_width;
535 info->fGlyphWidths->fAdvance.append(1, &advance);
536 finishRange(info->fGlyphWidths.get(), 0,
537 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000538 } else if (!cid) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000539 appendRange(&info->fGlyphWidths, 0);
540 // So as to not blow out the stack, get advances in batches.
541 for (int gID = 0; gID < face->num_glyphs; gID += 128) {
542 FT_Fixed advances[128];
543 int advanceCount = 128;
544 if (gID + advanceCount > face->num_glyphs)
545 advanceCount = face->num_glyphs - gID + 1;
546 getAdvances(face, gID, advanceCount, FT_LOAD_NO_SCALE,
547 advances);
548 for (int i = 0; i < advanceCount; i++) {
549 int16_t advance = advances[gID + i];
550 info->fGlyphWidths->fAdvance.append(1, &advance);
551 }
552 }
553 finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1,
554 SkAdvancedTypefaceMetrics::WidthRange::kRange);
555 } else {
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000556 info->fGlyphWidths.reset(
557 getAdvanceData(face, face->num_glyphs, &getWidthAdvance));
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000558 }
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000559 }
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000560
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000561 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kVAdvance_PerGlyphInfo &&
562 FT_HAS_VERTICAL(face)) {
563 SkASSERT(false); // Not implemented yet.
564 }
565
566 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo &&
567 info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
568 // Postscript fonts may contain more than 255 glyphs, so we end up
569 // using multiple font descriptions with a glyph ordering. Record
570 // the name of each glyph.
571 info->fGlyphNames.reset(
572 new SkAutoTArray<SkString>(face->num_glyphs));
573 for (int gID = 0; gID < face->num_glyphs; gID++) {
574 char glyphName[128]; // PS limit for names is 127 bytes.
575 FT_Get_Glyph_Name(face, gID, glyphName, 128);
576 info->fGlyphNames->get()[gID].set(glyphName);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000577 }
578 }
579
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000580 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo &&
581 info->fType != SkAdvancedTypefaceMetrics::kType1_Font &&
582 face->num_charmaps) {
583 populate_glyph_to_unicode(face, &(info->fGlyphToUnicode));
584 }
585
vandebo@chromium.orgc3a2ae52011-02-03 21:48:23 +0000586 if (!canEmbed(face))
587 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
588
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000589 unref_ft_face(face);
590 return info;
reed@google.com8a5d6922011-03-14 15:08:03 +0000591#endif
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000592}
reed@google.com618ef5e2011-01-26 22:10:41 +0000593///////////////////////////////////////////////////////////////////////////
594
595void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
596 if (!gLCDSupportValid) {
597 InitFreetype();
598 FT_Done_FreeType(gFTLibrary);
599 }
reed@google.com5b31b0f2011-02-23 14:41:42 +0000600
reed@google.comeffc5012011-06-27 16:44:46 +0000601 if (!gLCDSupport && isLCD(*rec)) {
reed@google.com618ef5e2011-01-26 22:10:41 +0000602 // If the runtime Freetype library doesn't support LCD mode, we disable
603 // it here.
604 rec->fMaskFormat = SkMask::kA8_Format;
605 }
reed@google.com5b31b0f2011-02-23 14:41:42 +0000606
reed@google.com618ef5e2011-01-26 22:10:41 +0000607 SkPaint::Hinting h = rec->getHinting();
reed@google.comeffc5012011-06-27 16:44:46 +0000608 if (SkPaint::kFull_Hinting == h && !isLCD(*rec)) {
reed@google.com618ef5e2011-01-26 22:10:41 +0000609 // collapse full->normal hinting if we're not doing LCD
610 h = SkPaint::kNormal_Hinting;
611 } else if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag) &&
612 SkPaint::kNo_Hinting != h) {
613 // to do subpixel, we must have at most slight hinting
614 h = SkPaint::kSlight_Hinting;
615 }
616 rec->setHinting(h);
617}
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000618
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000619#ifdef ANDROID
620uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) {
621 SkAutoMutexAcquire ac(gFTMutex);
622 SkFaceRec *rec = ref_ft_face(fontID);
623 uint16_t unitsPerEm = 0;
624
625 if (rec != NULL && rec->fFace != NULL) {
626 unitsPerEm = rec->fFace->units_per_EM;
627 unref_ft_face(rec->fFace);
628 }
629
630 return (uint32_t)unitsPerEm;
631}
632#endif
633
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
reed@android.com62900b42009-02-11 15:07:19 +0000635 : SkScalerContext(desc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 SkAutoMutexAcquire ac(gFTMutex);
637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 if (gFTCount == 0) {
reed@android.com659aaf92009-07-23 15:20:21 +0000639 if (!InitFreetype()) {
640 sk_throw();
641 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 }
643 ++gFTCount;
644
645 // load the font file
reed@android.com62900b42009-02-11 15:07:19 +0000646 fFTSize = NULL;
647 fFace = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 fFaceRec = ref_ft_face(fRec.fFontID);
reed@android.com62900b42009-02-11 15:07:19 +0000649 if (NULL == fFaceRec) {
650 return;
651 }
652 fFace = fFaceRec->fFace;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653
654 // compute our factors from the record
655
656 SkMatrix m;
657
658 fRec.getSingleMatrix(&m);
659
660#ifdef DUMP_STRIKE_CREATION
661 SkString keyString;
662 SkFontHost::GetDescriptorKeyString(desc, &keyString);
663 printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
664 SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
665 SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
666 SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
agl@chromium.org309485b2009-07-21 17:41:32 +0000667 fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668#endif
669
670 // now compute our scale factors
671 SkScalar sx = m.getScaleX();
672 SkScalar sy = m.getScaleY();
673
674 if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
675 // sort of give up on hinting
676 sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
677 sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
678 sx = sy = SkScalarAve(sx, sy);
679
680 SkScalar inv = SkScalarInvert(sx);
681
682 // flip the skew elements to go from our Y-down system to FreeType's
683 fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
684 fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
685 fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
686 fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
687 } else {
688 fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
689 fMatrix22.xy = fMatrix22.yx = 0;
690 }
691
692 fScaleX = SkScalarToFixed(sx);
693 fScaleY = SkScalarToFixed(sy);
694
695 // compute the flags we send to Load_Glyph
696 {
reed@android.come4d0bc02009-07-24 19:53:20 +0000697 FT_Int32 loadFlags = FT_LOAD_DEFAULT;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698
agl@chromium.org70a303f2010-05-10 14:15:50 +0000699 if (SkMask::kBW_Format == fRec.fMaskFormat) {
700 // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
701 loadFlags = FT_LOAD_TARGET_MONO;
reed@google.comeffc5012011-06-27 16:44:46 +0000702 if (fRec.getHinting() == SkPaint::kNo_Hinting) {
agl@chromium.org70a303f2010-05-10 14:15:50 +0000703 loadFlags = FT_LOAD_NO_HINTING;
reed@google.comeffc5012011-06-27 16:44:46 +0000704 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000705 } else {
706 switch (fRec.getHinting()) {
707 case SkPaint::kNo_Hinting:
708 loadFlags = FT_LOAD_NO_HINTING;
709 break;
710 case SkPaint::kSlight_Hinting:
711 loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT
712 break;
713 case SkPaint::kNormal_Hinting:
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000714 if (fRec.fFlags & SkScalerContext::kAutohinting_Flag)
715 loadFlags = FT_LOAD_FORCE_AUTOHINT;
716 else
717 loadFlags = FT_LOAD_NO_AUTOHINT;
agl@chromium.org70a303f2010-05-10 14:15:50 +0000718 break;
719 case SkPaint::kFull_Hinting:
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000720 if (fRec.fFlags & SkScalerContext::kAutohinting_Flag) {
721 loadFlags = FT_LOAD_FORCE_AUTOHINT;
722 break;
723 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000724 loadFlags = FT_LOAD_TARGET_NORMAL;
reed@google.comeffc5012011-06-27 16:44:46 +0000725 if (isLCD(fRec)) {
726 if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) {
727 loadFlags = FT_LOAD_TARGET_LCD_V;
728 } else {
729 loadFlags = FT_LOAD_TARGET_LCD;
730 }
reed@google.comea2333d2011-03-14 16:44:56 +0000731 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000732 break;
733 default:
734 SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting());
735 break;
736 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 }
738
reed@google.comeffc5012011-06-27 16:44:46 +0000739 if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) {
agl@chromium.orge0d08992009-08-07 19:19:23 +0000740 loadFlags |= FT_LOAD_NO_BITMAP;
reed@google.comeffc5012011-06-27 16:44:46 +0000741 }
agl@chromium.orge0d08992009-08-07 19:19:23 +0000742
reed@google.com96a9f7912011-05-06 11:49:30 +0000743 // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct
744 // advances, as fontconfig and cairo do.
745 // See http://code.google.com/p/skia/issues/detail?id=222.
746 loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
747
reed@android.come4d0bc02009-07-24 19:53:20 +0000748 fLoadGlyphFlags = loadFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 }
750
751 // now create the FT_Size
752
753 {
754 FT_Error err;
755
756 err = FT_New_Size(fFace, &fFTSize);
757 if (err != 0) {
758 SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
759 fFaceRec->fFontID, fScaleX, fScaleY, err));
760 fFace = NULL;
761 return;
762 }
763
764 err = FT_Activate_Size(fFTSize);
765 if (err != 0) {
766 SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
767 fFaceRec->fFontID, fScaleX, fScaleY, err));
768 fFTSize = NULL;
769 }
770
771 err = FT_Set_Char_Size( fFace,
772 SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
773 72, 72);
774 if (err != 0) {
775 SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
776 fFaceRec->fFontID, fScaleX, fScaleY, err));
777 fFace = NULL;
778 return;
779 }
780
781 FT_Set_Transform( fFace, &fMatrix22, NULL);
782 }
783}
784
785SkScalerContext_FreeType::~SkScalerContext_FreeType() {
786 if (fFTSize != NULL) {
787 FT_Done_Size(fFTSize);
788 }
789
790 SkAutoMutexAcquire ac(gFTMutex);
791
792 if (fFace != NULL) {
793 unref_ft_face(fFace);
794 }
795 if (--gFTCount == 0) {
796// SkDEBUGF(("FT_Done_FreeType\n"));
797 FT_Done_FreeType(gFTLibrary);
798 SkDEBUGCODE(gFTLibrary = NULL;)
799 }
800}
801
802/* We call this before each use of the fFace, since we may be sharing
803 this face with other context (at different sizes).
804*/
805FT_Error SkScalerContext_FreeType::setupSize() {
806 /* In the off-chance that a font has been removed, we want to error out
807 right away, so call resolve just to be sure.
808
809 TODO: perhaps we can skip this, by walking the global font cache and
810 killing all of the contexts when we know that a given fontID is going
811 away...
812 */
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000813 if (!SkFontHost::ValidFontID(fRec.fFontID)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 return (FT_Error)-1;
815 }
816
817 FT_Error err = FT_Activate_Size(fFTSize);
818
819 if (err != 0) {
820 SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
821 fFaceRec->fFontID, fScaleX, fScaleY, err));
822 fFTSize = NULL;
823 } else {
824 // seems we need to reset this every time (not sure why, but without it
825 // I get random italics from some other fFTSize)
826 FT_Set_Transform( fFace, &fMatrix22, NULL);
827 }
828 return err;
829}
830
senorblanco@chromium.org4526a842010-02-05 23:08:20 +0000831void SkScalerContext_FreeType::emboldenOutline(FT_Outline* outline) {
832 FT_Pos strength;
833 strength = FT_MulFix(fFace->units_per_EM, fFace->size->metrics.y_scale)
834 / 24;
835 FT_Outline_Embolden(outline, strength);
836}
837
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000838unsigned SkScalerContext_FreeType::generateGlyphCount() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 return fFace->num_glyphs;
840}
841
842uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
843 return SkToU16(FT_Get_Char_Index( fFace, uni ));
844}
845
reed@android.com9d3a9852010-01-08 14:07:42 +0000846SkUnichar SkScalerContext_FreeType::generateGlyphToChar(uint16_t glyph) {
847 // iterate through each cmap entry, looking for matching glyph indices
848 FT_UInt glyphIndex;
849 SkUnichar charCode = FT_Get_First_Char( fFace, &glyphIndex );
850
851 while (glyphIndex != 0) {
852 if (glyphIndex == glyph) {
853 return charCode;
854 }
855 charCode = FT_Get_Next_Char( fFace, charCode, &glyphIndex );
856 }
857
858 return 0;
859}
860
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
862 switch (format) {
863 case SkMask::kBW_Format:
864 return FT_PIXEL_MODE_MONO;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 case SkMask::kA8_Format:
866 default:
867 return FT_PIXEL_MODE_GRAY;
868 }
869}
870
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
872#ifdef FT_ADVANCES_H
873 /* unhinted and light hinted text have linearly scaled advances
874 * which are very cheap to compute with some font formats...
875 */
876 {
877 SkAutoMutexAcquire ac(gFTMutex);
878
879 if (this->setupSize()) {
reed@android.com62900b42009-02-11 15:07:19 +0000880 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 return;
882 }
883
884 FT_Error error;
885 FT_Fixed advance;
886
887 error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount),
888 fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
889 &advance );
890 if (0 == error) {
891 glyph->fRsbDelta = 0;
892 glyph->fLsbDelta = 0;
893 glyph->fAdvanceX = advance; // advance *2/3; //DEBUG
894 glyph->fAdvanceY = 0;
895 return;
896 }
897 }
898#endif /* FT_ADVANCES_H */
899 /* otherwise, we need to load/hint the glyph, which is slower */
900 this->generateMetrics(glyph);
901 return;
902}
903
904void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
905 SkAutoMutexAcquire ac(gFTMutex);
906
907 glyph->fRsbDelta = 0;
908 glyph->fLsbDelta = 0;
909
910 FT_Error err;
911
912 if (this->setupSize()) {
913 goto ERROR;
914 }
915
916 err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
917 if (err != 0) {
918 SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
919 fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
920 ERROR:
reed@android.com62900b42009-02-11 15:07:19 +0000921 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 return;
923 }
924
925 switch ( fFace->glyph->format ) {
thakis@chromium.org598b8592011-05-24 05:42:55 +0000926 case FT_GLYPH_FORMAT_OUTLINE: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 FT_BBox bbox;
928
senorblanco@chromium.org4526a842010-02-05 23:08:20 +0000929 if (fRec.fFlags & kEmbolden_Flag) {
930 emboldenOutline(&fFace->glyph->outline);
931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
933
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000934 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 int dx = glyph->getSubXFixed() >> 10;
936 int dy = glyph->getSubYFixed() >> 10;
937 // negate dy since freetype-y-goes-up and skia-y-goes-down
938 bbox.xMin += dx;
939 bbox.yMin -= dy;
940 bbox.xMax += dx;
941 bbox.yMax -= dy;
942 }
943
944 bbox.xMin &= ~63;
945 bbox.yMin &= ~63;
946 bbox.xMax = (bbox.xMax + 63) & ~63;
947 bbox.yMax = (bbox.yMax + 63) & ~63;
948
949 glyph->fWidth = SkToU16((bbox.xMax - bbox.xMin) >> 6);
950 glyph->fHeight = SkToU16((bbox.yMax - bbox.yMin) >> 6);
951 glyph->fTop = -SkToS16(bbox.yMax >> 6);
952 glyph->fLeft = SkToS16(bbox.xMin >> 6);
953 break;
thakis@chromium.org598b8592011-05-24 05:42:55 +0000954 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955
956 case FT_GLYPH_FORMAT_BITMAP:
agl@chromium.orge76073b2010-06-04 20:31:17 +0000957 if (fRec.fFlags & kEmbolden_Flag) {
958 FT_GlyphSlot_Own_Bitmap(fFace->glyph);
959 FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
960 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 glyph->fWidth = SkToU16(fFace->glyph->bitmap.width);
962 glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows);
963 glyph->fTop = -SkToS16(fFace->glyph->bitmap_top);
964 glyph->fLeft = SkToS16(fFace->glyph->bitmap_left);
965 break;
966
967 default:
968 SkASSERT(!"unknown glyph format");
969 goto ERROR;
970 }
971
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000972 if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
974 glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
975 if (fRec.fFlags & kDevKernText_Flag) {
976 glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
977 glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
978 }
979 } else {
980 glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
981 glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
982 }
983
984#ifdef ENABLE_GLYPH_SPEW
985 SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
986 SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth));
987#endif
988}
989
reed@google.combde3c8e2011-05-18 11:58:10 +0000990static int lerp(int start, int end) {
991 SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
992 return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
reed@google.comc5181342011-05-17 20:52:46 +0000993}
994
995static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
996 if (SK_FREETYPE_LCD_LERP) {
reed@google.combde3c8e2011-05-18 11:58:10 +0000997 // want (a+b+c)/3, but we approx to avoid the divide
998 unsigned ave = (5 * (r + g + b) + b) >> 4;
999 r = lerp(r, ave);
1000 g = lerp(g, ave);
1001 b = lerp(b, ave);
reed@google.comc5181342011-05-17 20:52:46 +00001002 }
1003 return SkPackRGB16(r >> 3, g >> 2, b >> 3);
1004}
1005
reed@google.com73824072011-06-23 13:17:30 +00001006static uint16_t grayToRGB16(U8CPU gray) {
1007 SkASSERT(gray <= 255);
1008 return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3);
1009}
1010
1011static int bittst(const uint8_t data[], int bitOffset) {
1012 SkASSERT(bitOffset >= 0);
reed@google.comc8e0f932011-06-23 19:39:49 +00001013 int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
reed@google.com73824072011-06-23 13:17:30 +00001014 return lowBit & 1;
1015}
1016
reed@google.comeffc5012011-06-27 16:44:46 +00001017static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
1018 int lcdIsBGR) {
reed@google.comea2333d2011-03-14 16:44:56 +00001019 SkASSERT(glyph.fHeight == bitmap.rows);
reed@google.comea2333d2011-03-14 16:44:56 +00001020 uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
reed@google.com73824072011-06-23 13:17:30 +00001021 const size_t dstRB = glyph.rowBytes();
1022 const int width = glyph.fWidth;
1023 const uint8_t* src = bitmap.buffer;
reed@google.comea2333d2011-03-14 16:44:56 +00001024
reed@google.com73824072011-06-23 13:17:30 +00001025 switch (bitmap.pixel_mode) {
1026 case FT_PIXEL_MODE_MONO: {
1027 for (int y = 0; y < glyph.fHeight; ++y) {
1028 for (int x = 0; x < width; ++x) {
1029 dst[x] = -bittst(src, x);
1030 }
1031 dst = (uint16_t*)((char*)dst + dstRB);
1032 src += bitmap.pitch;
1033 }
1034 } break;
1035 case FT_PIXEL_MODE_GRAY: {
1036 for (int y = 0; y < glyph.fHeight; ++y) {
1037 for (int x = 0; x < width; ++x) {
1038 dst[x] = grayToRGB16(src[x]);
1039 }
1040 dst = (uint16_t*)((char*)dst + dstRB);
1041 src += bitmap.pitch;
1042 }
1043 } break;
1044 default: {
1045 SkASSERT(glyph.fWidth * 3 == bitmap.width - 6);
1046 src += 3;
1047 for (int y = 0; y < glyph.fHeight; y++) {
1048 const uint8_t* triple = src;
reed@google.comeffc5012011-06-27 16:44:46 +00001049 if (lcdIsBGR) {
1050 for (int x = 0; x < width; x++) {
1051 dst[x] = packTriple(triple[2], triple[1], triple[0]);
1052 triple += 3;
1053 }
1054 } else {
1055 for (int x = 0; x < width; x++) {
1056 dst[x] = packTriple(triple[0], triple[1], triple[2]);
1057 triple += 3;
1058 }
reed@google.com73824072011-06-23 13:17:30 +00001059 }
1060 src += bitmap.pitch;
1061 dst = (uint16_t*)((char*)dst + dstRB);
1062 }
1063 } break;
reed@google.comea2333d2011-03-14 16:44:56 +00001064 }
1065}
1066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
1068 SkAutoMutexAcquire ac(gFTMutex);
1069
1070 FT_Error err;
1071
1072 if (this->setupSize()) {
1073 goto ERROR;
1074 }
1075
1076 err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);
1077 if (err != 0) {
1078 SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
1079 glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
1080 ERROR:
1081 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
1082 return;
1083 }
1084
1085 switch ( fFace->glyph->format ) {
1086 case FT_GLYPH_FORMAT_OUTLINE: {
1087 FT_Outline* outline = &fFace->glyph->outline;
1088 FT_BBox bbox;
1089 FT_Bitmap target;
1090
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001091 if (fRec.fFlags & kEmbolden_Flag) {
1092 emboldenOutline(outline);
1093 }
1094
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 int dx = 0, dy = 0;
agl@chromium.orga2c71cb2010-06-17 20:49:17 +00001096 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 dx = glyph.getSubXFixed() >> 10;
1098 dy = glyph.getSubYFixed() >> 10;
1099 // negate dy since freetype-y-goes-up and skia-y-goes-down
1100 dy = -dy;
1101 }
1102 FT_Outline_Get_CBox(outline, &bbox);
1103 /*
1104 what we really want to do for subpixel is
1105 offset(dx, dy)
1106 compute_bounds
1107 offset(bbox & !63)
1108 but that is two calls to offset, so we do the following, which
1109 achieves the same thing with only one offset call.
1110 */
1111 FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
1112 dy - ((bbox.yMin + dy) & ~63));
1113
reed@google.comea2333d2011-03-14 16:44:56 +00001114 if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
1115 FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
reed@google.comeffc5012011-06-27 16:44:46 +00001116 copyFT2LCD16(glyph, fFace->glyph->bitmap,
1117 fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
reed@google.comea2333d2011-03-14 16:44:56 +00001118 } else {
1119 target.width = glyph.fWidth;
1120 target.rows = glyph.fHeight;
1121 target.pitch = glyph.rowBytes();
1122 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
1123 target.pixel_mode = compute_pixel_mode(
1124 (SkMask::Format)fRec.fMaskFormat);
1125 target.num_grays = 256;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126
reed@google.comea2333d2011-03-14 16:44:56 +00001127 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
1128 FT_Outline_Get_Bitmap(gFTLibrary, outline, &target);
1129 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 } break;
1131
1132 case FT_GLYPH_FORMAT_BITMAP: {
agl@chromium.orge76073b2010-06-04 20:31:17 +00001133 if (fRec.fFlags & kEmbolden_Flag) {
1134 FT_GlyphSlot_Own_Bitmap(fFace->glyph);
1135 FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
1136 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width);
1138 SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows);
1139 SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top);
1140 SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left);
1141
1142 const uint8_t* src = (const uint8_t*)fFace->glyph->bitmap.buffer;
1143 uint8_t* dst = (uint8_t*)glyph.fImage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144
agl@chromium.org558434a2009-08-11 17:22:38 +00001145 if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY ||
1146 (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
1147 glyph.fMaskFormat == SkMask::kBW_Format)) {
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001148 unsigned srcRowBytes = fFace->glyph->bitmap.pitch;
1149 unsigned dstRowBytes = glyph.rowBytes();
1150 unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
1151 unsigned extraRowBytes = dstRowBytes - minRowBytes;
1152
1153 for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) {
1154 memcpy(dst, src, minRowBytes);
1155 memset(dst + minRowBytes, 0, extraRowBytes);
1156 src += srcRowBytes;
1157 dst += dstRowBytes;
1158 }
agl@chromium.org558434a2009-08-11 17:22:38 +00001159 } else if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
reed@google.comeffc5012011-06-27 16:44:46 +00001160 glyph.fMaskFormat == SkMask::kA8_Format) {
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001161 for (int y = 0; y < fFace->glyph->bitmap.rows; ++y) {
1162 uint8_t byte = 0;
1163 int bits = 0;
1164 const uint8_t* src_row = src;
1165 uint8_t* dst_row = dst;
1166
1167 for (int x = 0; x < fFace->glyph->bitmap.width; ++x) {
1168 if (!bits) {
1169 byte = *src_row++;
1170 bits = 8;
1171 }
1172
1173 *dst_row++ = byte & 0x80 ? 0xff : 0;
1174 bits--;
1175 byte <<= 1;
1176 }
1177
1178 src += fFace->glyph->bitmap.pitch;
1179 dst += glyph.rowBytes();
1180 }
reed@google.com73824072011-06-23 13:17:30 +00001181 } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
reed@google.comeffc5012011-06-27 16:44:46 +00001182 copyFT2LCD16(glyph, fFace->glyph->bitmap,
1183 fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
agl@chromium.org558434a2009-08-11 17:22:38 +00001184 } else {
reed@google.com73824072011-06-23 13:17:30 +00001185 SkASSERT(!"unknown glyph bitmap transform needed");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 }
1187 } break;
1188
1189 default:
1190 SkASSERT(!"unknown glyph format");
1191 goto ERROR;
1192 }
1193}
1194
1195///////////////////////////////////////////////////////////////////////////////
1196
1197#define ft2sk(x) SkFixedToScalar((x) << 10)
1198
reed@android.com6f252972009-01-14 16:46:16 +00001199#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 #define CONST_PARAM const
1201#else // older freetype doesn't use const here
1202 #define CONST_PARAM
1203#endif
1204
1205static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
1206 SkPath* path = (SkPath*)ctx;
1207 path->close(); // to close the previous contour (if any)
1208 path->moveTo(ft2sk(pt->x), -ft2sk(pt->y));
1209 return 0;
1210}
1211
1212static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
1213 SkPath* path = (SkPath*)ctx;
1214 path->lineTo(ft2sk(pt->x), -ft2sk(pt->y));
1215 return 0;
1216}
1217
1218static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
1219 void* ctx) {
1220 SkPath* path = (SkPath*)ctx;
1221 path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y));
1222 return 0;
1223}
1224
1225static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
1226 CONST_PARAM FT_Vector* pt2, void* ctx) {
1227 SkPath* path = (SkPath*)ctx;
1228 path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x),
1229 -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y));
1230 return 0;
1231}
1232
1233void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
1234 SkPath* path) {
1235 SkAutoMutexAcquire ac(gFTMutex);
1236
1237 SkASSERT(&glyph && path);
1238
1239 if (this->setupSize()) {
1240 path->reset();
1241 return;
1242 }
1243
1244 uint32_t flags = fLoadGlyphFlags;
1245 flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
1246 flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
1247
1248 FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);
1249
1250 if (err != 0) {
1251 SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
1252 glyph.getGlyphID(fBaseGlyphCount), flags, err));
1253 path->reset();
1254 return;
1255 }
1256
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001257 if (fRec.fFlags & kEmbolden_Flag) {
1258 emboldenOutline(&fFace->glyph->outline);
1259 }
1260
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 FT_Outline_Funcs funcs;
1262
1263 funcs.move_to = move_proc;
1264 funcs.line_to = line_proc;
1265 funcs.conic_to = quad_proc;
1266 funcs.cubic_to = cubic_proc;
1267 funcs.shift = 0;
1268 funcs.delta = 0;
1269
1270 err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path);
1271
1272 if (err != 0) {
1273 SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
1274 glyph.getGlyphID(fBaseGlyphCount), flags, err));
1275 path->reset();
1276 return;
1277 }
1278
1279 path->close();
1280}
1281
1282void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
1283 SkPaint::FontMetrics* my) {
1284 if (NULL == mx && NULL == my) {
1285 return;
1286 }
1287
1288 SkAutoMutexAcquire ac(gFTMutex);
1289
1290 if (this->setupSize()) {
reed@android.coma8a8b8b2009-05-04 15:00:11 +00001291 ERROR:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 if (mx) {
reed@android.com4516f472009-06-29 16:25:36 +00001293 sk_bzero(mx, sizeof(SkPaint::FontMetrics));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 }
1295 if (my) {
reed@android.com4516f472009-06-29 16:25:36 +00001296 sk_bzero(my, sizeof(SkPaint::FontMetrics));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 }
1298 return;
1299 }
1300
reed@android.coma8a8b8b2009-05-04 15:00:11 +00001301 FT_Face face = fFace;
1302 int upem = face->units_per_EM;
1303 if (upem <= 0) {
1304 goto ERROR;
1305 }
1306
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001307 SkPoint pts[6];
1308 SkFixed ys[6];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 SkFixed scaleY = fScaleY;
1310 SkFixed mxy = fMatrix22.xy;
1311 SkFixed myy = fMatrix22.yy;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001312 SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem;
1313 SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001315 int leading = face->height - (face->ascender + -face->descender);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 if (leading < 0) {
1317 leading = 0;
1318 }
1319
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001320 // Try to get the OS/2 table from the font. This contains the specific
1321 // average font width metrics which Windows uses.
1322 TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
1323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 ys[0] = -face->bbox.yMax;
1325 ys[1] = -face->ascender;
1326 ys[2] = -face->descender;
1327 ys[3] = -face->bbox.yMin;
1328 ys[4] = leading;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001329 ys[5] = os2 ? os2->xAvgCharWidth : 0;
1330
1331 SkScalar x_height;
1332 if (os2 && os2->sxHeight) {
1333 x_height = SkFixedToScalar(SkMulDiv(fScaleX, os2->sxHeight, upem));
1334 } else {
1335 const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
1336 if (x_glyph) {
1337 FT_BBox bbox;
1338 FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001339 if (fRec.fFlags & kEmbolden_Flag) {
1340 emboldenOutline(&fFace->glyph->outline);
1341 }
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001342 FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
1343 x_height = SkIntToScalar(bbox.yMax) / 64;
1344 } else {
1345 x_height = 0;
1346 }
1347 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348
1349 // convert upem-y values into scalar points
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001350 for (int i = 0; i < 6; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 SkFixed y = SkMulDiv(scaleY, ys[i], upem);
1352 SkFixed x = SkFixedMul(mxy, y);
1353 y = SkFixedMul(myy, y);
1354 pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y));
1355 }
1356
1357 if (mx) {
1358 mx->fTop = pts[0].fX;
1359 mx->fAscent = pts[1].fX;
1360 mx->fDescent = pts[2].fX;
1361 mx->fBottom = pts[3].fX;
1362 mx->fLeading = pts[4].fX;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001363 mx->fAvgCharWidth = pts[5].fX;
1364 mx->fXMin = xmin;
1365 mx->fXMax = xmax;
1366 mx->fXHeight = x_height;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 }
1368 if (my) {
1369 my->fTop = pts[0].fY;
1370 my->fAscent = pts[1].fY;
1371 my->fDescent = pts[2].fY;
1372 my->fBottom = pts[3].fY;
1373 my->fLeading = pts[4].fY;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001374 my->fAvgCharWidth = pts[5].fY;
1375 my->fXMin = xmin;
1376 my->fXMax = xmax;
1377 my->fXHeight = x_height;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 }
1379}
1380
1381////////////////////////////////////////////////////////////////////////
1382////////////////////////////////////////////////////////////////////////
1383
1384SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
reed@android.com62900b42009-02-11 15:07:19 +00001385 SkScalerContext_FreeType* c = SkNEW_ARGS(SkScalerContext_FreeType, (desc));
1386 if (!c->success()) {
1387 SkDELETE(c);
1388 c = NULL;
1389 }
1390 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391}
1392
1393///////////////////////////////////////////////////////////////////////////////
1394
1395/* Export this so that other parts of our FonttHost port can make use of our
1396 ability to extract the name+style from a stream, using FreeType's api.
1397*/
reed@google.com5b31b0f2011-02-23 14:41:42 +00001398SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
1399 bool* isFixedWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 FT_Library library;
reed@android.combfbd4ff2009-07-23 17:44:41 +00001401 if (FT_Init_FreeType(&library)) {
djsollen@google.com7b34ea62011-02-24 16:28:51 +00001402 name->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 return SkTypeface::kNormal;
1404 }
1405
1406 FT_Open_Args args;
1407 memset(&args, 0, sizeof(args));
1408
1409 const void* memoryBase = stream->getMemoryBase();
1410 FT_StreamRec streamRec;
1411
1412 if (NULL != memoryBase) {
1413 args.flags = FT_OPEN_MEMORY;
1414 args.memory_base = (const FT_Byte*)memoryBase;
1415 args.memory_size = stream->getLength();
1416 } else {
1417 memset(&streamRec, 0, sizeof(streamRec));
1418 streamRec.size = stream->read(NULL, 0);
1419 streamRec.descriptor.pointer = stream;
1420 streamRec.read = sk_stream_read;
1421 streamRec.close = sk_stream_close;
1422
1423 args.flags = FT_OPEN_STREAM;
1424 args.stream = &streamRec;
1425 }
1426
1427 FT_Face face;
1428 if (FT_Open_Face(library, &args, 0, &face)) {
1429 FT_Done_FreeType(library);
djsollen@google.com7b34ea62011-02-24 16:28:51 +00001430 name->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 return SkTypeface::kNormal;
1432 }
1433
1434 name->set(face->family_name);
1435 int style = SkTypeface::kNormal;
1436
1437 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
1438 style |= SkTypeface::kBold;
1439 }
1440 if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
1441 style |= SkTypeface::kItalic;
1442 }
reed@google.com5b31b0f2011-02-23 14:41:42 +00001443 if (isFixedWidth) {
1444 *isFixedWidth = FT_IS_FIXED_WIDTH(face);
1445 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446
1447 FT_Done_Face(face);
1448 FT_Done_FreeType(library);
1449 return (SkTypeface::Style)style;
1450}