blob: 8c11604a00f8bb26fecceb0f714d01d6b9b11ecc [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
18#include "SkScalerContext.h"
19#include "SkBitmap.h"
20#include "SkCanvas.h"
21#include "SkDescriptor.h"
22#include "SkFDot6.h"
23#include "SkFontHost.h"
24#include "SkMask.h"
25#include "SkStream.h"
26#include "SkString.h"
27#include "SkThread.h"
28#include "SkTemplates.h"
29
30#include <ft2build.h>
31#include FT_FREETYPE_H
32#include FT_OUTLINE_H
33#include FT_SIZES_H
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000034#include FT_TRUETYPE_TABLES_H
reed@android.com8a1c16f2008-12-17 15:59:43 +000035#ifdef FT_ADVANCES_H
36#include FT_ADVANCES_H
37#endif
38
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000039#if 0
40// Also include the files by name for build tools which require this.
41#include <freetype/freetype.h>
42#include <freetype/ftoutln.h>
43#include <freetype/ftsizes.h>
44#include <freetype/tttables.h>
45#include <freetype/ftadvanc.h>
46#endif
47
reed@android.com8a1c16f2008-12-17 15:59:43 +000048//#define ENABLE_GLYPH_SPEW // for tracing calls
49//#define DUMP_STRIKE_CREATION
50
51#ifdef SK_DEBUG
52 #define SkASSERT_CONTINUE(pred) \
53 do { \
54 if (!(pred)) \
55 SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \
56 } while (false)
57#else
58 #define SkASSERT_CONTINUE(pred)
59#endif
60
61//////////////////////////////////////////////////////////////////////////
62
63struct SkFaceRec;
64
65static SkMutex gFTMutex;
66static int gFTCount;
67static FT_Library gFTLibrary;
68static SkFaceRec* gFaceRecHead;
69
70/////////////////////////////////////////////////////////////////////////
71
72class SkScalerContext_FreeType : public SkScalerContext {
73public:
74 SkScalerContext_FreeType(const SkDescriptor* desc);
75 virtual ~SkScalerContext_FreeType();
agl@chromium.orgcc3096b2009-04-22 22:09:04 +000076
reed@android.com62900b42009-02-11 15:07:19 +000077 bool success() const {
78 return fFaceRec != NULL && fFTSize != NULL;
79 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81protected:
82 virtual unsigned generateGlyphCount() const;
83 virtual uint16_t generateCharToGlyph(SkUnichar uni);
84 virtual void generateAdvance(SkGlyph* glyph);
85 virtual void generateMetrics(SkGlyph* glyph);
86 virtual void generateImage(const SkGlyph& glyph);
87 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
88 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
89 SkPaint::FontMetrics* my);
90
91private:
92 SkFaceRec* fFaceRec;
93 FT_Face fFace; // reference to shared face in gFaceRecHead
94 FT_Size fFTSize; // our own copy
95 SkFixed fScaleX, fScaleY;
96 FT_Matrix fMatrix22;
97 uint32_t fLoadGlyphFlags;
98
99 FT_Error setupSize();
100};
101
102///////////////////////////////////////////////////////////////////////////
103///////////////////////////////////////////////////////////////////////////
104
105#include "SkStream.h"
106
107struct SkFaceRec {
108 SkFaceRec* fNext;
109 FT_Face fFace;
110 FT_StreamRec fFTStream;
111 SkStream* fSkStream;
112 uint32_t fRefCnt;
113 uint32_t fFontID;
114
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000115 // assumes ownership of the stream, will call unref() when its done
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 SkFaceRec(SkStream* strm, uint32_t fontID);
117 ~SkFaceRec() {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000118 fSkStream->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 }
120};
121
122extern "C" {
123 static unsigned long sk_stream_read(FT_Stream stream,
124 unsigned long offset,
125 unsigned char* buffer,
126 unsigned long count ) {
127 SkStream* str = (SkStream*)stream->descriptor.pointer;
128
129 if (count) {
130 if (!str->rewind()) {
131 return 0;
132 } else {
133 unsigned long ret;
134 if (offset) {
135 ret = str->read(NULL, offset);
136 if (ret != offset) {
137 return 0;
138 }
139 }
140 ret = str->read(buffer, count);
141 if (ret != count) {
142 return 0;
143 }
144 count = ret;
145 }
146 }
147 return count;
148 }
149
150 static void sk_stream_close( FT_Stream stream) {}
151}
152
153SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
154 : fSkStream(strm), fFontID(fontID) {
155// SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
156
157 bzero(&fFTStream, sizeof(fFTStream));
158 fFTStream.size = fSkStream->getLength();
159 fFTStream.descriptor.pointer = fSkStream;
160 fFTStream.read = sk_stream_read;
161 fFTStream.close = sk_stream_close;
162}
163
reed@android.com62900b42009-02-11 15:07:19 +0000164// Will return 0 on failure
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165static SkFaceRec* ref_ft_face(uint32_t fontID) {
166 SkFaceRec* rec = gFaceRecHead;
167 while (rec) {
168 if (rec->fFontID == fontID) {
169 SkASSERT(rec->fFace);
170 rec->fRefCnt += 1;
171 return rec;
172 }
173 rec = rec->fNext;
174 }
175
176 SkStream* strm = SkFontHost::OpenStream(fontID);
177 if (NULL == strm) {
178 SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 return 0;
180 }
181
182 // this passes ownership of strm to the rec
183 rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
184
185 FT_Open_Args args;
186 memset(&args, 0, sizeof(args));
187 const void* memoryBase = strm->getMemoryBase();
188
189 if (NULL != memoryBase) {
190//printf("mmap(%s)\n", keyString.c_str());
191 args.flags = FT_OPEN_MEMORY;
192 args.memory_base = (const FT_Byte*)memoryBase;
193 args.memory_size = strm->getLength();
194 } else {
195//printf("fopen(%s)\n", keyString.c_str());
196 args.flags = FT_OPEN_STREAM;
197 args.stream = &rec->fFTStream;
198 }
199
200 FT_Error err = FT_Open_Face(gFTLibrary, &args, 0, &rec->fFace);
201
202 if (err) { // bad filename, try the default font
203 fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID);
204 SkDELETE(rec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 return 0;
206 } else {
207 SkASSERT(rec->fFace);
208 //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
209 rec->fNext = gFaceRecHead;
210 gFaceRecHead = rec;
211 rec->fRefCnt = 1;
212 return rec;
213 }
214}
215
216static void unref_ft_face(FT_Face face) {
217 SkFaceRec* rec = gFaceRecHead;
218 SkFaceRec* prev = NULL;
219 while (rec) {
220 SkFaceRec* next = rec->fNext;
221 if (rec->fFace == face) {
222 if (--rec->fRefCnt == 0) {
223 if (prev) {
224 prev->fNext = next;
225 } else {
226 gFaceRecHead = next;
227 }
228 FT_Done_Face(face);
229 SkDELETE(rec);
230 }
231 return;
232 }
233 prev = rec;
234 rec = next;
235 }
236 SkASSERT("shouldn't get here, face not in list");
237}
238
239///////////////////////////////////////////////////////////////////////////
240
241SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
reed@android.com62900b42009-02-11 15:07:19 +0000242 : SkScalerContext(desc) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 SkAutoMutexAcquire ac(gFTMutex);
244
245 FT_Error err;
246
247 if (gFTCount == 0) {
248 err = FT_Init_FreeType(&gFTLibrary);
249// SkDEBUGF(("FT_Init_FreeType returned %d\n", err));
250 SkASSERT(err == 0);
251 }
252 ++gFTCount;
253
254 // load the font file
reed@android.com62900b42009-02-11 15:07:19 +0000255 fFTSize = NULL;
256 fFace = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 fFaceRec = ref_ft_face(fRec.fFontID);
reed@android.com62900b42009-02-11 15:07:19 +0000258 if (NULL == fFaceRec) {
259 return;
260 }
261 fFace = fFaceRec->fFace;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262
263 // compute our factors from the record
264
265 SkMatrix m;
266
267 fRec.getSingleMatrix(&m);
268
269#ifdef DUMP_STRIKE_CREATION
270 SkString keyString;
271 SkFontHost::GetDescriptorKeyString(desc, &keyString);
272 printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
273 SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
274 SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
275 SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
276 fRec.fHints, fRec.fMaskFormat, keyString.c_str());
277#endif
278
279 // now compute our scale factors
280 SkScalar sx = m.getScaleX();
281 SkScalar sy = m.getScaleY();
282
283 if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
284 // sort of give up on hinting
285 sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
286 sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
287 sx = sy = SkScalarAve(sx, sy);
288
289 SkScalar inv = SkScalarInvert(sx);
290
291 // flip the skew elements to go from our Y-down system to FreeType's
292 fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
293 fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
294 fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
295 fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
296 } else {
297 fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
298 fMatrix22.xy = fMatrix22.yx = 0;
299 }
300
301 fScaleX = SkScalarToFixed(sx);
302 fScaleY = SkScalarToFixed(sy);
303
304 // compute the flags we send to Load_Glyph
305 {
306 uint32_t flags = FT_LOAD_DEFAULT;
307 uint32_t render_flags = FT_LOAD_TARGET_NORMAL;
308
309 // we force autohinting at the moment
310
311 switch (fRec.fHints) {
312 case kNo_Hints:
313 flags |= FT_LOAD_NO_HINTING;
314 break;
315 case kSubpixel_Hints:
316 flags |= FT_LOAD_FORCE_AUTOHINT;
317 render_flags = FT_LOAD_TARGET_LIGHT;
318 break;
319 case kNormal_Hints:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320#ifdef ANDROID
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000321 // The following line disables the font's hinting tables. For
322 // Chromium we want font hinting on so that we can generate
323 // baselines that look at little like Firefox. It's expected that
324 // Mike Reed will rework this code sometime soon so we don't wish
325 // to make more extensive changes.
326 flags |= FT_LOAD_FORCE_AUTOHINT;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 /* Switch to light hinting (vertical only) to address some chars
328 that behaved poorly with NORMAL. In the future we could consider
329 making this choice exposed at runtime to the caller.
330 */
331 render_flags = FT_LOAD_TARGET_LIGHT;
332#endif
333 break;
334 }
335
336 if (SkMask::kBW_Format == fRec.fMaskFormat)
337 render_flags = FT_LOAD_TARGET_MONO;
338 else if (SkMask::kLCD_Format == fRec.fMaskFormat)
339 render_flags = FT_LOAD_TARGET_LCD;
340
341 fLoadGlyphFlags = flags | render_flags;
342 }
343
344 // now create the FT_Size
345
346 {
347 FT_Error err;
348
349 err = FT_New_Size(fFace, &fFTSize);
350 if (err != 0) {
351 SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
352 fFaceRec->fFontID, fScaleX, fScaleY, err));
353 fFace = NULL;
354 return;
355 }
356
357 err = FT_Activate_Size(fFTSize);
358 if (err != 0) {
359 SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
360 fFaceRec->fFontID, fScaleX, fScaleY, err));
361 fFTSize = NULL;
362 }
363
364 err = FT_Set_Char_Size( fFace,
365 SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
366 72, 72);
367 if (err != 0) {
368 SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
369 fFaceRec->fFontID, fScaleX, fScaleY, err));
370 fFace = NULL;
371 return;
372 }
373
374 FT_Set_Transform( fFace, &fMatrix22, NULL);
375 }
376}
377
378SkScalerContext_FreeType::~SkScalerContext_FreeType() {
379 if (fFTSize != NULL) {
380 FT_Done_Size(fFTSize);
381 }
382
383 SkAutoMutexAcquire ac(gFTMutex);
384
385 if (fFace != NULL) {
386 unref_ft_face(fFace);
387 }
388 if (--gFTCount == 0) {
389// SkDEBUGF(("FT_Done_FreeType\n"));
390 FT_Done_FreeType(gFTLibrary);
391 SkDEBUGCODE(gFTLibrary = NULL;)
392 }
393}
394
395/* We call this before each use of the fFace, since we may be sharing
396 this face with other context (at different sizes).
397*/
398FT_Error SkScalerContext_FreeType::setupSize() {
399 /* In the off-chance that a font has been removed, we want to error out
400 right away, so call resolve just to be sure.
401
402 TODO: perhaps we can skip this, by walking the global font cache and
403 killing all of the contexts when we know that a given fontID is going
404 away...
405 */
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000406 if (!SkFontHost::ValidFontID(fRec.fFontID)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 return (FT_Error)-1;
408 }
409
410 FT_Error err = FT_Activate_Size(fFTSize);
411
412 if (err != 0) {
413 SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
414 fFaceRec->fFontID, fScaleX, fScaleY, err));
415 fFTSize = NULL;
416 } else {
417 // seems we need to reset this every time (not sure why, but without it
418 // I get random italics from some other fFTSize)
419 FT_Set_Transform( fFace, &fMatrix22, NULL);
420 }
421 return err;
422}
423
424unsigned SkScalerContext_FreeType::generateGlyphCount() const {
425 return fFace->num_glyphs;
426}
427
428uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
429 return SkToU16(FT_Get_Char_Index( fFace, uni ));
430}
431
432static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
433 switch (format) {
434 case SkMask::kBW_Format:
435 return FT_PIXEL_MODE_MONO;
436 case SkMask::kLCD_Format:
437 return FT_PIXEL_MODE_LCD;
438 case SkMask::kA8_Format:
439 default:
440 return FT_PIXEL_MODE_GRAY;
441 }
442}
443
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
445#ifdef FT_ADVANCES_H
446 /* unhinted and light hinted text have linearly scaled advances
447 * which are very cheap to compute with some font formats...
448 */
449 {
450 SkAutoMutexAcquire ac(gFTMutex);
451
452 if (this->setupSize()) {
reed@android.com62900b42009-02-11 15:07:19 +0000453 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 return;
455 }
456
457 FT_Error error;
458 FT_Fixed advance;
459
460 error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount),
461 fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
462 &advance );
463 if (0 == error) {
464 glyph->fRsbDelta = 0;
465 glyph->fLsbDelta = 0;
466 glyph->fAdvanceX = advance; // advance *2/3; //DEBUG
467 glyph->fAdvanceY = 0;
468 return;
469 }
470 }
471#endif /* FT_ADVANCES_H */
472 /* otherwise, we need to load/hint the glyph, which is slower */
473 this->generateMetrics(glyph);
474 return;
475}
476
477void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
478 SkAutoMutexAcquire ac(gFTMutex);
479
480 glyph->fRsbDelta = 0;
481 glyph->fLsbDelta = 0;
482
483 FT_Error err;
484
485 if (this->setupSize()) {
486 goto ERROR;
487 }
488
489 err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
490 if (err != 0) {
491 SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
492 fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
493 ERROR:
reed@android.com62900b42009-02-11 15:07:19 +0000494 glyph->zeroMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 return;
496 }
497
498 switch ( fFace->glyph->format ) {
499 case FT_GLYPH_FORMAT_OUTLINE:
500 FT_BBox bbox;
501
502 FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
503
504 if (kSubpixel_Hints == fRec.fHints) {
505 int dx = glyph->getSubXFixed() >> 10;
506 int dy = glyph->getSubYFixed() >> 10;
507 // negate dy since freetype-y-goes-up and skia-y-goes-down
508 bbox.xMin += dx;
509 bbox.yMin -= dy;
510 bbox.xMax += dx;
511 bbox.yMax -= dy;
512 }
513
514 bbox.xMin &= ~63;
515 bbox.yMin &= ~63;
516 bbox.xMax = (bbox.xMax + 63) & ~63;
517 bbox.yMax = (bbox.yMax + 63) & ~63;
518
519 glyph->fWidth = SkToU16((bbox.xMax - bbox.xMin) >> 6);
520 glyph->fHeight = SkToU16((bbox.yMax - bbox.yMin) >> 6);
521 glyph->fTop = -SkToS16(bbox.yMax >> 6);
522 glyph->fLeft = SkToS16(bbox.xMin >> 6);
523 break;
524
525 case FT_GLYPH_FORMAT_BITMAP:
526 glyph->fWidth = SkToU16(fFace->glyph->bitmap.width);
527 glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows);
528 glyph->fTop = -SkToS16(fFace->glyph->bitmap_top);
529 glyph->fLeft = SkToS16(fFace->glyph->bitmap_left);
530 break;
531
532 default:
533 SkASSERT(!"unknown glyph format");
534 goto ERROR;
535 }
536
537 if (kNormal_Hints == fRec.fHints) {
538 glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
539 glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
540 if (fRec.fFlags & kDevKernText_Flag) {
541 glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
542 glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
543 }
544 } else {
545 glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
546 glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
547 }
548
549#ifdef ENABLE_GLYPH_SPEW
550 SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
551 SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth));
552#endif
553}
554
555void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
556 SkAutoMutexAcquire ac(gFTMutex);
557
558 FT_Error err;
559
560 if (this->setupSize()) {
561 goto ERROR;
562 }
563
564 err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);
565 if (err != 0) {
566 SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
567 glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
568 ERROR:
569 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
570 return;
571 }
572
573 switch ( fFace->glyph->format ) {
574 case FT_GLYPH_FORMAT_OUTLINE: {
575 FT_Outline* outline = &fFace->glyph->outline;
576 FT_BBox bbox;
577 FT_Bitmap target;
578
579 int dx = 0, dy = 0;
580 if (kSubpixel_Hints == fRec.fHints) {
581 dx = glyph.getSubXFixed() >> 10;
582 dy = glyph.getSubYFixed() >> 10;
583 // negate dy since freetype-y-goes-up and skia-y-goes-down
584 dy = -dy;
585 }
586 FT_Outline_Get_CBox(outline, &bbox);
587 /*
588 what we really want to do for subpixel is
589 offset(dx, dy)
590 compute_bounds
591 offset(bbox & !63)
592 but that is two calls to offset, so we do the following, which
593 achieves the same thing with only one offset call.
594 */
595 FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
596 dy - ((bbox.yMin + dy) & ~63));
597
598 target.width = glyph.fWidth;
599 target.rows = glyph.fHeight;
600 target.pitch = glyph.rowBytes();
601 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
602 target.pixel_mode = compute_pixel_mode(
603 (SkMask::Format)fRec.fMaskFormat);
604 target.num_grays = 256;
605
606 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
607 FT_Outline_Get_Bitmap(gFTLibrary, outline, &target);
608 } break;
609
610 case FT_GLYPH_FORMAT_BITMAP: {
611 SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width);
612 SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows);
613 SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top);
614 SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left);
615
616 const uint8_t* src = (const uint8_t*)fFace->glyph->bitmap.buffer;
617 uint8_t* dst = (uint8_t*)glyph.fImage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000619 if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
620 unsigned srcRowBytes = fFace->glyph->bitmap.pitch;
621 unsigned dstRowBytes = glyph.rowBytes();
622 unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
623 unsigned extraRowBytes = dstRowBytes - minRowBytes;
624
625 for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) {
626 memcpy(dst, src, minRowBytes);
627 memset(dst + minRowBytes, 0, extraRowBytes);
628 src += srcRowBytes;
629 dst += dstRowBytes;
630 }
631 } else if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
632 for (int y = 0; y < fFace->glyph->bitmap.rows; ++y) {
633 uint8_t byte = 0;
634 int bits = 0;
635 const uint8_t* src_row = src;
636 uint8_t* dst_row = dst;
637
638 for (int x = 0; x < fFace->glyph->bitmap.width; ++x) {
639 if (!bits) {
640 byte = *src_row++;
641 bits = 8;
642 }
643
644 *dst_row++ = byte & 0x80 ? 0xff : 0;
645 bits--;
646 byte <<= 1;
647 }
648
649 src += fFace->glyph->bitmap.pitch;
650 dst += glyph.rowBytes();
651 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 }
653 } break;
654
655 default:
656 SkASSERT(!"unknown glyph format");
657 goto ERROR;
658 }
659}
660
661///////////////////////////////////////////////////////////////////////////////
662
663#define ft2sk(x) SkFixedToScalar((x) << 10)
664
reed@android.com6f252972009-01-14 16:46:16 +0000665#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 #define CONST_PARAM const
667#else // older freetype doesn't use const here
668 #define CONST_PARAM
669#endif
670
671static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
672 SkPath* path = (SkPath*)ctx;
673 path->close(); // to close the previous contour (if any)
674 path->moveTo(ft2sk(pt->x), -ft2sk(pt->y));
675 return 0;
676}
677
678static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
679 SkPath* path = (SkPath*)ctx;
680 path->lineTo(ft2sk(pt->x), -ft2sk(pt->y));
681 return 0;
682}
683
684static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
685 void* ctx) {
686 SkPath* path = (SkPath*)ctx;
687 path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y));
688 return 0;
689}
690
691static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
692 CONST_PARAM FT_Vector* pt2, void* ctx) {
693 SkPath* path = (SkPath*)ctx;
694 path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x),
695 -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y));
696 return 0;
697}
698
699void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
700 SkPath* path) {
701 SkAutoMutexAcquire ac(gFTMutex);
702
703 SkASSERT(&glyph && path);
704
705 if (this->setupSize()) {
706 path->reset();
707 return;
708 }
709
710 uint32_t flags = fLoadGlyphFlags;
711 flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
712 flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
713
714 FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);
715
716 if (err != 0) {
717 SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
718 glyph.getGlyphID(fBaseGlyphCount), flags, err));
719 path->reset();
720 return;
721 }
722
723 FT_Outline_Funcs funcs;
724
725 funcs.move_to = move_proc;
726 funcs.line_to = line_proc;
727 funcs.conic_to = quad_proc;
728 funcs.cubic_to = cubic_proc;
729 funcs.shift = 0;
730 funcs.delta = 0;
731
732 err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path);
733
734 if (err != 0) {
735 SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
736 glyph.getGlyphID(fBaseGlyphCount), flags, err));
737 path->reset();
738 return;
739 }
740
741 path->close();
742}
743
744void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
745 SkPaint::FontMetrics* my) {
746 if (NULL == mx && NULL == my) {
747 return;
748 }
749
750 SkAutoMutexAcquire ac(gFTMutex);
751
752 if (this->setupSize()) {
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000753 ERROR:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 if (mx) {
755 bzero(mx, sizeof(SkPaint::FontMetrics));
756 }
757 if (my) {
758 bzero(my, sizeof(SkPaint::FontMetrics));
759 }
760 return;
761 }
762
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000763 FT_Face face = fFace;
deanm@chromium.org5c44d322009-06-19 08:57:16 +0000764 if (!face) {
765 goto ERROR;
766 }
767
reed@android.coma8a8b8b2009-05-04 15:00:11 +0000768 int upem = face->units_per_EM;
769 if (upem <= 0) {
770 goto ERROR;
771 }
772
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000773 SkPoint pts[6];
774 SkFixed ys[6];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 SkFixed scaleY = fScaleY;
776 SkFixed mxy = fMatrix22.xy;
777 SkFixed myy = fMatrix22.yy;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000778 SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem;
779 SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000781 int leading = face->height - (face->ascender + -face->descender);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 if (leading < 0) {
783 leading = 0;
784 }
785
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000786 // Try to get the OS/2 table from the font. This contains the specific
787 // average font width metrics which Windows uses.
788 TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 ys[0] = -face->bbox.yMax;
791 ys[1] = -face->ascender;
792 ys[2] = -face->descender;
793 ys[3] = -face->bbox.yMin;
794 ys[4] = leading;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000795 ys[5] = os2 ? os2->xAvgCharWidth : 0;
796
797 SkScalar x_height;
798 if (os2 && os2->sxHeight) {
799 x_height = SkFixedToScalar(SkMulDiv(fScaleX, os2->sxHeight, upem));
800 } else {
801 const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
802 if (x_glyph) {
803 FT_BBox bbox;
804 FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
805 FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
806 x_height = SkIntToScalar(bbox.yMax) / 64;
807 } else {
808 x_height = 0;
809 }
810 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811
812 // convert upem-y values into scalar points
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000813 for (int i = 0; i < 6; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 SkFixed y = SkMulDiv(scaleY, ys[i], upem);
815 SkFixed x = SkFixedMul(mxy, y);
816 y = SkFixedMul(myy, y);
817 pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y));
818 }
819
820 if (mx) {
821 mx->fTop = pts[0].fX;
822 mx->fAscent = pts[1].fX;
823 mx->fDescent = pts[2].fX;
824 mx->fBottom = pts[3].fX;
825 mx->fLeading = pts[4].fX;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000826 mx->fAvgCharWidth = pts[5].fX;
827 mx->fXMin = xmin;
828 mx->fXMax = xmax;
829 mx->fXHeight = x_height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 }
831 if (my) {
832 my->fTop = pts[0].fY;
833 my->fAscent = pts[1].fY;
834 my->fDescent = pts[2].fY;
835 my->fBottom = pts[3].fY;
836 my->fLeading = pts[4].fY;
agl@chromium.orgcc3096b2009-04-22 22:09:04 +0000837 my->fAvgCharWidth = pts[5].fY;
838 my->fXMin = xmin;
839 my->fXMax = xmax;
840 my->fXHeight = x_height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 }
842}
843
844////////////////////////////////////////////////////////////////////////
845////////////////////////////////////////////////////////////////////////
846
847SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
reed@android.com62900b42009-02-11 15:07:19 +0000848 SkScalerContext_FreeType* c = SkNEW_ARGS(SkScalerContext_FreeType, (desc));
849 if (!c->success()) {
850 SkDELETE(c);
851 c = NULL;
852 }
853 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854}
855
856///////////////////////////////////////////////////////////////////////////////
857
858/* Export this so that other parts of our FonttHost port can make use of our
859 ability to extract the name+style from a stream, using FreeType's api.
860*/
861SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
862 FT_Library library;
863 if (FT_Init_FreeType(&library)) {
864 name->set(NULL);
865 return SkTypeface::kNormal;
866 }
867
868 FT_Open_Args args;
869 memset(&args, 0, sizeof(args));
870
871 const void* memoryBase = stream->getMemoryBase();
872 FT_StreamRec streamRec;
873
874 if (NULL != memoryBase) {
875 args.flags = FT_OPEN_MEMORY;
876 args.memory_base = (const FT_Byte*)memoryBase;
877 args.memory_size = stream->getLength();
878 } else {
879 memset(&streamRec, 0, sizeof(streamRec));
880 streamRec.size = stream->read(NULL, 0);
881 streamRec.descriptor.pointer = stream;
882 streamRec.read = sk_stream_read;
883 streamRec.close = sk_stream_close;
884
885 args.flags = FT_OPEN_STREAM;
886 args.stream = &streamRec;
887 }
888
889 FT_Face face;
890 if (FT_Open_Face(library, &args, 0, &face)) {
891 FT_Done_FreeType(library);
892 name->set(NULL);
893 return SkTypeface::kNormal;
894 }
895
896 name->set(face->family_name);
897 int style = SkTypeface::kNormal;
898
899 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
900 style |= SkTypeface::kBold;
901 }
902 if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
903 style |= SkTypeface::kItalic;
904 }
905
906 FT_Done_Face(face);
907 FT_Done_FreeType(library);
908 return (SkTypeface::Style)style;
909}
910