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