blob: 98a44543766648277b996203179c103502265558 [file] [log] [blame]
bungeman@google.comfe755b42014-01-28 20:33:09 +00001
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +00009#include "SkAdvancedTypefaceMetrics.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmap.h"
11#include "SkCanvas.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000012#include "SkColorPriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDescriptor.h"
14#include "SkFDot6.h"
bungeman@google.com3aacb412012-03-13 14:55:12 +000015#include "SkFloatingPoint.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkFontHost.h"
george@mozilla.comc59b5da2012-08-23 00:39:08 +000017#include "SkFontHost_FreeType_common.h"
bungeman@google.combbe50132012-07-24 20:33:21 +000018#include "SkGlyph.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkMask.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000020#include "SkMaskGamma.h"
bungeman@google.comd3fbd342014-04-15 15:52:07 +000021#include "SkMatrix22.h"
bungeman@google.coma9802692013-08-07 02:45:25 +000022#include "SkOTUtils.h"
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +000023#include "SkOnce.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000024#include "SkScalerContext.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkStream.h"
26#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkTemplates.h"
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000028#include "SkThread.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
bungeman@google.comfd668cf2012-08-24 17:46:11 +000030#if defined(SK_CAN_USE_DLOPEN)
31#include <dlfcn.h>
32#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000033#include <ft2build.h>
34#include FT_FREETYPE_H
35#include FT_OUTLINE_H
36#include FT_SIZES_H
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000037#include FT_TRUETYPE_TABLES_H
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000038#include FT_TYPE1_TABLES_H
agl@chromium.orge76073b2010-06-04 20:31:17 +000039#include FT_BITMAP_H
agl@chromium.org36bb6972010-06-04 20:57:16 +000040// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
41#include FT_SYNTHESIS_H
vandebo@chromium.org2a22e102011-01-25 21:01:34 +000042#include FT_XFREE86_H
epoger@google.com5070d792011-06-29 20:43:14 +000043#ifdef FT_LCD_FILTER_H
agl@chromium.org309485b2009-07-21 17:41:32 +000044#include FT_LCD_FILTER_H
epoger@google.com5070d792011-06-29 20:43:14 +000045#endif
agl@chromium.org309485b2009-07-21 17:41:32 +000046
bungeman@google.comd3fbd342014-04-15 15:52:07 +000047// Defined in FreeType 2.3.8 and later.
48// This is a silly build time check, we would need a runtime check if we really cared.
reed@android.com8a1c16f2008-12-17 15:59:43 +000049#ifdef FT_ADVANCES_H
50#include FT_ADVANCES_H
51#endif
52
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000053#if 0
54// Also include the files by name for build tools which require this.
55#include <freetype/freetype.h>
56#include <freetype/ftoutln.h>
57#include <freetype/ftsizes.h>
58#include <freetype/tttables.h>
59#include <freetype/ftadvanc.h>
agl@chromium.org309485b2009-07-21 17:41:32 +000060#include <freetype/ftlcdfil.h>
agl@chromium.orge76073b2010-06-04 20:31:17 +000061#include <freetype/ftbitmap.h>
agl@chromium.org36bb6972010-06-04 20:57:16 +000062#include <freetype/ftsynth.h>
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000063#endif
64
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +000065// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
66// were introduced in FreeType 2.5.0.
67// The following may be removed once FreeType 2.5.0 is required to build.
68#ifndef FT_LOAD_COLOR
69# define FT_LOAD_COLOR ( 1L << 20 )
70# define FT_PIXEL_MODE_BGRA 7
71#endif
72
73// FT_HAS_COLOR and the corresponding FT_FACE_FLAG_COLOR
74// were introduced in FreeType 2.5.1
75// The following may be removed once FreeType 2.5.1 is required to build.
76#ifndef FT_HAS_COLOR
77# define FT_HAS_COLOR(face) false
78#endif
79
reed@android.com8a1c16f2008-12-17 15:59:43 +000080//#define ENABLE_GLYPH_SPEW // for tracing calls
81//#define DUMP_STRIKE_CREATION
82
reed@google.com1ac83502012-02-28 17:06:02 +000083//#define SK_GAMMA_APPLY_TO_A8
reed@google.com1ac83502012-02-28 17:06:02 +000084
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +000085using namespace skia_advanced_typeface_metrics_utils;
86
reed@google.comeffc5012011-06-27 16:44:46 +000087static bool isLCD(const SkScalerContext::Rec& rec) {
88 switch (rec.fMaskFormat) {
89 case SkMask::kLCD16_Format:
90 case SkMask::kLCD32_Format:
91 return true;
92 default:
93 return false;
94 }
95}
96
reed@android.com8a1c16f2008-12-17 15:59:43 +000097//////////////////////////////////////////////////////////////////////////
98
99struct SkFaceRec;
100
digit@google.com1771cbf2012-01-26 21:26:40 +0000101SK_DECLARE_STATIC_MUTEX(gFTMutex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102static int gFTCount;
103static FT_Library gFTLibrary;
104static SkFaceRec* gFaceRecHead;
agl@chromium.orgf18d8762009-07-28 18:38:08 +0000105static bool gLCDSupportValid; // true iff |gLCDSupport| has been set.
106static bool gLCDSupport; // true iff LCD is supported by the runtime.
reed@google.coma1c32562012-03-01 19:38:23 +0000107static int gLCDExtra; // number of extra pixels for filtering.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
bungeman@google.com62566f32012-10-13 03:21:53 +0000109/////////////////////////////////////////////////////////////////////////
110
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000111// FT_Library_SetLcdFilterWeights was introduced in FreeType 2.4.0.
112// The following platforms provide FreeType of at least 2.4.0.
113// Ubuntu >= 11.04 (previous deprecated April 2013)
114// Debian >= 6.0 (good)
115// OpenSuse >= 11.4 (previous deprecated January 2012 / Nov 2013 for Evergreen 11.2)
116// Fedora >= 14 (good)
117// Android >= Gingerbread (good)
118typedef FT_Error (*FT_Library_SetLcdFilterWeightsProc)(FT_Library, unsigned char*);
119
scroggo@google.com94bc60f2012-10-04 20:45:06 +0000120// Caller must lock gFTMutex before calling this function.
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000121static bool InitFreetype() {
agl@chromium.org309485b2009-07-21 17:41:32 +0000122 FT_Error err = FT_Init_FreeType(&gFTLibrary);
reed@google.comea2333d2011-03-14 16:44:56 +0000123 if (err) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000124 return false;
reed@google.comea2333d2011-03-14 16:44:56 +0000125 }
agl@chromium.org309485b2009-07-21 17:41:32 +0000126
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000127 // Setup LCD filtering. This reduces color fringes for LCD smoothed glyphs.
epoger@google.comb371ed12011-06-29 21:20:52 +0000128#ifdef FT_LCD_FILTER_H
bungeman@google.com62566f32012-10-13 03:21:53 +0000129 // Use default { 0x10, 0x40, 0x70, 0x40, 0x10 }, as it adds up to 0x110, simulating ink spread.
130 // SetLcdFilter must be called before SetLcdFilterWeights.
131 err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000132 if (0 == err) {
133 gLCDSupport = true;
134 gLCDExtra = 2; //Using a filter adds one full pixel to each side.
135
bungeman@google.com62566f32012-10-13 03:21:53 +0000136#ifdef SK_FONTHOST_FREETYPE_USE_NORMAL_LCD_FILTER
137 // This also adds to 0x110 simulating ink spread, but provides better results than default.
138 static unsigned char gGaussianLikeHeavyWeights[] = { 0x1A, 0x43, 0x56, 0x43, 0x1A, };
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000139
140#if defined(SK_FONTHOST_FREETYPE_RUNTIME_VERSION) && \
141 SK_FONTHOST_FREETYPE_RUNTIME_VERSION > 0x020400
bungeman@google.com62566f32012-10-13 03:21:53 +0000142 err = FT_Library_SetLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000143#elif defined(SK_CAN_USE_DLOPEN) && SK_CAN_USE_DLOPEN == 1
144 //The FreeType library is already loaded, so symbols are available in process.
145 void* self = dlopen(NULL, RTLD_LAZY);
bsalomon49f085d2014-09-05 13:34:00 -0700146 if (self) {
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000147 FT_Library_SetLcdFilterWeightsProc setLcdFilterWeights;
148 //The following cast is non-standard, but safe for POSIX.
149 *reinterpret_cast<void**>(&setLcdFilterWeights) = dlsym(self, "FT_Library_SetLcdFilterWeights");
150 dlclose(self);
151
bsalomon49f085d2014-09-05 13:34:00 -0700152 if (setLcdFilterWeights) {
bungeman@google.com62566f32012-10-13 03:21:53 +0000153 err = setLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
bungeman@google.comfd668cf2012-08-24 17:46:11 +0000154 }
155 }
156#endif
bungeman@google.com62566f32012-10-13 03:21:53 +0000157#endif
reed@google.coma1c32562012-03-01 19:38:23 +0000158 }
epoger@google.com5070d792011-06-29 20:43:14 +0000159#else
160 gLCDSupport = false;
161#endif
reed@android.com61608aa2009-07-31 14:52:54 +0000162 gLCDSupportValid = true;
agl@chromium.org309485b2009-07-21 17:41:32 +0000163
164 return true;
165}
166
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +0000167// Called while holding gFTMutex.
168static void determine_lcd_support(bool* lcdSupported) {
169 if (!gLCDSupportValid) {
170 // This will determine LCD support as a side effect.
171 InitFreetype();
172 FT_Done_FreeType(gFTLibrary);
173 }
174 SkASSERT(gLCDSupportValid);
175 *lcdSupported = gLCDSupport;
176}
177
reed@google.comfb2fdcc2012-10-17 15:49:36 +0000178// Lazy, once, wrapper to ask the FreeType Library if it can support LCD text
179static bool is_lcd_supported() {
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +0000180 static bool lcdSupported = false;
181 SkOnce(&gLCDSupportValid, &gFTMutex, determine_lcd_support, &lcdSupported);
182 return lcdSupported;
reed@google.comfb2fdcc2012-10-17 15:49:36 +0000183}
184
george@mozilla.comc59b5da2012-08-23 00:39:08 +0000185class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186public:
reed@google.com0da48612013-03-19 16:06:52 +0000187 SkScalerContext_FreeType(SkTypeface*, const SkDescriptor* desc);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 virtual ~SkScalerContext_FreeType();
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000189
reed@android.com62900b42009-02-11 15:07:19 +0000190 bool success() const {
reed@android.coma0f5d152009-06-22 17:38:10 +0000191 return fFaceRec != NULL &&
192 fFTSize != NULL &&
193 fFace != NULL;
reed@android.com62900b42009-02-11 15:07:19 +0000194 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
196protected:
bungeman@google.coma76de722012-10-26 19:35:54 +0000197 virtual unsigned generateGlyphCount() SK_OVERRIDE;
198 virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
199 virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
200 virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
201 virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
202 virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
bungeman41078062014-07-07 08:16:37 -0700203 virtual void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
bungeman@google.coma76de722012-10-26 19:35:54 +0000204 virtual SkUnichar generateGlyphToChar(uint16_t glyph) SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205
206private:
207 SkFaceRec* fFaceRec;
208 FT_Face fFace; // reference to shared face in gFaceRecHead
209 FT_Size fFTSize; // our own copy
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000210 FT_Int fStrikeIndex;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkFixed fScaleX, fScaleY;
212 FT_Matrix fMatrix22;
213 uint32_t fLoadGlyphFlags;
reed@google.combdc99882011-11-21 14:36:57 +0000214 bool fDoLinearMetrics;
reed@google.coma1bfa212012-03-08 21:57:12 +0000215 bool fLCDIsVert;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
reed@google.comf073b332013-05-06 12:21:16 +0000217 // Need scalar versions for generateFontMetrics
218 SkVector fScale;
219 SkMatrix fMatrix22Scalar;
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 FT_Error setupSize();
djsollen@google.comd8b599c2012-03-19 19:44:19 +0000222 void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
223 bool snapToPixelBoundary = false);
bungeman@google.comcbe1b542013-12-16 17:02:39 +0000224 bool getCBoxForLetter(char letter, FT_BBox* bbox);
scroggo@google.com94bc60f2012-10-04 20:45:06 +0000225 // Caller must lock gFTMutex before calling this function.
djsollen@google.comd8b599c2012-03-19 19:44:19 +0000226 void updateGlyphIfLCD(SkGlyph* glyph);
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +0000227 // Caller must lock gFTMutex before calling this function.
228 // update FreeType2 glyph slot with glyph emboldened
229 void emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230};
231
232///////////////////////////////////////////////////////////////////////////
233///////////////////////////////////////////////////////////////////////////
234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235struct SkFaceRec {
236 SkFaceRec* fNext;
237 FT_Face fFace;
238 FT_StreamRec fFTStream;
239 SkStream* fSkStream;
240 uint32_t fRefCnt;
241 uint32_t fFontID;
242
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000243 // assumes ownership of the stream, will call unref() when its done
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 SkFaceRec(SkStream* strm, uint32_t fontID);
245 ~SkFaceRec() {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000246 fSkStream->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 }
248};
249
250extern "C" {
251 static unsigned long sk_stream_read(FT_Stream stream,
252 unsigned long offset,
253 unsigned char* buffer,
254 unsigned long count ) {
255 SkStream* str = (SkStream*)stream->descriptor.pointer;
256
257 if (count) {
258 if (!str->rewind()) {
259 return 0;
260 } else {
261 unsigned long ret;
262 if (offset) {
263 ret = str->read(NULL, offset);
264 if (ret != offset) {
265 return 0;
266 }
267 }
268 ret = str->read(buffer, count);
269 if (ret != count) {
270 return 0;
271 }
272 count = ret;
273 }
274 }
275 return count;
276 }
277
sugoi@google.com66a58ac2013-03-05 20:40:52 +0000278 static void sk_stream_close(FT_Stream) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
281SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
vandebo@chromium.org9af25f32012-03-28 21:24:27 +0000282 : fNext(NULL), fSkStream(strm), fRefCnt(1), fFontID(fontID) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
284
reed@android.com4516f472009-06-29 16:25:36 +0000285 sk_bzero(&fFTStream, sizeof(fFTStream));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fFTStream.size = fSkStream->getLength();
287 fFTStream.descriptor.pointer = fSkStream;
288 fFTStream.read = sk_stream_read;
289 fFTStream.close = sk_stream_close;
290}
291
reed@android.com62900b42009-02-11 15:07:19 +0000292// Will return 0 on failure
scroggo@google.com94bc60f2012-10-04 20:45:06 +0000293// Caller must lock gFTMutex before calling this function.
reed@google.com2cdc6712013-03-21 18:22:00 +0000294static SkFaceRec* ref_ft_face(const SkTypeface* typeface) {
295 const SkFontID fontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 SkFaceRec* rec = gFaceRecHead;
297 while (rec) {
298 if (rec->fFontID == fontID) {
299 SkASSERT(rec->fFace);
300 rec->fRefCnt += 1;
301 return rec;
302 }
303 rec = rec->fNext;
304 }
305
reed@google.com2cdc6712013-03-21 18:22:00 +0000306 int face_index;
307 SkStream* strm = typeface->openStream(&face_index);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 if (NULL == strm) {
reed@google.com2cdc6712013-03-21 18:22:00 +0000309 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 }
311
312 // this passes ownership of strm to the rec
313 rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
314
315 FT_Open_Args args;
316 memset(&args, 0, sizeof(args));
317 const void* memoryBase = strm->getMemoryBase();
318
bsalomon49f085d2014-09-05 13:34:00 -0700319 if (memoryBase) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320//printf("mmap(%s)\n", keyString.c_str());
321 args.flags = FT_OPEN_MEMORY;
322 args.memory_base = (const FT_Byte*)memoryBase;
323 args.memory_size = strm->getLength();
324 } else {
325//printf("fopen(%s)\n", keyString.c_str());
326 args.flags = FT_OPEN_STREAM;
327 args.stream = &rec->fFTStream;
328 }
329
reed@google.com2cdc6712013-03-21 18:22:00 +0000330 FT_Error err = FT_Open_Face(gFTLibrary, &args, face_index, &rec->fFace);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 if (err) { // bad filename, try the default font
332 fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID);
333 SkDELETE(rec);
reed@google.com2cdc6712013-03-21 18:22:00 +0000334 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 } else {
336 SkASSERT(rec->fFace);
337 //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
338 rec->fNext = gFaceRecHead;
339 gFaceRecHead = rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 return rec;
341 }
342}
343
scroggo@google.com94bc60f2012-10-04 20:45:06 +0000344// Caller must lock gFTMutex before calling this function.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345static void unref_ft_face(FT_Face face) {
346 SkFaceRec* rec = gFaceRecHead;
347 SkFaceRec* prev = NULL;
348 while (rec) {
349 SkFaceRec* next = rec->fNext;
350 if (rec->fFace == face) {
351 if (--rec->fRefCnt == 0) {
352 if (prev) {
353 prev->fNext = next;
354 } else {
355 gFaceRecHead = next;
356 }
357 FT_Done_Face(face);
358 SkDELETE(rec);
359 }
360 return;
361 }
362 prev = rec;
363 rec = next;
364 }
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000365 SkDEBUGFAIL("shouldn't get here, face not in list");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366}
367
reed@google.comb4162b12013-07-02 16:32:29 +0000368class AutoFTAccess {
369public:
370 AutoFTAccess(const SkTypeface* tf) : fRec(NULL), fFace(NULL) {
371 gFTMutex.acquire();
372 if (1 == ++gFTCount) {
373 if (!InitFreetype()) {
374 sk_throw();
375 }
376 }
377 fRec = ref_ft_face(tf);
378 if (fRec) {
379 fFace = fRec->fFace;
380 }
381 }
382
383 ~AutoFTAccess() {
384 if (fFace) {
385 unref_ft_face(fFace);
386 }
387 if (0 == --gFTCount) {
388 FT_Done_FreeType(gFTLibrary);
389 }
390 gFTMutex.release();
391 }
392
393 SkFaceRec* rec() { return fRec; }
394 FT_Face face() { return fFace; }
395
396private:
397 SkFaceRec* fRec;
398 FT_Face fFace;
399};
400
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401///////////////////////////////////////////////////////////////////////////
402
vandebo@chromium.org16be6b82011-01-28 21:28:56 +0000403// Work around for old versions of freetype.
404static FT_Error getAdvances(FT_Face face, FT_UInt start, FT_UInt count,
405 FT_Int32 loadFlags, FT_Fixed* advances) {
406#ifdef FT_ADVANCES_H
407 return FT_Get_Advances(face, start, count, loadFlags, advances);
408#else
409 if (!face || start >= face->num_glyphs ||
410 start + count > face->num_glyphs || loadFlags != FT_LOAD_NO_SCALE) {
411 return 6; // "Invalid argument."
412 }
413 if (count == 0)
414 return 0;
415
416 for (int i = 0; i < count; i++) {
417 FT_Error err = FT_Load_Glyph(face, start + i, FT_LOAD_NO_SCALE);
418 if (err)
419 return err;
420 advances[i] = face->glyph->advance.x;
421 }
422
423 return 0;
424#endif
425}
426
427static bool canEmbed(FT_Face face) {
djsollen@google.comfa394d42012-01-09 20:40:25 +0000428#ifdef FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING
vandebo@chromium.org16be6b82011-01-28 21:28:56 +0000429 FT_UShort fsType = FT_Get_FSType_Flags(face);
430 return (fsType & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING |
431 FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0;
432#else
433 // No embedding is 0x2 and bitmap embedding only is 0x200.
434 TT_OS2* os2_table;
435 if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
436 return (os2_table->fsType & 0x202) == 0;
437 }
438 return false; // We tried, fail safe.
439#endif
440}
441
vandebo0f9bad02014-06-19 11:05:39 -0700442static bool canSubset(FT_Face face) {
443#ifdef FT_FSTYPE_NO_SUBSETTING
444 FT_UShort fsType = FT_Get_FSType_Flags(face);
445 return (fsType & FT_FSTYPE_NO_SUBSETTING) == 0;
446#else
447 // No subset is 0x100.
448 TT_OS2* os2_table;
449 if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
450 return (os2_table->fsType & 0x100) == 0;
451 }
452 return false; // We tried, fail safe.
453#endif
454}
455
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000456static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) {
457 const FT_UInt glyph_id = FT_Get_Char_Index(face, letter);
458 if (!glyph_id)
459 return false;
bungeman@google.comcbe1b542013-12-16 17:02:39 +0000460 if (FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE) != 0)
461 return false;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000462 FT_Outline_Get_CBox(&face->glyph->outline, bbox);
463 return true;
464}
465
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000466static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000467 FT_Fixed advance = 0;
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000468 if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) {
469 return false;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000470 }
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000471 SkASSERT(data);
472 *data = advance;
473 return true;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000474}
475
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000476static void populate_glyph_to_unicode(FT_Face& face,
477 SkTDArray<SkUnichar>* glyphToUnicode) {
478 // Check and see if we have Unicode cmaps.
479 for (int i = 0; i < face->num_charmaps; ++i) {
480 // CMaps known to support Unicode:
481 // Platform ID Encoding ID Name
482 // ----------- ----------- -----------------------------------
483 // 0 0,1 Apple Unicode
484 // 0 3 Apple Unicode 2.0 (preferred)
485 // 3 1 Microsoft Unicode UCS-2
486 // 3 10 Microsoft Unicode UCS-4 (preferred)
487 //
488 // See Apple TrueType Reference Manual
489 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html
490 // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html#ID
491 // Microsoft OpenType Specification
492 // http://www.microsoft.com/typography/otspec/cmap.htm
493
494 FT_UShort platformId = face->charmaps[i]->platform_id;
495 FT_UShort encodingId = face->charmaps[i]->encoding_id;
496
497 if (platformId != 0 && platformId != 3) {
498 continue;
499 }
500 if (platformId == 3 && encodingId != 1 && encodingId != 10) {
501 continue;
502 }
503 bool preferredMap = ((platformId == 3 && encodingId == 10) ||
504 (platformId == 0 && encodingId == 3));
505
506 FT_Set_Charmap(face, face->charmaps[i]);
507 if (glyphToUnicode->isEmpty()) {
508 glyphToUnicode->setCount(face->num_glyphs);
509 memset(glyphToUnicode->begin(), 0,
510 sizeof(SkUnichar) * face->num_glyphs);
511 }
512
513 // Iterate through each cmap entry.
514 FT_UInt glyphIndex;
515 for (SkUnichar charCode = FT_Get_First_Char(face, &glyphIndex);
516 glyphIndex != 0;
517 charCode = FT_Get_Next_Char(face, charCode, &glyphIndex)) {
518 if (charCode &&
519 ((*glyphToUnicode)[glyphIndex] == 0 || preferredMap)) {
520 (*glyphToUnicode)[glyphIndex] = charCode;
521 }
522 }
523 }
524}
525
reed@google.com2689f612013-03-20 20:01:47 +0000526SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +0000527 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
528 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +0000529 uint32_t glyphIDsCount) const {
djsollen@google.comda957722011-11-16 17:00:46 +0000530#if defined(SK_BUILD_FOR_MAC)
reed@google.com8a5d6922011-03-14 15:08:03 +0000531 return NULL;
532#else
reed@google.comb4162b12013-07-02 16:32:29 +0000533 AutoFTAccess fta(this);
534 FT_Face face = fta.face();
535 if (!face) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000536 return NULL;
reed@google.comb4162b12013-07-02 16:32:29 +0000537 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000538
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000539 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
540 info->fFontName.set(FT_Get_Postscript_Name(face));
vandebo0f9bad02014-06-19 11:05:39 -0700541 info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
542 if (FT_HAS_MULTIPLE_MASTERS(face)) {
543 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
544 info->fFlags, SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag);
545 }
546 if (!canEmbed(face)) {
547 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
548 info->fFlags,
549 SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
550 }
551 if (!canSubset(face)) {
552 info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
553 info->fFlags,
554 SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag);
555 }
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000556 info->fLastGlyphID = face->num_glyphs - 1;
557 info->fEmSize = 1000;
558
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000559 bool cid = false;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000560 const char* fontType = FT_Get_X11_Font_Format(face);
vandebo@chromium.orgc3a2ae52011-02-03 21:48:23 +0000561 if (strcmp(fontType, "Type 1") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000562 info->fType = SkAdvancedTypefaceMetrics::kType1_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000563 } else if (strcmp(fontType, "CID Type 1") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000564 info->fType = SkAdvancedTypefaceMetrics::kType1CID_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000565 cid = true;
566 } else if (strcmp(fontType, "CFF") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000567 info->fType = SkAdvancedTypefaceMetrics::kCFF_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000568 } else if (strcmp(fontType, "TrueType") == 0) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000569 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000570 cid = true;
571 TT_Header* ttHeader;
572 if ((ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face,
573 ft_sfnt_head)) != NULL) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000574 info->fEmSize = ttHeader->Units_Per_EM;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000575 }
bungeman@google.com4d71db82013-12-02 19:10:02 +0000576 } else {
577 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000578 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000579
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000580 info->fStyle = 0;
581 if (FT_IS_FIXED_WIDTH(face))
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000582 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000583 if (face->style_flags & FT_STYLE_FLAG_ITALIC)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000584 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000585
586 PS_FontInfoRec ps_info;
587 TT_Postscript* tt_info;
588 if (FT_Get_PS_Font_Info(face, &ps_info) == 0) {
589 info->fItalicAngle = ps_info.italic_angle;
590 } else if ((tt_info =
591 (TT_Postscript*)FT_Get_Sfnt_Table(face,
592 ft_sfnt_post)) != NULL) {
593 info->fItalicAngle = SkFixedToScalar(tt_info->italicAngle);
594 } else {
595 info->fItalicAngle = 0;
596 }
597
598 info->fAscent = face->ascender;
599 info->fDescent = face->descender;
600
601 // Figure out a good guess for StemV - Min width of i, I, !, 1.
602 // This probably isn't very good with an italic font.
603 int16_t min_width = SHRT_MAX;
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000604 info->fStemV = 0;
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000605 char stem_chars[] = {'i', 'I', '!', '1'};
606 for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
607 FT_BBox bbox;
608 if (GetLetterCBox(face, stem_chars[i], &bbox)) {
609 int16_t width = bbox.xMax - bbox.xMin;
610 if (width > 0 && width < min_width) {
611 min_width = width;
612 info->fStemV = min_width;
613 }
614 }
615 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000616
617 TT_PCLT* pclt_info;
618 TT_OS2* os2_table;
619 if ((pclt_info = (TT_PCLT*)FT_Get_Sfnt_Table(face, ft_sfnt_pclt)) != NULL) {
620 info->fCapHeight = pclt_info->CapHeight;
621 uint8_t serif_style = pclt_info->SerifStyle & 0x3F;
622 if (serif_style >= 2 && serif_style <= 6)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000623 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000624 else if (serif_style >= 9 && serif_style <= 12)
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000625 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
bungeman@google.comcbe1b542013-12-16 17:02:39 +0000626 } else if (((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) &&
627 // sCapHeight is available only when version 2 or later.
628 os2_table->version != 0xFFFF &&
629 os2_table->version >= 2) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000630 info->fCapHeight = os2_table->sCapHeight;
631 } else {
632 // Figure out a good guess for CapHeight: average the height of M and X.
633 FT_BBox m_bbox, x_bbox;
634 bool got_m, got_x;
635 got_m = GetLetterCBox(face, 'M', &m_bbox);
636 got_x = GetLetterCBox(face, 'X', &x_bbox);
637 if (got_m && got_x) {
638 info->fCapHeight = (m_bbox.yMax - m_bbox.yMin + x_bbox.yMax -
639 x_bbox.yMin) / 2;
640 } else if (got_m && !got_x) {
641 info->fCapHeight = m_bbox.yMax - m_bbox.yMin;
642 } else if (!got_m && got_x) {
643 info->fCapHeight = x_bbox.yMax - x_bbox.yMin;
bungeman@google.com12bd4a02013-12-19 19:34:22 +0000644 } else {
645 // Last resort, use the ascent.
646 info->fCapHeight = info->fAscent;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000647 }
648 }
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000649
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000650 info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax,
651 face->bbox.xMax, face->bbox.yMin);
652
vandebo0f9bad02014-06-19 11:05:39 -0700653 if (!FT_IS_SCALABLE(face)) {
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000654 perGlyphInfo = SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo;
655 }
656
657 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000658 if (FT_IS_FIXED_WIDTH(face)) {
659 appendRange(&info->fGlyphWidths, 0);
660 int16_t advance = face->max_advance_width;
661 info->fGlyphWidths->fAdvance.append(1, &advance);
662 finishRange(info->fGlyphWidths.get(), 0,
663 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000664 } else if (!cid) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000665 appendRange(&info->fGlyphWidths, 0);
666 // So as to not blow out the stack, get advances in batches.
667 for (int gID = 0; gID < face->num_glyphs; gID += 128) {
668 FT_Fixed advances[128];
669 int advanceCount = 128;
670 if (gID + advanceCount > face->num_glyphs)
bungeman@google.comb8aa4dd2013-10-15 18:50:00 +0000671 advanceCount = face->num_glyphs - gID;
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000672 getAdvances(face, gID, advanceCount, FT_LOAD_NO_SCALE,
673 advances);
674 for (int i = 0; i < advanceCount; i++) {
vandebo@chromium.orgce8a1952012-10-22 20:09:31 +0000675 int16_t advance = advances[i];
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000676 info->fGlyphWidths->fAdvance.append(1, &advance);
677 }
678 }
679 finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1,
680 SkAdvancedTypefaceMetrics::WidthRange::kRange);
681 } else {
vandebo@chromium.org6f72d1e2011-02-14 23:19:59 +0000682 info->fGlyphWidths.reset(
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +0000683 getAdvanceData(face,
684 face->num_glyphs,
685 glyphIDs,
686 glyphIDsCount,
687 &getWidthAdvance));
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000688 }
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000689 }
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000690
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000691 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kVAdvance_PerGlyphInfo &&
692 FT_HAS_VERTICAL(face)) {
693 SkASSERT(false); // Not implemented yet.
694 }
695
696 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo &&
697 info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
698 // Postscript fonts may contain more than 255 glyphs, so we end up
699 // using multiple font descriptions with a glyph ordering. Record
700 // the name of each glyph.
701 info->fGlyphNames.reset(
702 new SkAutoTArray<SkString>(face->num_glyphs));
703 for (int gID = 0; gID < face->num_glyphs; gID++) {
704 char glyphName[128]; // PS limit for names is 127 bytes.
705 FT_Get_Glyph_Name(face, gID, glyphName, 128);
706 info->fGlyphNames->get()[gID].set(glyphName);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000707 }
708 }
709
vandebo@chromium.org6744d492011-05-09 18:13:47 +0000710 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo &&
711 info->fType != SkAdvancedTypefaceMetrics::kType1_Font &&
712 face->num_charmaps) {
713 populate_glyph_to_unicode(face, &(info->fGlyphToUnicode));
714 }
715
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000716 return info;
reed@google.com8a5d6922011-03-14 15:08:03 +0000717#endif
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000718}
vandebo@chromium.org37ad8fb2011-08-18 02:38:50 +0000719
reed@google.com618ef5e2011-01-26 22:10:41 +0000720///////////////////////////////////////////////////////////////////////////
721
reed@google.com8ed436c2011-07-21 14:12:36 +0000722static bool bothZero(SkScalar a, SkScalar b) {
723 return 0 == a && 0 == b;
724}
725
726// returns false if there is any non-90-rotation or skew
727static bool isAxisAligned(const SkScalerContext::Rec& rec) {
728 return 0 == rec.fPreSkewX &&
729 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
730 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
731}
732
reed@google.com0da48612013-03-19 16:06:52 +0000733SkScalerContext* SkTypeface_FreeType::onCreateScalerContext(
734 const SkDescriptor* desc) const {
735 SkScalerContext_FreeType* c = SkNEW_ARGS(SkScalerContext_FreeType,
736 (const_cast<SkTypeface_FreeType*>(this),
737 desc));
738 if (!c->success()) {
739 SkDELETE(c);
740 c = NULL;
741 }
742 return c;
743}
744
745void SkTypeface_FreeType::onFilterRec(SkScalerContextRec* rec) const {
bungeman@google.com8cf32262012-04-02 14:34:30 +0000746 //BOGUS: http://code.google.com/p/chromium/issues/detail?id=121119
747 //Cap the requested size as larger sizes give bogus values.
748 //Remove when http://code.google.com/p/skia/issues/detail?id=554 is fixed.
bungeman@google.com5582e632012-04-02 14:51:54 +0000749 if (rec->fTextSize > SkIntToScalar(1 << 14)) {
scroggo@google.com94bc60f2012-10-04 20:45:06 +0000750 rec->fTextSize = SkIntToScalar(1 << 14);
bungeman@google.com8cf32262012-04-02 14:34:30 +0000751 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000752
reed@google.comfb2fdcc2012-10-17 15:49:36 +0000753 if (!is_lcd_supported() && isLCD(*rec)) {
reed@google.com618ef5e2011-01-26 22:10:41 +0000754 // If the runtime Freetype library doesn't support LCD mode, we disable
755 // it here.
756 rec->fMaskFormat = SkMask::kA8_Format;
757 }
reed@google.com5b31b0f2011-02-23 14:41:42 +0000758
reed@google.com618ef5e2011-01-26 22:10:41 +0000759 SkPaint::Hinting h = rec->getHinting();
reed@google.comeffc5012011-06-27 16:44:46 +0000760 if (SkPaint::kFull_Hinting == h && !isLCD(*rec)) {
reed@google.com618ef5e2011-01-26 22:10:41 +0000761 // collapse full->normal hinting if we're not doing LCD
762 h = SkPaint::kNormal_Hinting;
reed@google.com618ef5e2011-01-26 22:10:41 +0000763 }
bungeman@google.comf4f2b802012-03-08 19:19:51 +0000764 if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag)) {
reed@google.com1ac83502012-02-28 17:06:02 +0000765 if (SkPaint::kNo_Hinting != h) {
766 h = SkPaint::kSlight_Hinting;
767 }
768 }
769
reed@google.com8ed436c2011-07-21 14:12:36 +0000770 // rotated text looks bad with hinting, so we disable it as needed
771 if (!isAxisAligned(*rec)) {
772 h = SkPaint::kNo_Hinting;
773 }
reed@google.com618ef5e2011-01-26 22:10:41 +0000774 rec->setHinting(h);
reed@google.comffe49f52011-11-22 19:42:41 +0000775
bungeman@google.com97efada2012-07-30 20:40:50 +0000776#ifndef SK_GAMMA_APPLY_TO_A8
777 if (!isLCD(*rec)) {
778 rec->ignorePreBlend();
reed@google.comffe49f52011-11-22 19:42:41 +0000779 }
reed@google.com1ac83502012-02-28 17:06:02 +0000780#endif
reed@google.com618ef5e2011-01-26 22:10:41 +0000781}
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000782
reed@google.com38c37dd2013-03-21 15:36:26 +0000783int SkTypeface_FreeType::onGetUPEM() const {
reed@google.comb4162b12013-07-02 16:32:29 +0000784 AutoFTAccess fta(this);
785 FT_Face face = fta.face();
786 return face ? face->units_per_EM : 0;
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000787}
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000788
reed@google.com35fe7372013-10-30 15:07:03 +0000789bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[],
790 int count, int32_t adjustments[]) const {
791 AutoFTAccess fta(this);
792 FT_Face face = fta.face();
793 if (!face || !FT_HAS_KERNING(face)) {
794 return false;
795 }
796
797 for (int i = 0; i < count - 1; ++i) {
798 FT_Vector delta;
799 FT_Error err = FT_Get_Kerning(face, glyphs[i], glyphs[i+1],
800 FT_KERNING_UNSCALED, &delta);
801 if (err) {
802 return false;
803 }
804 adjustments[i] = delta.x;
805 }
806 return true;
807}
808
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000809static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) {
810 // early out if face is bad
811 if (face == NULL) {
812 SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n"));
813 return -1;
814 }
815 // determine target ppem
816 FT_Pos targetPPEM = SkFixedToFDot6(scaleY);
817 // find a bitmap strike equal to or just larger than the requested size
818 FT_Int chosenStrikeIndex = -1;
819 FT_Pos chosenPPEM = 0;
820 for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) {
821 FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem;
822 if (thisPPEM == targetPPEM) {
823 // exact match - our search stops here
824 chosenPPEM = thisPPEM;
825 chosenStrikeIndex = strikeIndex;
826 break;
827 } else if (chosenPPEM < targetPPEM) {
828 // attempt to increase chosenPPEM
829 if (thisPPEM > chosenPPEM) {
830 chosenPPEM = thisPPEM;
831 chosenStrikeIndex = strikeIndex;
832 }
833 } else {
834 // attempt to decrease chosenPPEM, but not below targetPPEM
835 if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) {
836 chosenPPEM = thisPPEM;
837 chosenStrikeIndex = strikeIndex;
838 }
839 }
840 }
841 if (chosenStrikeIndex != -1) {
842 // use the chosen strike
843 FT_Error err = FT_Select_Size(face, chosenStrikeIndex);
844 if (err != 0) {
845 SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name,
846 chosenStrikeIndex, err));
847 chosenStrikeIndex = -1;
848 }
849 }
850 return chosenStrikeIndex;
851}
852
reed@google.com0da48612013-03-19 16:06:52 +0000853SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
854 const SkDescriptor* desc)
855 : SkScalerContext_FreeType_Base(typeface, desc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 SkAutoMutexAcquire ac(gFTMutex);
857
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 if (gFTCount == 0) {
reed@android.com659aaf92009-07-23 15:20:21 +0000859 if (!InitFreetype()) {
860 sk_throw();
861 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 }
863 ++gFTCount;
864
865 // load the font file
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000866 fStrikeIndex = -1;
reed@android.com62900b42009-02-11 15:07:19 +0000867 fFTSize = NULL;
868 fFace = NULL;
reed@google.com2cdc6712013-03-21 18:22:00 +0000869 fFaceRec = ref_ft_face(typeface);
reed@android.com62900b42009-02-11 15:07:19 +0000870 if (NULL == fFaceRec) {
871 return;
872 }
873 fFace = fFaceRec->fFace;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874
bungeman@google.comd3fbd342014-04-15 15:52:07 +0000875 // A is the total matrix.
876 SkMatrix A;
877 fRec.getSingleMatrix(&A);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878
bungeman@google.comd3fbd342014-04-15 15:52:07 +0000879 SkScalar sx = A.getScaleX();
880 SkScalar sy = A.getScaleY();
reed@google.comf073b332013-05-06 12:21:16 +0000881 fMatrix22Scalar.reset();
882
bungeman@google.comd3fbd342014-04-15 15:52:07 +0000883 // In GDI, the hinter is aware of the current transformation
884 // (the transform is in some sense applied before/with the hinting).
885 // The bytecode can then test if it is rotated or stretched and decide
886 // to apply instructions or not.
887 //
888 // FreeType, however, always does the transformation strictly after hinting.
889 // It just sets 'rotated' and 'stretched' to false and only applies the
890 // size before hinting.
891 //
892 // Also, FreeType respects the head::flags::IntegerScaling flag,
893 // (although this is patched out on most major distros)
894 // so it is critical to get the size correct on the request.
895 //
896 // This also gets us the actual closest size on bitmap fonts as well.
897 if (A.getSkewX() || A.getSkewY() || sx < 0 || sy < 0) {
898 // h is where A maps the horizontal baseline.
899 SkPoint h = SkPoint::Make(SK_Scalar1, 0);
900 A.mapPoints(&h, 1);
901
902 // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
903 SkMatrix G;
904 SkComputeGivensRotation(h, &G);
905
906 // GA is the matrix A with rotation removed.
907 SkMatrix GA(G);
908 GA.preConcat(A);
909
910 sx = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
911 sy = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
912
913 // sA is the total matrix A without the text scale.
914 SkMatrix sA(A);
915 sA.preScale(SkScalarInvert(sx), SkScalarInvert(sy)); //remove text size
916
917 fMatrix22Scalar.setScaleX(sA.getScaleX());
918 fMatrix22Scalar.setSkewX(-sA.getSkewX());
919 fMatrix22Scalar.setSkewY(-sA.getSkewY());
920 fMatrix22Scalar.setScaleY(sA.getScaleY());
921 }
922 fScale.set(sx, sy);
923 fScaleX = SkScalarToFixed(sx);
924 fScaleY = SkScalarToFixed(sy);
925 fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX());
926 fMatrix22.xy = SkScalarToFixed(fMatrix22Scalar.getSkewX());
927 fMatrix22.yx = SkScalarToFixed(fMatrix22Scalar.getSkewY());
928 fMatrix22.yy = SkScalarToFixed(fMatrix22Scalar.getScaleY());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929
reed@google.coma1bfa212012-03-08 21:57:12 +0000930 fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
931
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 // compute the flags we send to Load_Glyph
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000933 bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 {
reed@android.come4d0bc02009-07-24 19:53:20 +0000935 FT_Int32 loadFlags = FT_LOAD_DEFAULT;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936
agl@chromium.org70a303f2010-05-10 14:15:50 +0000937 if (SkMask::kBW_Format == fRec.fMaskFormat) {
938 // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
939 loadFlags = FT_LOAD_TARGET_MONO;
reed@google.comeffc5012011-06-27 16:44:46 +0000940 if (fRec.getHinting() == SkPaint::kNo_Hinting) {
agl@chromium.org70a303f2010-05-10 14:15:50 +0000941 loadFlags = FT_LOAD_NO_HINTING;
reed@google.combdc99882011-11-21 14:36:57 +0000942 linearMetrics = true;
reed@google.comeffc5012011-06-27 16:44:46 +0000943 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000944 } else {
945 switch (fRec.getHinting()) {
946 case SkPaint::kNo_Hinting:
947 loadFlags = FT_LOAD_NO_HINTING;
reed@google.combdc99882011-11-21 14:36:57 +0000948 linearMetrics = true;
agl@chromium.org70a303f2010-05-10 14:15:50 +0000949 break;
950 case SkPaint::kSlight_Hinting:
951 loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT
952 break;
953 case SkPaint::kNormal_Hinting:
bungeman@google.comf6f56872014-01-23 19:01:36 +0000954 if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000955 loadFlags = FT_LOAD_FORCE_AUTOHINT;
djsollen858a7892014-08-20 07:03:23 -0700956#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
957 } else {
958 loadFlags = FT_LOAD_NO_AUTOHINT;
959#endif
bungeman@google.comf6f56872014-01-23 19:01:36 +0000960 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000961 break;
962 case SkPaint::kFull_Hinting:
bungeman@google.comf6f56872014-01-23 19:01:36 +0000963 if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000964 loadFlags = FT_LOAD_FORCE_AUTOHINT;
965 break;
966 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000967 loadFlags = FT_LOAD_TARGET_NORMAL;
reed@google.comeffc5012011-06-27 16:44:46 +0000968 if (isLCD(fRec)) {
reed@google.coma1bfa212012-03-08 21:57:12 +0000969 if (fLCDIsVert) {
reed@google.comeffc5012011-06-27 16:44:46 +0000970 loadFlags = FT_LOAD_TARGET_LCD_V;
971 } else {
972 loadFlags = FT_LOAD_TARGET_LCD;
973 }
reed@google.comea2333d2011-03-14 16:44:56 +0000974 }
agl@chromium.org70a303f2010-05-10 14:15:50 +0000975 break;
976 default:
977 SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting());
978 break;
979 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 }
981
reed@google.comeffc5012011-06-27 16:44:46 +0000982 if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) {
agl@chromium.orge0d08992009-08-07 19:19:23 +0000983 loadFlags |= FT_LOAD_NO_BITMAP;
reed@google.comeffc5012011-06-27 16:44:46 +0000984 }
agl@chromium.orge0d08992009-08-07 19:19:23 +0000985
reed@google.com96a9f7912011-05-06 11:49:30 +0000986 // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct
987 // advances, as fontconfig and cairo do.
988 // See http://code.google.com/p/skia/issues/detail?id=222.
989 loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
990
bungeman@google.com8ff8a192012-09-25 20:38:28 +0000991 // Use vertical layout if requested.
992 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
993 loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
994 }
995
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +0000996 loadFlags |= FT_LOAD_COLOR;
997
reed@android.come4d0bc02009-07-24 19:53:20 +0000998 fLoadGlyphFlags = loadFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 }
1000
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001001 FT_Error err = FT_New_Size(fFace, &fFTSize);
1002 if (err != 0) {
1003 SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name));
1004 fFace = NULL;
1005 return;
1006 }
1007 err = FT_Activate_Size(fFTSize);
1008 if (err != 0) {
1009 SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY,
1010 err));
1011 fFTSize = NULL;
1012 return;
1013 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001015 if (FT_IS_SCALABLE(fFace)) {
1016 err = FT_Set_Char_Size(fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 if (err != 0) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001018 SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n",
1019 fFace, fScaleX, fScaleY, err));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 fFace = NULL;
1021 return;
1022 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001023 FT_Set_Transform(fFace, &fMatrix22, NULL);
1024 } else if (FT_HAS_FIXED_SIZES(fFace)) {
1025 fStrikeIndex = chooseBitmapStrike(fFace, fScaleY);
1026 if (fStrikeIndex == -1) {
1027 SkDEBUGF(("no glyphs for font \"%s\" size %f?\n",
1028 fFace->family_name, SkFixedToScalar(fScaleY)));
1029 } else {
1030 // FreeType does no provide linear metrics for bitmap fonts.
1031 linearMetrics = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001033 // FreeType documentation says:
1034 // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading.
1035 // Bitmap-only fonts ignore this flag.
1036 //
1037 // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag.
1038 // Force this flag off for bitmap only fonts.
1039 fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001041 } else {
1042 SkDEBUGF(("unknown kind of font \"%s\" size %f?\n",
1043 fFace->family_name, SkFixedToScalar(fScaleY)));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001045
1046 fDoLinearMetrics = linearMetrics;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047}
1048
1049SkScalerContext_FreeType::~SkScalerContext_FreeType() {
scroggo@google.com94bc60f2012-10-04 20:45:06 +00001050 SkAutoMutexAcquire ac(gFTMutex);
1051
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 if (fFTSize != NULL) {
1053 FT_Done_Size(fFTSize);
1054 }
1055
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 if (fFace != NULL) {
1057 unref_ft_face(fFace);
1058 }
1059 if (--gFTCount == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 FT_Done_FreeType(gFTLibrary);
1061 SkDEBUGCODE(gFTLibrary = NULL;)
1062 }
1063}
1064
1065/* We call this before each use of the fFace, since we may be sharing
1066 this face with other context (at different sizes).
1067*/
1068FT_Error SkScalerContext_FreeType::setupSize() {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001069 FT_Error err = FT_Activate_Size(fFTSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 if (err != 0) {
1071 SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001072 fFaceRec->fFontID, fScaleX, fScaleY, err));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 fFTSize = NULL;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001074 return err;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001076
1077 // seems we need to reset this every time (not sure why, but without it
1078 // I get random italics from some other fFTSize)
1079 FT_Set_Transform(fFace, &fMatrix22, NULL);
1080 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081}
1082
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +00001083unsigned SkScalerContext_FreeType::generateGlyphCount() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 return fFace->num_glyphs;
1085}
1086
1087uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
1088 return SkToU16(FT_Get_Char_Index( fFace, uni ));
1089}
1090
reed@android.com9d3a9852010-01-08 14:07:42 +00001091SkUnichar SkScalerContext_FreeType::generateGlyphToChar(uint16_t glyph) {
1092 // iterate through each cmap entry, looking for matching glyph indices
1093 FT_UInt glyphIndex;
1094 SkUnichar charCode = FT_Get_First_Char( fFace, &glyphIndex );
1095
1096 while (glyphIndex != 0) {
1097 if (glyphIndex == glyph) {
1098 return charCode;
1099 }
1100 charCode = FT_Get_Next_Char( fFace, charCode, &glyphIndex );
1101 }
1102
1103 return 0;
1104}
1105
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
1107#ifdef FT_ADVANCES_H
1108 /* unhinted and light hinted text have linearly scaled advances
1109 * which are very cheap to compute with some font formats...
1110 */
reed@google.combdc99882011-11-21 14:36:57 +00001111 if (fDoLinearMetrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 SkAutoMutexAcquire ac(gFTMutex);
1113
1114 if (this->setupSize()) {
reed@android.com62900b42009-02-11 15:07:19 +00001115 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116 return;
1117 }
1118
1119 FT_Error error;
1120 FT_Fixed advance;
1121
djsollen1b277042014-08-06 06:58:06 -07001122 error = FT_Get_Advance( fFace, glyph->getGlyphID(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
1124 &advance );
1125 if (0 == error) {
1126 glyph->fRsbDelta = 0;
1127 glyph->fLsbDelta = 0;
reed@google.comd074c372012-07-18 13:45:58 +00001128 glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance);
1129 glyph->fAdvanceY = - SkFixedMul(fMatrix22.yx, advance);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 return;
1131 }
1132 }
1133#endif /* FT_ADVANCES_H */
1134 /* otherwise, we need to load/hint the glyph, which is slower */
1135 this->generateMetrics(glyph);
1136 return;
1137}
1138
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001139void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph,
1140 FT_BBox* bbox,
1141 bool snapToPixelBoundary) {
1142
1143 FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
1144
1145 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
george@mozilla.comc59b5da2012-08-23 00:39:08 +00001146 int dx = SkFixedToFDot6(glyph->getSubXFixed());
1147 int dy = SkFixedToFDot6(glyph->getSubYFixed());
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001148 // negate dy since freetype-y-goes-up and skia-y-goes-down
1149 bbox->xMin += dx;
1150 bbox->yMin -= dy;
1151 bbox->xMax += dx;
1152 bbox->yMax -= dy;
1153 }
1154
1155 // outset the box to integral boundaries
1156 if (snapToPixelBoundary) {
1157 bbox->xMin &= ~63;
1158 bbox->yMin &= ~63;
1159 bbox->xMax = (bbox->xMax + 63) & ~63;
1160 bbox->yMax = (bbox->yMax + 63) & ~63;
1161 }
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001162
1163 // Must come after snapToPixelBoundary so that the width and height are
1164 // consistent. Otherwise asserts will fire later on when generating the
1165 // glyph image.
1166 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1167 FT_Vector vector;
1168 vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
1169 vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
1170 FT_Vector_Transform(&vector, &fMatrix22);
1171 bbox->xMin += vector.x;
1172 bbox->xMax += vector.x;
1173 bbox->yMin += vector.y;
1174 bbox->yMax += vector.y;
1175 }
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001176}
1177
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001178bool SkScalerContext_FreeType::getCBoxForLetter(char letter, FT_BBox* bbox) {
1179 const FT_UInt glyph_id = FT_Get_Char_Index(fFace, letter);
1180 if (!glyph_id)
1181 return false;
1182 if (FT_Load_Glyph(fFace, glyph_id, fLoadGlyphFlags) != 0)
1183 return false;
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001184 emboldenIfNeeded(fFace, fFace->glyph);
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001185 FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
1186 return true;
1187}
1188
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001189void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
1190 if (isLCD(fRec)) {
1191 if (fLCDIsVert) {
1192 glyph->fHeight += gLCDExtra;
1193 glyph->fTop -= gLCDExtra >> 1;
1194 } else {
1195 glyph->fWidth += gLCDExtra;
1196 glyph->fLeft -= gLCDExtra >> 1;
1197 }
1198 }
1199}
1200
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001201inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) {
1202 glyph.fWidth *= scale;
1203 glyph.fHeight *= scale;
1204 glyph.fTop *= scale;
1205 glyph.fLeft *= scale;
1206
1207 SkFixed fixedScale = SkScalarToFixed(scale);
1208 glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale);
1209 glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale);
1210}
1211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
1213 SkAutoMutexAcquire ac(gFTMutex);
1214
1215 glyph->fRsbDelta = 0;
1216 glyph->fLsbDelta = 0;
1217
1218 FT_Error err;
1219
1220 if (this->setupSize()) {
1221 goto ERROR;
1222 }
1223
djsollen1b277042014-08-06 06:58:06 -07001224 err = FT_Load_Glyph( fFace, glyph->getGlyphID(), fLoadGlyphFlags );
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 if (err != 0) {
mike@reedtribe.org7a722f02012-11-15 02:12:14 +00001226#if 0
1227 SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%x) returned 0x%x\n",
djsollen1b277042014-08-06 06:58:06 -07001228 fFaceRec->fFontID, glyph->getGlyphID(), fLoadGlyphFlags, err));
mike@reedtribe.org7a722f02012-11-15 02:12:14 +00001229#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 ERROR:
reed@android.com62900b42009-02-11 15:07:19 +00001231 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 return;
1233 }
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001234 emboldenIfNeeded(fFace, fFace->glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235
1236 switch ( fFace->glyph->format ) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001237 case FT_GLYPH_FORMAT_OUTLINE:
bungeman@google.com0f0c2882011-11-04 15:47:41 +00001238 if (0 == fFace->glyph->outline.n_contours) {
1239 glyph->fWidth = 0;
1240 glyph->fHeight = 0;
1241 glyph->fTop = 0;
1242 glyph->fLeft = 0;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001243 } else {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001244 FT_BBox bbox;
1245 getBBoxForCurrentGlyph(glyph, &bbox, true);
1246
1247 glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin));
1248 glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin));
1249 glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax));
1250 glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin));
1251
1252 updateGlyphIfLCD(glyph);
bungeman@google.com0f0c2882011-11-04 15:47:41 +00001253 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 break;
1255
1256 case FT_GLYPH_FORMAT_BITMAP:
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001257 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1258 FT_Vector vector;
1259 vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
1260 vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
1261 FT_Vector_Transform(&vector, &fMatrix22);
1262 fFace->glyph->bitmap_left += SkFDot6Floor(vector.x);
1263 fFace->glyph->bitmap_top += SkFDot6Floor(vector.y);
1264 }
1265
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001266 if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
1267 glyph->fMaskFormat = SkMask::kARGB32_Format;
1268 }
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 glyph->fWidth = SkToU16(fFace->glyph->bitmap.width);
1271 glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows);
1272 glyph->fTop = -SkToS16(fFace->glyph->bitmap_top);
1273 glyph->fLeft = SkToS16(fFace->glyph->bitmap_left);
1274 break;
1275
1276 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001277 SkDEBUGFAIL("unknown glyph format");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 goto ERROR;
1279 }
1280
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001281 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1282 if (fDoLinearMetrics) {
1283 glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance);
1284 glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance);
1285 } else {
1286 glyph->fAdvanceX = -SkFDot6ToFixed(fFace->glyph->advance.x);
1287 glyph->fAdvanceY = SkFDot6ToFixed(fFace->glyph->advance.y);
1288 }
bungeman@google.com34f10262012-03-23 18:11:47 +00001289 } else {
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001290 if (fDoLinearMetrics) {
1291 glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
1292 glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
1293 } else {
1294 glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
1295 glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
bungeman@google.com34f10262012-03-23 18:11:47 +00001296
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001297 if (fRec.fFlags & kDevKernText_Flag) {
1298 glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
1299 glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001300 }
1301 }
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001302 }
1303
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001304 if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) {
1305 // NOTE: both dimensions are scaled by y_ppem. this is WAI.
1306 scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY),
1307 SkIntToScalar(fFace->size->metrics.y_ppem)));
1308 }
djsollen@google.comd8b599c2012-03-19 19:44:19 +00001309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310#ifdef ENABLE_GLYPH_SPEW
1311 SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
djsollen1b277042014-08-06 06:58:06 -07001312 SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(), fLoadGlyphFlags, glyph->fWidth));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313#endif
1314}
1315
reed@google.comea2333d2011-03-14 16:44:56 +00001316
bungeman@google.coma76de722012-10-26 19:35:54 +00001317void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 SkAutoMutexAcquire ac(gFTMutex);
1319
1320 FT_Error err;
1321
1322 if (this->setupSize()) {
1323 goto ERROR;
1324 }
1325
djsollen1b277042014-08-06 06:58:06 -07001326 err = FT_Load_Glyph( fFace, glyph.getGlyphID(), fLoadGlyphFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 if (err != 0) {
1328 SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
djsollen1b277042014-08-06 06:58:06 -07001329 glyph.getGlyphID(), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 ERROR:
1331 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
1332 return;
1333 }
1334
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001335 emboldenIfNeeded(fFace, fFace->glyph);
bungeman@google.coma76de722012-10-26 19:35:54 +00001336 generateGlyphImage(fFace, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337}
1338
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339
1340void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
1341 SkPath* path) {
1342 SkAutoMutexAcquire ac(gFTMutex);
1343
1344 SkASSERT(&glyph && path);
1345
1346 if (this->setupSize()) {
1347 path->reset();
1348 return;
1349 }
1350
1351 uint32_t flags = fLoadGlyphFlags;
1352 flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
1353 flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
1354
djsollen1b277042014-08-06 06:58:06 -07001355 FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(), flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356
1357 if (err != 0) {
1358 SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
djsollen1b277042014-08-06 06:58:06 -07001359 glyph.getGlyphID(), flags, err));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 path->reset();
1361 return;
1362 }
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001363 emboldenIfNeeded(fFace, fFace->glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
sugoi@google.com66a58ac2013-03-05 20:40:52 +00001365 generateGlyphPath(fFace, path);
bungeman@google.com8ff8a192012-09-25 20:38:28 +00001366
1367 // The path's origin from FreeType is always the horizontal layout origin.
1368 // Offset the path so that it is relative to the vertical origin if needed.
1369 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1370 FT_Vector vector;
1371 vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
1372 vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
1373 FT_Vector_Transform(&vector, &fMatrix22);
1374 path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y));
1375 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376}
1377
bungeman41078062014-07-07 08:16:37 -07001378void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* metrics) {
1379 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 return;
1381 }
1382
bungeman41078062014-07-07 08:16:37 -07001383 SkAutoMutexAcquire ac(gFTMutex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384
1385 if (this->setupSize()) {
reed@android.coma8a8b8b2009-05-04 15:00:11 +00001386 ERROR:
bungeman41078062014-07-07 08:16:37 -07001387 sk_bzero(metrics, sizeof(*metrics));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 return;
1389 }
1390
reed@android.coma8a8b8b2009-05-04 15:00:11 +00001391 FT_Face face = fFace;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001392 SkScalar scaleX = fScale.x();
reed@google.comf073b332013-05-06 12:21:16 +00001393 SkScalar scaleY = fScale.y();
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001394 SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY;
1395 SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001397 // fetch units/EM from "head" table if needed (ie for bitmap fonts)
1398 SkScalar upem = SkIntToScalar(face->units_per_EM);
1399 if (!upem) {
1400 TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head);
1401 if (ttHeader) {
1402 upem = SkIntToScalar(ttHeader->Units_Per_EM);
agl@chromium.orgcc3096b2009-04-22 22:09:04 +00001403 }
1404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001406 // use the os/2 table as a source of reasonable defaults.
1407 SkScalar x_height = 0.0f;
1408 SkScalar avgCharWidth = 0.0f;
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001409 SkScalar cap_height = 0.0f;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001410 TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
1411 if (os2) {
1412 x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem;
1413 avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem;
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001414 if (os2->version != 0xFFFF && os2->version >= 2) {
1415 cap_height = scaleX * SkIntToScalar(os2->sCapHeight) / upem;
1416 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001417 }
1418
1419 // pull from format-specific metrics as needed
1420 SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax;
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001421 SkScalar underlineThickness, underlinePosition;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001422 if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font
1423 ascent = -SkIntToScalar(face->ascender) / upem;
1424 descent = -SkIntToScalar(face->descender) / upem;
1425 leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem;
1426 xmin = SkIntToScalar(face->bbox.xMin) / upem;
1427 xmax = SkIntToScalar(face->bbox.xMax) / upem;
1428 ymin = -SkIntToScalar(face->bbox.yMin) / upem;
1429 ymax = -SkIntToScalar(face->bbox.yMax) / upem;
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001430 underlineThickness = SkIntToScalar(face->underline_thickness) / upem;
commit-bot@chromium.orgd3031aa2014-05-14 14:54:51 +00001431 underlinePosition = -SkIntToScalar(face->underline_position +
1432 face->underline_thickness / 2) / upem;
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001433
bungeman41078062014-07-07 08:16:37 -07001434 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1435 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
1436
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001437 // we may be able to synthesize x_height and cap_height from outline
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001438 if (!x_height) {
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001439 FT_BBox bbox;
1440 if (getCBoxForLetter('x', &bbox)) {
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001441 x_height = SkIntToScalar(bbox.yMax) / 64.0f;
1442 }
1443 }
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001444 if (!cap_height) {
1445 FT_BBox bbox;
1446 if (getCBoxForLetter('H', &bbox)) {
1447 cap_height = SkIntToScalar(bbox.yMax) / 64.0f;
1448 }
1449 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001450 } else if (fStrikeIndex != -1) { // bitmap strike metrics
1451 SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem);
1452 SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem);
1453 ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f);
1454 descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f);
1455 leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f))
1456 + ascent - descent;
1457 xmin = 0.0f;
1458 xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem;
1459 ymin = descent + leading;
1460 ymax = ascent - descent;
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001461 underlineThickness = 0;
1462 underlinePosition = 0;
1463
bungeman41078062014-07-07 08:16:37 -07001464 metrics->fFlags &= ~SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1465 metrics->fFlags &= ~SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001466 } else {
1467 goto ERROR;
1468 }
1469
1470 // synthesize elements that were not provided by the os/2 table or format-specific metrics
1471 if (!x_height) {
1472 x_height = -ascent;
1473 }
1474 if (!avgCharWidth) {
1475 avgCharWidth = xmax - xmin;
1476 }
bungeman@google.comcbe1b542013-12-16 17:02:39 +00001477 if (!cap_height) {
1478 cap_height = -ascent;
1479 }
bungeman@google.comc9a8a7e2013-12-10 18:09:36 +00001480
1481 // disallow negative linespacing
1482 if (leading < 0.0f) {
1483 leading = 0.0f;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 }
1485
bungeman41078062014-07-07 08:16:37 -07001486 SkScalar scale = myy;
1487 if (this->isVertical()) {
1488 scale = mxy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 }
bungeman41078062014-07-07 08:16:37 -07001490 metrics->fTop = ymax * scale;
1491 metrics->fAscent = ascent * scale;
1492 metrics->fDescent = descent * scale;
1493 metrics->fBottom = ymin * scale;
1494 metrics->fLeading = leading * scale;
1495 metrics->fAvgCharWidth = avgCharWidth * scale;
1496 metrics->fXMin = xmin;
1497 metrics->fXMax = xmax;
1498 metrics->fXHeight = x_height;
1499 metrics->fCapHeight = cap_height;
1500 metrics->fUnderlineThickness = underlineThickness * scale;
1501 metrics->fUnderlinePosition = underlinePosition * scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001504void SkScalerContext_FreeType::emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph)
1505{
commit-bot@chromium.org921d2b32014-04-01 19:03:07 +00001506 // check to see if the embolden bit is set
1507 if (0 == (fRec.fFlags & SkScalerContext::kEmbolden_Flag)) {
1508 return;
1509 }
1510
commit-bot@chromium.org921d2b32014-04-01 19:03:07 +00001511 switch (glyph->format) {
1512 case FT_GLYPH_FORMAT_OUTLINE:
1513 FT_Pos strength;
1514 strength = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) / 24;
1515 FT_Outline_Embolden(&glyph->outline, strength);
1516 break;
1517 case FT_GLYPH_FORMAT_BITMAP:
1518 FT_GlyphSlot_Own_Bitmap(glyph);
1519 FT_Bitmap_Embolden(glyph->library, &glyph->bitmap, kBitmapEmboldenStrength, 0);
1520 break;
1521 default:
1522 SkDEBUGFAIL("unknown glyph format");
commit-bot@chromium.org6fa81d72013-12-26 15:50:29 +00001523 }
1524}
1525
reed@google.comb4162b12013-07-02 16:32:29 +00001526///////////////////////////////////////////////////////////////////////////////
1527
1528#include "SkUtils.h"
1529
1530static SkUnichar next_utf8(const void** chars) {
1531 return SkUTF8_NextUnichar((const char**)chars);
1532}
1533
1534static SkUnichar next_utf16(const void** chars) {
1535 return SkUTF16_NextUnichar((const uint16_t**)chars);
1536}
1537
1538static SkUnichar next_utf32(const void** chars) {
1539 const SkUnichar** uniChars = (const SkUnichar**)chars;
1540 SkUnichar uni = **uniChars;
1541 *uniChars += 1;
1542 return uni;
1543}
1544
1545typedef SkUnichar (*EncodingProc)(const void**);
1546
1547static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) {
1548 static const EncodingProc gProcs[] = {
1549 next_utf8, next_utf16, next_utf32
1550 };
1551 SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs));
1552 return gProcs[enc];
1553}
1554
1555int SkTypeface_FreeType::onCharsToGlyphs(const void* chars, Encoding encoding,
1556 uint16_t glyphs[], int glyphCount) const {
1557 AutoFTAccess fta(this);
1558 FT_Face face = fta.face();
1559 if (!face) {
1560 if (glyphs) {
1561 sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
1562 }
1563 return 0;
1564 }
1565
1566 EncodingProc next_uni_proc = find_encoding_proc(encoding);
1567
1568 if (NULL == glyphs) {
1569 for (int i = 0; i < glyphCount; ++i) {
1570 if (0 == FT_Get_Char_Index(face, next_uni_proc(&chars))) {
1571 return i;
1572 }
1573 }
1574 return glyphCount;
1575 } else {
1576 int first = glyphCount;
1577 for (int i = 0; i < glyphCount; ++i) {
1578 unsigned id = FT_Get_Char_Index(face, next_uni_proc(&chars));
1579 glyphs[i] = SkToU16(id);
1580 if (0 == id && i < first) {
1581 first = i;
1582 }
1583 }
1584 return first;
1585 }
1586}
1587
1588int SkTypeface_FreeType::onCountGlyphs() const {
1589 // we cache this value, using -1 as a sentinel for "not computed"
1590 if (fGlyphCount < 0) {
1591 AutoFTAccess fta(this);
1592 FT_Face face = fta.face();
1593 // if the face failed, we still assign a non-negative value
1594 fGlyphCount = face ? face->num_glyphs : 0;
1595 }
1596 return fGlyphCount;
1597}
1598
bungeman@google.com839702b2013-08-07 17:09:22 +00001599SkTypeface::LocalizedStrings* SkTypeface_FreeType::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001600 SkTypeface::LocalizedStrings* nameIter =
1601 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
1602 if (NULL == nameIter) {
1603 SkString familyName;
1604 this->getFamilyName(&familyName);
1605 SkString language("und"); //undetermined
1606 nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
1607 }
1608 return nameIter;
1609}
1610
bungeman@google.comddc218e2013-08-01 22:29:43 +00001611int SkTypeface_FreeType::onGetTableTags(SkFontTableTag tags[]) const {
1612 AutoFTAccess fta(this);
1613 FT_Face face = fta.face();
1614
1615 FT_ULong tableCount = 0;
1616 FT_Error error;
1617
1618 // When 'tag' is NULL, returns number of tables in 'length'.
1619 error = FT_Sfnt_Table_Info(face, 0, NULL, &tableCount);
1620 if (error) {
1621 return 0;
1622 }
1623
1624 if (tags) {
1625 for (FT_ULong tableIndex = 0; tableIndex < tableCount; ++tableIndex) {
1626 FT_ULong tableTag;
1627 FT_ULong tablelength;
1628 error = FT_Sfnt_Table_Info(face, tableIndex, &tableTag, &tablelength);
1629 if (error) {
1630 return 0;
1631 }
1632 tags[tableIndex] = static_cast<SkFontTableTag>(tableTag);
1633 }
1634 }
1635 return tableCount;
1636}
1637
1638size_t SkTypeface_FreeType::onGetTableData(SkFontTableTag tag, size_t offset,
1639 size_t length, void* data) const
1640{
1641 AutoFTAccess fta(this);
1642 FT_Face face = fta.face();
1643
1644 FT_ULong tableLength = 0;
1645 FT_Error error;
1646
1647 // When 'length' is 0 it is overwritten with the full table length; 'offset' is ignored.
1648 error = FT_Load_Sfnt_Table(face, tag, 0, NULL, &tableLength);
1649 if (error) {
1650 return 0;
1651 }
1652
1653 if (offset > tableLength) {
1654 return 0;
1655 }
bungeman@google.com5ecd4fa2013-08-01 22:48:21 +00001656 FT_ULong size = SkTMin((FT_ULong)length, tableLength - (FT_ULong)offset);
bsalomon49f085d2014-09-05 13:34:00 -07001657 if (data) {
bungeman@google.comddc218e2013-08-01 22:29:43 +00001658 error = FT_Load_Sfnt_Table(face, tag, offset, reinterpret_cast<FT_Byte*>(data), &size);
1659 if (error) {
1660 return 0;
1661 }
1662 }
1663
1664 return size;
1665}
1666
reed@google.comb4162b12013-07-02 16:32:29 +00001667///////////////////////////////////////////////////////////////////////////////
1668///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669
bungeman3a21d612014-07-11 08:52:26 -07001670/*static*/ bool SkTypeface_FreeType::ScanFont(
1671 SkStream* stream, int ttcIndex, SkString* name, SkTypeface::Style* style, bool* isFixedPitch)
1672{
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 FT_Library library;
reed@android.combfbd4ff2009-07-23 17:44:41 +00001674 if (FT_Init_FreeType(&library)) {
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001675 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 }
1677
1678 FT_Open_Args args;
1679 memset(&args, 0, sizeof(args));
1680
1681 const void* memoryBase = stream->getMemoryBase();
1682 FT_StreamRec streamRec;
1683
bsalomon49f085d2014-09-05 13:34:00 -07001684 if (memoryBase) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685 args.flags = FT_OPEN_MEMORY;
1686 args.memory_base = (const FT_Byte*)memoryBase;
1687 args.memory_size = stream->getLength();
1688 } else {
1689 memset(&streamRec, 0, sizeof(streamRec));
djsollen@google.com5dd45022013-03-21 13:30:54 +00001690 streamRec.size = stream->getLength();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691 streamRec.descriptor.pointer = stream;
1692 streamRec.read = sk_stream_read;
1693 streamRec.close = sk_stream_close;
1694
1695 args.flags = FT_OPEN_STREAM;
1696 args.stream = &streamRec;
1697 }
1698
1699 FT_Face face;
bungeman3a21d612014-07-11 08:52:26 -07001700 if (FT_Open_Face(library, &args, ttcIndex, &face)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701 FT_Done_FreeType(library);
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001702 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 }
1704
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001705 int tempStyle = SkTypeface::kNormal;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001707 tempStyle |= SkTypeface::kBold;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708 }
1709 if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001710 tempStyle |= SkTypeface::kItalic;
1711 }
1712
1713 if (name) {
1714 name->set(face->family_name);
1715 }
1716 if (style) {
1717 *style = (SkTypeface::Style) tempStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 }
bungeman@google.comfe747652013-03-25 19:36:11 +00001719 if (isFixedPitch) {
1720 *isFixedPitch = FT_IS_FIXED_WIDTH(face);
reed@google.com5b31b0f2011-02-23 14:41:42 +00001721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722
1723 FT_Done_Face(face);
1724 FT_Done_FreeType(library);
djsollen@google.com4dc686d2012-02-15 21:03:45 +00001725 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}