xref: /haiku/src/servers/app/font/FontEngine.cpp (revision 0d452c8f34013b611a54c746a71c05e28796eae2)
1 /*
2  * Copyright 2007, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Maxim Shemanarev <mcseemagg@yahoo.com>
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Anthony Lee <don.anthony.lee@gmail.com>
9  *		Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
10  */
11 
12 //----------------------------------------------------------------------------
13 // Anti-Grain Geometry - Version 2.4
14 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
15 //
16 // Permission to copy, use, modify, sell and distribute this software
17 // is granted provided this copyright notice appears in all copies.
18 // This software is provided "as is" without express or implied
19 // warranty, and with no claim as to its suitability for any purpose.
20 //
21 //----------------------------------------------------------------------------
22 // Contact: mcseem@antigrain.com
23 //			mcseemagg@yahoo.com
24 //			http://www.antigrain.com
25 //----------------------------------------------------------------------------
26 
27 
28 #include "FontEngine.h"
29 
30 #include FT_GLYPH_H
31 #include FT_OUTLINE_H
32 #include FT_LCD_FILTER_H
33 
34 #include <stdio.h>
35 
36 #include <agg_bitset_iterator.h>
37 #include <agg_renderer_scanline.h>
38 
39 #include "GlobalSubpixelSettings.h"
40 
41 
42 static const bool kFlipY = true;
43 
44 
45 static inline double
46 int26p6_to_dbl(int p)
47 {
48 	return double(p) / 64.0;
49 }
50 
51 
52 static inline int
53 dbl_to_int26p6(double p)
54 {
55 	return int(p * 64.0 + 0.5);
56 }
57 
58 
59 template<class PathStorage>
60 bool
61 decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path)
62 {
63 	typedef typename PathStorage::value_type value_type;
64 
65 	FT_Vector v_last;
66 	FT_Vector v_control;
67 	FT_Vector v_start;
68 	double x1, y1, x2, y2, x3, y3;
69 
70 	FT_Vector* point;
71 	FT_Vector* limit;
72 	char* tags;
73 
74 	int   n;		 // index of contour in outline
75 	int   first;	 // index of first point in contour
76 	char  tag;	   // current point's state
77 
78 	first = 0;
79 
80 	for (n = 0; n < outline.n_contours; n++) {
81 		int  last;  // index of last point in contour
82 
83 		last  = outline.contours[n];
84 		limit = outline.points + last;
85 
86 		v_start = outline.points[first];
87 		v_last  = outline.points[last];
88 
89 		v_control = v_start;
90 
91 		point = outline.points + first;
92 		tags  = outline.tags  + first;
93 		tag   = FT_CURVE_TAG(tags[0]);
94 
95 		// A contour cannot start with a cubic control point!
96 		if (tag == FT_CURVE_TAG_CUBIC)
97 			return false;
98 
99 		// check first point to determine origin
100 		if ( tag == FT_CURVE_TAG_CONIC) {
101 			// first point is conic control.  Yes, this happens.
102 			if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
103 				// start at last point if it is on the curve
104 				v_start = v_last;
105 				limit--;
106 			} else {
107 				// if both first and last points are conic,
108 				// start at their middle and record its position
109 				// for closure
110 				v_start.x = (v_start.x + v_last.x) / 2;
111 				v_start.y = (v_start.y + v_last.y) / 2;
112 
113 				v_last = v_start;
114 			}
115 			point--;
116 			tags--;
117 		}
118 
119 		x1 = int26p6_to_dbl(v_start.x);
120 		y1 = int26p6_to_dbl(v_start.y);
121 		if (flip_y) y1 = -y1;
122 		path.move_to(value_type(dbl_to_int26p6(x1)),
123 					 value_type(dbl_to_int26p6(y1)));
124 
125 		while(point < limit) {
126 			point++;
127 			tags++;
128 
129 			tag = FT_CURVE_TAG(tags[0]);
130 			switch(tag) {
131 				case FT_CURVE_TAG_ON: { // emit a single line_to
132 					x1 = int26p6_to_dbl(point->x);
133 					y1 = int26p6_to_dbl(point->y);
134 					if (flip_y) y1 = -y1;
135 					path.line_to(value_type(dbl_to_int26p6(x1)),
136 								 value_type(dbl_to_int26p6(y1)));
137 					//path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y));
138 					continue;
139 				}
140 
141 				case FT_CURVE_TAG_CONIC: { // consume conic arcs
142 					v_control.x = point->x;
143 					v_control.y = point->y;
144 
145 				Do_Conic:
146 					if (point < limit) {
147 						FT_Vector vec;
148 						FT_Vector v_middle;
149 
150 						point++;
151 						tags++;
152 						tag = FT_CURVE_TAG(tags[0]);
153 
154 						vec.x = point->x;
155 						vec.y = point->y;
156 
157 						if (tag == FT_CURVE_TAG_ON) {
158 							x1 = int26p6_to_dbl(v_control.x);
159 							y1 = int26p6_to_dbl(v_control.y);
160 							x2 = int26p6_to_dbl(vec.x);
161 							y2 = int26p6_to_dbl(vec.y);
162 							if (flip_y) { y1 = -y1; y2 = -y2; }
163 							path.curve3(value_type(dbl_to_int26p6(x1)),
164 										value_type(dbl_to_int26p6(y1)),
165 										value_type(dbl_to_int26p6(x2)),
166 										value_type(dbl_to_int26p6(y2)));
167 							continue;
168 						}
169 
170 						if (tag != FT_CURVE_TAG_CONIC)
171 							return false;
172 
173 						v_middle.x = (v_control.x + vec.x) / 2;
174 						v_middle.y = (v_control.y + vec.y) / 2;
175 
176 						x1 = int26p6_to_dbl(v_control.x);
177 						y1 = int26p6_to_dbl(v_control.y);
178 						x2 = int26p6_to_dbl(v_middle.x);
179 						y2 = int26p6_to_dbl(v_middle.y);
180 						if (flip_y) { y1 = -y1; y2 = -y2; }
181 						path.curve3(value_type(dbl_to_int26p6(x1)),
182 									value_type(dbl_to_int26p6(y1)),
183 									value_type(dbl_to_int26p6(x2)),
184 									value_type(dbl_to_int26p6(y2)));
185 
186 						//path.curve3(conv(v_control.x),
187 						//			flip_y ? -conv(v_control.y) : conv(v_control.y),
188 						//			conv(v_middle.x),
189 						//			flip_y ? -conv(v_middle.y) : conv(v_middle.y));
190 
191 						v_control = vec;
192 						goto Do_Conic;
193 					}
194 
195 					x1 = int26p6_to_dbl(v_control.x);
196 					y1 = int26p6_to_dbl(v_control.y);
197 					x2 = int26p6_to_dbl(v_start.x);
198 					y2 = int26p6_to_dbl(v_start.y);
199 					if (flip_y) { y1 = -y1; y2 = -y2; }
200 					path.curve3(value_type(dbl_to_int26p6(x1)),
201 								value_type(dbl_to_int26p6(y1)),
202 								value_type(dbl_to_int26p6(x2)),
203 								value_type(dbl_to_int26p6(y2)));
204 
205 					//path.curve3(conv(v_control.x),
206 					//			flip_y ? -conv(v_control.y) : conv(v_control.y),
207 					//			conv(v_start.x),
208 					//			flip_y ? -conv(v_start.y) : conv(v_start.y));
209 					goto Close;
210 				}
211 
212 				default: { // FT_CURVE_TAG_CUBIC
213 					FT_Vector vec1, vec2;
214 
215 					if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
216 						return false;
217 
218 					vec1.x = point[0].x;
219 					vec1.y = point[0].y;
220 					vec2.x = point[1].x;
221 					vec2.y = point[1].y;
222 
223 					point += 2;
224 					tags  += 2;
225 
226 					if (point <= limit) {
227 						FT_Vector vec;
228 
229 						vec.x = point->x;
230 						vec.y = point->y;
231 
232 						x1 = int26p6_to_dbl(vec1.x);
233 						y1 = int26p6_to_dbl(vec1.y);
234 						x2 = int26p6_to_dbl(vec2.x);
235 						y2 = int26p6_to_dbl(vec2.y);
236 						x3 = int26p6_to_dbl(vec.x);
237 						y3 = int26p6_to_dbl(vec.y);
238 						if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
239 						path.curve4(value_type(dbl_to_int26p6(x1)),
240 									value_type(dbl_to_int26p6(y1)),
241 									value_type(dbl_to_int26p6(x2)),
242 									value_type(dbl_to_int26p6(y2)),
243 									value_type(dbl_to_int26p6(x3)),
244 									value_type(dbl_to_int26p6(y3)));
245 
246 						//path.curve4(conv(vec1.x),
247 						//			flip_y ? -conv(vec1.y) : conv(vec1.y),
248 						//			conv(vec2.x),
249 						//			flip_y ? -conv(vec2.y) : conv(vec2.y),
250 						//			conv(vec.x),
251 						//			flip_y ? -conv(vec.y) : conv(vec.y));
252 						continue;
253 					}
254 
255 					x1 = int26p6_to_dbl(vec1.x);
256 					y1 = int26p6_to_dbl(vec1.y);
257 					x2 = int26p6_to_dbl(vec2.x);
258 					y2 = int26p6_to_dbl(vec2.y);
259 					x3 = int26p6_to_dbl(v_start.x);
260 					y3 = int26p6_to_dbl(v_start.y);
261 					if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
262 					path.curve4(value_type(dbl_to_int26p6(x1)),
263 								value_type(dbl_to_int26p6(y1)),
264 								value_type(dbl_to_int26p6(x2)),
265 								value_type(dbl_to_int26p6(y2)),
266 								value_type(dbl_to_int26p6(x3)),
267 								value_type(dbl_to_int26p6(y3)));
268 
269 					//path.curve4(conv(vec1.x),
270 					//			flip_y ? -conv(vec1.y) : conv(vec1.y),
271 					//			conv(vec2.x),
272 					//			flip_y ? -conv(vec2.y) : conv(vec2.y),
273 					//			conv(v_start.x),
274 					//			flip_y ? -conv(v_start.y) : conv(v_start.y));
275 					goto Close;
276 				}
277 			}
278 		}
279 
280 		path.close_polygon();
281 
282    Close:
283 		first = last + 1;
284 	}
285 
286 	return true;
287 }
288 
289 
290 template<class Scanline, class ScanlineStorage>
291 void
292 decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
293 	bool flip_y, Scanline& sl, ScanlineStorage& storage)
294 {
295 	const uint8* buf = (const uint8*)bitmap.buffer;
296 	int pitch = bitmap.pitch;
297 	sl.reset(x, x + bitmap.width);
298 	storage.prepare();
299 	if (flip_y) {
300 		buf += bitmap.pitch * (bitmap.rows - 1);
301 		y += bitmap.rows;
302 		pitch = -pitch;
303 	}
304 	for (int i = 0; i < bitmap.rows; i++) {
305 		sl.reset_spans();
306 		agg::bitset_iterator bits(buf, 0);
307 		for (int j = 0; j < bitmap.width; j++) {
308 			if (bits.bit())
309 				sl.add_cell(x + j, agg::cover_full);
310 			++bits;
311 		}
312 		buf += pitch;
313 		if (sl.num_spans()) {
314 			sl.finalize(y - i - 1);
315 			storage.render(sl);
316 		}
317 	}
318 }
319 
320 
321 template<class Scanline, class ScanlineStorage>
322 void
323 decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
324 	bool flip_y, Scanline& sl, ScanlineStorage& storage)
325 {
326 	const uint8* buf = (const uint8*)bitmap.buffer;
327 	int pitch = bitmap.pitch;
328 	sl.reset(x, x + bitmap.width);
329 	storage.prepare();
330 	if (flip_y) {
331 		buf += bitmap.pitch * (bitmap.rows - 1);
332 		y += bitmap.rows;
333 		pitch = -pitch;
334 	}
335 	for (int i = 0; i < bitmap.rows; i++) {
336 		sl.reset_spans();
337 
338 		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
339 			// font has built-in mono bitmap
340 			agg::bitset_iterator bits(buf, 0);
341 			for (int j = 0; j < bitmap.width; j++) {
342 				if (bits.bit())
343 					sl.add_cell(x + j, agg::cover_full);
344 				++bits;
345 			}
346 		} else {
347 			const uint8* p = buf;
348 			for (int j = 0; j < bitmap.width; j++) {
349 				if (*p)
350 					sl.add_cell(x + j, *p);
351 				++p;
352 			}
353  		}
354 
355 		buf += pitch;
356 		if (sl.num_spans()) {
357 			sl.finalize(y - i - 1);
358 			storage.render(sl);
359 		}
360 	}
361 }
362 
363 
364 template<class Scanline, class ScanlineStorage>
365 void
366 decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
367 	bool flip_y, Scanline& sl, ScanlineStorage& storage)
368 {
369 #ifdef AVERAGE_BASED_SUBPIXEL_FILTERING
370 	const uint8* buf = (const uint8*)bitmap.buffer;
371 	int pitch = bitmap.pitch;
372 	sl.reset(x, x + bitmap.width / 3);
373 	storage.prepare();
374 
375 	if (flip_y) {
376 		buf += bitmap.pitch * (bitmap.rows - 1);
377 		y += bitmap.rows;
378 		pitch = -pitch;
379 	}
380 
381 	for (int i = 0; i < bitmap.rows; i++) {
382 		sl.reset_spans();
383 
384 		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
385 			// font has built-in mono bitmap
386 			agg::bitset_iterator bits(buf, 0);
387 			for (int j = 0; j < bitmap.width; j++) {
388 				if (bits.bit()) {
389 					sl.add_cell(x + j,
390 						agg::cover_full, agg::cover_full, agg::cover_full);
391 				}
392 				++bits;
393 			}
394 		} else {
395 			const uint8* p = buf;
396 			int w = bitmap.width / 3;
397 
398 			for (int j = 0; j < w; j++) {
399 				if (p[0] || p[1] || p[2])
400 					sl.add_cell(x + j, p[0], p[1], p[2]);
401 				p += 3;
402 			}
403  		}
404 
405 		buf += pitch;
406 		if (sl.num_spans()) {
407 			sl.finalize(y - i - 1);
408 			storage.render(sl);
409 		}
410 	}
411 #else
412 // filter based anti-colored edges method
413 	// Filtering weights
414 	const uint8 filter[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 };
415 
416 	const uint8* buf = (const uint8*)bitmap.buffer;
417 	int pitch = bitmap.pitch;
418 	sl.reset(x - 1, x + bitmap.width / 3 + 1);
419 		// -1 and +1 to account for additional edge pixels needed by filtering
420 	storage.prepare();
421 	if (flip_y) {
422 		buf += bitmap.pitch * (bitmap.rows - 1);
423 		y += bitmap.rows;
424 		pitch = -pitch;
425 	}
426 	for (int i = 0; i < bitmap.rows; i++) {
427 		sl.reset_spans();
428 
429 		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
430 			// font has built-in mono bitmap
431 			agg::bitset_iterator bits(buf, 0);
432 			for (int j = 0; j < bitmap.width; j++) {
433 				if (bits.bit()) {
434 					sl.add_cell(x + j,
435 						agg::cover_full, agg::cover_full, agg::cover_full);
436 				}
437 				++bits;
438 			}
439 		} else {
440 			const uint8* p = buf;
441 			uint32 coverR;
442 			uint32 coverG;
443 			uint32 coverB;
444 			int w = bitmap.width / 3;
445 			// handle the left extra edge pixel
446 			if (w && !(p[0] == p[1] && p[1] == p[2]
447 				&& (w == 1 || (p[3] == p[4] && p[4] == p[5])))) {
448 
449 				coverR = 0;
450 				coverG = (p[0] * filter[0]) >> 8;
451 				coverB = (p[0] * filter[1] + p[1] * filter[0]) >> 8;
452 				coverG = coverG | ( -(coverG >> 8));
453 				coverB = coverB | ( -(coverB >> 8));
454 
455 				if (coverR || coverG || coverB)
456 					sl.add_cell(x - 1, coverR, coverG, coverB);
457 			}
458 			for (int j = 0; j < w; j++) {
459 				if (p[0] == p[1] && p[1] == p[2]
460 					&& (j == 0 || (p[-3] == p[-2] && p[-2] == p[-1]))
461 					&& (j == w-1 || (p[3] == p[4] && p[4] == p[5]))) {
462 
463 					coverR = p[0];
464 					coverG = p[0];
465 					coverB = p[0];
466 
467 				} else if (p[0] == p[1] && p[1] == p[2]
468 					&& (j < w-1 && p[3] == p[4] && p[4] == p[5])
469 					&& (j == w-2 || (p[6] == p[7] && p[7] == p[8]))) {
470 
471 					coverR = ((j > 0 ? p[-2] * filter[4]
472 						+ p[-1] * filter[3] : 0)
473 						+ p[0] * filter[2] + p[1] * filter[1]
474 						+ p[2] * filter[0])
475 						>> 8;
476 					coverG = ((j > 0 ? p[-1] * filter[4] : 0)
477 						+ p[0] * filter[3] + p[1] * filter[2]
478 						+ p[2] * filter[1])
479 						>> 8;
480 					coverB = (p[0] * filter[4]
481 							+ p[1] * filter[3] + p[2] * filter[2]) >> 8;
482 					coverR = coverR | ( -(coverR >> 8));
483 					coverG = coverG | ( -(coverG >> 8));
484 					coverB = coverB | ( -(coverB >> 8));
485 
486 				} else if (p[0] == p[1] && p[1] == p[2]
487 					&& (j > 0 && p[-3] == p[-2] && p[-2] == p[-1])
488 					&& (j == 1 || (p[-6] == p[-5] && p[-5] == p[-4]))) {
489 
490 					coverR = (p[0] * filter[2] + p[1] * filter[1]
491 						+ p[2] * filter[0]) >> 8;
492 					coverG = (p[0] * filter[3] + p[1] * filter[2]
493 						+ p[2] * filter[1]
494 						+ (j < w-1 ? p[3] * filter[0] : 0))
495 						>> 8;
496 					coverB = (p[0] * filter[4] + p[1] * filter[3]
497 						+ p[2] * filter[2]
498 						+ (j < w-1 ? p[3] * filter[1]
499 						+ p[4] * filter[0] : 0))
500 						>> 8;
501 					coverR = coverR | ( -(coverR >> 8));
502 					coverG = coverG | ( -(coverG >> 8));
503 					coverB = coverB | ( -(coverB >> 8));
504 
505 				} else {
506 
507 					coverR = ((j > 0 ? p[-2] * filter[4]
508 						+ p[-1] * filter[3] : 0)
509 						+ p[0] * filter[2] + p[1] * filter[1]
510 						+ p[2] * filter[0])
511 						>> 8;
512 					coverG = ((j > 0 ? p[-1] * filter[4] : 0)
513 						+ p[0] * filter[3] + p[1] * filter[2]
514 						+ p[2] * filter[1]
515 						+ (j < w-1 ? p[3] * filter[0] : 0))
516 						>> 8;
517 					coverB = (p[0] * filter[4] + p[1] * filter[3]
518 						+ p[2] * filter[2]
519 						+ (j < w-1 ? p[3] * filter[1]
520 						+ p[4] * filter[0] : 0))
521 						>> 8;
522 					coverR = coverR | ( -(coverR >> 8));
523 					coverG = coverG | ( -(coverG >> 8));
524 					coverB = coverB | ( -(coverB >> 8));
525 				}
526 
527 				if (coverR || coverG || coverB)
528 					sl.add_cell(x + j, coverR, coverG, coverB);
529 				p += 3;
530 			}
531 			// handle the right extra edge pixel
532 			if (w && !(p[-3] == p[-2] && p[-2] == p[-1]
533 				&& (w == 1 || (p[-6] == p[-5] && p[-5] == p[-4])))) {
534 
535 				coverR = (p[-2] * filter[4] + p[-1] * filter[3]) >> 8;
536 				coverG = (p[-1] * filter[4]) >> 8;
537 				coverB = 0;
538 				coverR = coverR | ( -(coverR >> 8));
539 				coverG = coverG | ( -(coverG >> 8));
540 
541 				if (coverR || coverG || coverB)
542 					sl.add_cell(x + w, coverR, coverG, coverB);
543 			}
544  		}
545 
546 		buf += pitch;
547 		if (sl.num_spans()) {
548 			sl.finalize(y - i - 1);
549 			storage.render(sl);
550 		}
551 	}
552 #endif
553 }
554 
555 
556 // #pragma mark -
557 
558 
559 FontEngine::FontEngine()
560 	:
561 	fLastError(0),
562 	fLibraryInitialized(false),
563 	fLibrary(0),
564 	fFace(NULL),
565 
566 	fGlyphRendering(glyph_ren_native_gray8),
567 	fHinting(true),
568 
569 	fDataSize(0),
570 	fDataType(glyph_data_invalid),
571 	fBounds(1, 1, 0, 0),
572 	fAdvanceX(0.0),
573 	fAdvanceY(0.0),
574 	fInsetLeft(0.0),
575 	fInsetRight(0.0),
576 
577 	fPath(),
578 	fCurves(fPath),
579 	fScanlineAA(),
580 	fScanlineBin(),
581 	fScanlineSubpix(),
582 	fScanlineStorageAA(),
583 	fScanlineStorageBin(),
584 	fScanlineStorageSubpix()
585 {
586 	fCurves.approximation_scale(4.0);
587 
588 	fLastError = FT_Init_FreeType(&fLibrary);
589 	if (fLastError == 0)
590 		fLibraryInitialized = true;
591 }
592 
593 
594 FontEngine::~FontEngine()
595 {
596 	FT_Done_Face(fFace);
597 
598 	if (fLibraryInitialized)
599 		FT_Done_FreeType(fLibrary);
600 }
601 
602 
603 unsigned
604 FontEngine::CountFaces() const
605 {
606 	if (fFace)
607 		return fFace->num_faces;
608 
609 	return 0;
610 }
611 
612 
613 uint32
614 FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode) const
615 {
616 	return FT_Get_Char_Index(fFace, glyphCode);
617 }
618 
619 
620 bool
621 FontEngine::PrepareGlyph(uint32 glyphIndex)
622 {
623 	fLastError = FT_Load_Glyph(fFace, glyphIndex,
624 		(fHinting ? (FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD)
625 			: FT_LOAD_NO_HINTING));
626 
627 	if (fLastError != 0)
628 		return false;
629 
630 	fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
631 	fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
632 	fInsetLeft = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX);
633 	fInsetRight = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX
634 		+ fFace->glyph->metrics.width - fFace->glyph->metrics.horiAdvance);
635 
636 	switch(fGlyphRendering) {
637 		case glyph_ren_native_mono:
638 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO);
639 			if (fLastError == 0) {
640 				decompose_ft_bitmap_mono(fFace->glyph->bitmap,
641 					fFace->glyph->bitmap_left, kFlipY ?
642 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
643 					kFlipY, fScanlineBin, fScanlineStorageBin);
644 				fBounds.x1 = fScanlineStorageBin.min_x();
645 				fBounds.y1 = fScanlineStorageBin.min_y();
646 				fBounds.x2 = fScanlineStorageBin.max_x();
647 				fBounds.y2 = fScanlineStorageBin.max_y();
648 				fDataSize = fScanlineStorageBin.byte_size();
649 				fDataType = glyph_data_mono;
650 				return true;
651 			}
652 			break;
653 
654 
655 		case glyph_ren_native_gray8:
656 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_NORMAL);
657 			if (fLastError == 0) {
658 				decompose_ft_bitmap_gray8(fFace->glyph->bitmap,
659 					fFace->glyph->bitmap_left, kFlipY ?
660 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
661 					kFlipY, fScanlineAA, fScanlineStorageAA);
662 				fBounds.x1 = fScanlineStorageAA.min_x();
663 				fBounds.y1 = fScanlineStorageAA.min_y();
664 				fBounds.x2 = fScanlineStorageAA.max_x();
665 				fBounds.y2 = fScanlineStorageAA.max_y();
666 				fDataSize = fScanlineStorageAA.byte_size();
667 				fDataType = glyph_data_gray8;
668 				return true;
669 			}
670 			break;
671 
672 
673 		case glyph_ren_subpix:
674 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
675 			if (fLastError == 0) {
676 				decompose_ft_bitmap_subpix(fFace->glyph->bitmap,
677 					fFace->glyph->bitmap_left, kFlipY ?
678 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
679 					kFlipY, fScanlineSubpix, fScanlineStorageSubpix);
680 				fBounds.x1 = fScanlineStorageSubpix.min_x();
681 				fBounds.y1 = fScanlineStorageSubpix.min_y();
682 				fBounds.x2 = fScanlineStorageSubpix.max_x();
683 				fBounds.y2 = fScanlineStorageSubpix.max_y();
684 				fDataSize = fScanlineStorageSubpix.byte_size();
685 				fDataType = glyph_data_subpix;
686 				return true;
687 			}
688 			break;
689 
690 
691 		case glyph_ren_outline:
692 			fPath.remove_all();
693 			if (decompose_ft_outline(fFace->glyph->outline, kFlipY, fPath)) {
694 				agg::rect_d bounds = fPath.bounding_rect();
695 				fBounds.x1 = int(floor(bounds.x1));
696 				fBounds.y1 = int(floor(bounds.y1));
697 				fBounds.x2 = int(ceil(bounds.x2));
698 				fBounds.y2 = int(ceil(bounds.y2));
699 				fDataSize = fPath.byte_size();
700 				fDataType = glyph_data_outline;
701 				return true;
702 			}
703 			break;
704 	}
705 	return false;
706 }
707 
708 // #pragma mark -
709 
710 // WriteGlyphTo
711 void
712 FontEngine::WriteGlyphTo(uint8* data) const
713 {
714 	if (data && fDataSize) {
715 		switch(fDataType) {
716 			case glyph_data_mono:
717 				fScanlineStorageBin.serialize(data);
718 				break;
719 
720 			case glyph_data_gray8:
721 				fScanlineStorageAA.serialize(data);
722 				break;
723 
724 			case glyph_data_subpix:
725 				fScanlineStorageSubpix.serialize(data);
726 				break;
727 
728 			case glyph_data_outline:
729 				fPath.serialize(data);
730 				break;
731 
732 			case glyph_data_invalid:
733 			default:
734 				break;
735 		}
736 	}
737 }
738 
739 
740 // GetKerning
741 bool
742 FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
743 {
744 	if (fFace && first && second && FT_HAS_KERNING(fFace)) {
745 		FT_Vector delta;
746 		FT_Get_Kerning(fFace, first, second, FT_KERNING_DEFAULT, &delta);
747 
748 		double dx = int26p6_to_dbl(delta.x);
749 		double dy = int26p6_to_dbl(delta.y);
750 
751 		*x += dx;
752 		*y += dy;
753 
754 		return true;
755 	}
756 	return false;
757 }
758 
759 
760 // #pragma mark -
761 
762 
763 bool
764 FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size,
765 	FT_Encoding charMap, glyph_rendering ren_type, bool hinting,
766 	const char* fontFileBuffer, const long fontFileBufferSize)
767 {
768 	if (!fLibraryInitialized)
769 		return false;
770 
771 	fHinting = hinting;
772 
773 	fLastError = 0;
774 
775 	FT_Done_Face(fFace);
776 	if (fontFileBuffer && fontFileBufferSize) {
777 		fLastError = FT_New_Memory_Face(fLibrary,
778 			(const FT_Byte*)fontFileBuffer, fontFileBufferSize,
779 			faceIndex, &fFace);
780 	} else {
781 		fLastError = FT_New_Face(fLibrary, fontFilePath, faceIndex, &fFace);
782 	}
783 
784 	if (fLastError != 0)
785 		return false;
786 
787 	switch(ren_type) {
788 		case glyph_ren_native_mono:
789 			fGlyphRendering = glyph_ren_native_mono;
790 			break;
791 
792 		case glyph_ren_native_gray8:
793 			fGlyphRendering = glyph_ren_native_gray8;
794 			break;
795 
796 		case glyph_ren_subpix:
797 			fGlyphRendering = glyph_ren_subpix;
798 			break;
799 
800 		case glyph_ren_outline:
801 			if (FT_IS_SCALABLE(fFace))
802 				fGlyphRendering = glyph_ren_outline;
803 			else
804 				fGlyphRendering = glyph_ren_native_gray8;
805 			break;
806 	}
807 
808 	FT_Set_Pixel_Sizes(fFace,
809 		unsigned(size * 64.0) >> 6,		// pixel_width
810 		unsigned(size * 64.0) >> 6);	// pixel_height
811 
812 	if (charMap != FT_ENCODING_NONE) {
813 		fLastError = FT_Select_Charmap(fFace, charMap);
814 	} else {
815 		if (FT_Select_Charmap(fFace, FT_ENCODING_UNICODE) != 0)
816 			fLastError = FT_Select_Charmap(fFace, FT_ENCODING_NONE);
817 	}
818 
819 	return fLastError == 0;
820 }
821 
822