xref: /haiku/src/servers/app/font/FontEngine.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 (unsigned int i = 0; i < bitmap.rows; i++) {
305 		sl.reset_spans();
306 		agg::bitset_iterator bits(buf, 0);
307 		for (unsigned 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 (unsigned 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 (unsigned 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 (unsigned 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 	const uint8* buf = (const uint8*)bitmap.buffer;
370 	int pitch = bitmap.pitch;
371 	if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
372 		sl.reset(x, x + bitmap.width);
373 	else
374 		sl.reset(x, x + bitmap.width / 3);
375 	storage.prepare();
376 
377 	if (flip_y) {
378 		buf += bitmap.pitch * (bitmap.rows - 1);
379 		y += bitmap.rows;
380 		pitch = -pitch;
381 	}
382 
383 	for (unsigned int i = 0; i < bitmap.rows; i++) {
384 		sl.reset_spans();
385 
386 		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
387 			// font has built-in mono bitmap
388 			agg::bitset_iterator bits(buf, 0);
389 			for (unsigned int j = 0; j < bitmap.width; j++) {
390 				if (bits.bit()) {
391 					sl.add_cell(x + j,
392 						agg::cover_full, agg::cover_full, agg::cover_full);
393 				}
394 				++bits;
395 			}
396 		} else {
397 			const uint8* p = buf;
398 			int w = bitmap.width / 3;
399 
400 			for (int j = 0; j < w; j++) {
401 				if (p[0] || p[1] || p[2])
402 					sl.add_cell(x + j, p[0], p[1], p[2]);
403 				p += 3;
404 			}
405 		}
406 
407 		buf += pitch;
408 		if (sl.num_spans()) {
409 			sl.finalize(y - i - 1);
410 			storage.render(sl);
411 		}
412 	}
413 }
414 
415 
416 // #pragma mark -
417 
418 
419 FontEngine::FontEngine()
420 	:
421 	fLastError(0),
422 	fLibraryInitialized(false),
423 	fLibrary(0),
424 	fFace(NULL),
425 
426 	fGlyphRendering(glyph_ren_native_gray8),
427 	fHinting(true),
428 
429 	fDataSize(0),
430 	fDataType(glyph_data_invalid),
431 	fBounds(1, 1, 0, 0),
432 	fAdvanceX(0.0),
433 	fAdvanceY(0.0),
434 	fInsetLeft(0.0),
435 	fInsetRight(0.0),
436 
437 	fPath(),
438 	fCurves(fPath),
439 	fScanlineAA(),
440 	fScanlineBin(),
441 	fScanlineSubpix(),
442 	fScanlineStorageAA(),
443 	fScanlineStorageBin(),
444 	fScanlineStorageSubpix()
445 {
446 	fCurves.approximation_scale(4.0);
447 
448 	fLastError = FT_Init_FreeType(&fLibrary);
449 	if (fLastError == 0)
450 		fLibraryInitialized = true;
451 }
452 
453 
454 FontEngine::~FontEngine()
455 {
456 	FT_Done_Face(fFace);
457 
458 	if (fLibraryInitialized)
459 		FT_Done_FreeType(fLibrary);
460 }
461 
462 
463 unsigned
464 FontEngine::CountFaces() const
465 {
466 	if (fFace)
467 		return fFace->num_faces;
468 
469 	return 0;
470 }
471 
472 
473 uint32
474 FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode) const
475 {
476 	return FT_Get_Char_Index(fFace, glyphCode);
477 }
478 
479 
480 bool
481 FontEngine::PrepareGlyph(uint32 glyphIndex)
482 {
483 	FT_Int32 loadFlags = fHinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING;
484 	loadFlags |= fGlyphRendering == glyph_ren_subpix ?
485 		FT_LOAD_TARGET_LCD : FT_LOAD_TARGET_NORMAL;
486 
487 	// Load unscaled and without hinting to get precise advance values
488 	// for B_CHAR_SPACING
489 	fLastError = FT_Load_Glyph(fFace, glyphIndex, loadFlags
490 		| FT_LOAD_NO_HINTING | FT_LOAD_NO_SCALE);
491 
492 	FT_UShort units_per_EM = fFace->units_per_EM;
493 	if (!FT_IS_SCALABLE(fFace))
494 		units_per_EM = 1;
495 	fPreciseAdvanceX = (double)fFace->glyph->advance.x / units_per_EM;
496 	fPreciseAdvanceY = (double)fFace->glyph->advance.y / units_per_EM;
497 
498 	// Need to load again with hinting.
499 	fLastError = FT_Load_Glyph(fFace, glyphIndex, loadFlags);
500 
501 	if (fLastError != 0)
502 		return false;
503 
504 	fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
505 	fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
506 
507 	fInsetLeft = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX);
508 	fInsetRight = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX
509 		+ fFace->glyph->metrics.width - fFace->glyph->metrics.horiAdvance);
510 
511 	switch(fGlyphRendering) {
512 		case glyph_ren_native_mono:
513 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO);
514 			if (fLastError == 0) {
515 				decompose_ft_bitmap_mono(fFace->glyph->bitmap,
516 					fFace->glyph->bitmap_left, kFlipY ?
517 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
518 					kFlipY, fScanlineBin, fScanlineStorageBin);
519 				fBounds.x1 = fScanlineStorageBin.min_x();
520 				fBounds.y1 = fScanlineStorageBin.min_y();
521 				fBounds.x2 = fScanlineStorageBin.max_x();
522 				fBounds.y2 = fScanlineStorageBin.max_y();
523 				fDataSize = fScanlineStorageBin.byte_size();
524 				fDataType = glyph_data_mono;
525 				return true;
526 			}
527 			break;
528 
529 
530 		case glyph_ren_native_gray8:
531 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_NORMAL);
532 			if (fLastError == 0) {
533 				decompose_ft_bitmap_gray8(fFace->glyph->bitmap,
534 					fFace->glyph->bitmap_left, kFlipY ?
535 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
536 					kFlipY, fScanlineAA, fScanlineStorageAA);
537 				fBounds.x1 = fScanlineStorageAA.min_x();
538 				fBounds.y1 = fScanlineStorageAA.min_y();
539 				fBounds.x2 = fScanlineStorageAA.max_x();
540 				fBounds.y2 = fScanlineStorageAA.max_y();
541 				fDataSize = fScanlineStorageAA.byte_size();
542 				fDataType = glyph_data_gray8;
543 				return true;
544 			}
545 			break;
546 
547 
548 		case glyph_ren_subpix:
549 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
550 			if (fLastError == 0) {
551 				decompose_ft_bitmap_subpix(fFace->glyph->bitmap,
552 					fFace->glyph->bitmap_left, kFlipY ?
553 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
554 					kFlipY, fScanlineSubpix, fScanlineStorageSubpix);
555 				fBounds.x1 = fScanlineStorageSubpix.min_x();
556 				fBounds.y1 = fScanlineStorageSubpix.min_y();
557 				fBounds.x2 = fScanlineStorageSubpix.max_x();
558 				fBounds.y2 = fScanlineStorageSubpix.max_y();
559 				fDataSize = fScanlineStorageSubpix.byte_size();
560 				fDataType = glyph_data_subpix;
561 				return true;
562 			}
563 			break;
564 
565 
566 		case glyph_ren_outline:
567 			fPath.remove_all();
568 			if (decompose_ft_outline(fFace->glyph->outline, kFlipY, fPath)) {
569 				agg::rect_d bounds = fPath.bounding_rect();
570 				fBounds.x1 = int(floor(bounds.x1));
571 				fBounds.y1 = int(floor(bounds.y1));
572 				fBounds.x2 = int(ceil(bounds.x2));
573 				fBounds.y2 = int(ceil(bounds.y2));
574 				fDataSize = fPath.byte_size();
575 				fDataType = glyph_data_outline;
576 				return true;
577 			}
578 			break;
579 	}
580 	return false;
581 }
582 
583 // #pragma mark -
584 
585 // WriteGlyphTo
586 void
587 FontEngine::WriteGlyphTo(uint8* data) const
588 {
589 	if (data && fDataSize) {
590 		switch(fDataType) {
591 			case glyph_data_mono:
592 				fScanlineStorageBin.serialize(data);
593 				break;
594 
595 			case glyph_data_gray8:
596 				fScanlineStorageAA.serialize(data);
597 				break;
598 
599 			case glyph_data_subpix:
600 				fScanlineStorageSubpix.serialize(data);
601 				break;
602 
603 			case glyph_data_outline:
604 				fPath.serialize(data);
605 				break;
606 
607 			case glyph_data_invalid:
608 			default:
609 				break;
610 		}
611 	}
612 }
613 
614 
615 // GetKerning
616 bool
617 FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
618 {
619 	if (fFace && first && second && FT_HAS_KERNING(fFace)) {
620 		FT_Vector delta;
621 		FT_Get_Kerning(fFace, first, second, FT_KERNING_DEFAULT, &delta);
622 
623 		double dx = int26p6_to_dbl(delta.x);
624 		double dy = int26p6_to_dbl(delta.y);
625 
626 		*x += dx;
627 		*y += dy;
628 
629 		return true;
630 	}
631 	return false;
632 }
633 
634 
635 // #pragma mark -
636 
637 
638 bool
639 FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size,
640 	FT_Encoding charMap, glyph_rendering ren_type, bool hinting,
641 	const void* fontFileBuffer, const long fontFileBufferSize)
642 {
643 	if (!fLibraryInitialized)
644 		return false;
645 
646 	fHinting = hinting;
647 
648 	fLastError = 0;
649 
650 	FT_Done_Face(fFace);
651 	if (fontFileBuffer && fontFileBufferSize) {
652 		fLastError = FT_New_Memory_Face(fLibrary,
653 			(const FT_Byte*)fontFileBuffer, fontFileBufferSize,
654 			faceIndex, &fFace);
655 	} else {
656 		fLastError = FT_New_Face(fLibrary, fontFilePath, faceIndex, &fFace);
657 	}
658 
659 	if (fLastError != 0)
660 		return false;
661 
662 	switch(ren_type) {
663 		case glyph_ren_native_mono:
664 			fGlyphRendering = glyph_ren_native_mono;
665 			break;
666 
667 		case glyph_ren_native_gray8:
668 			fGlyphRendering = glyph_ren_native_gray8;
669 			break;
670 
671 		case glyph_ren_subpix:
672 			fGlyphRendering = glyph_ren_subpix;
673 			break;
674 
675 		case glyph_ren_outline:
676 			if (FT_IS_SCALABLE(fFace))
677 				fGlyphRendering = glyph_ren_outline;
678 			else
679 				fGlyphRendering = glyph_ren_native_gray8;
680 			break;
681 	}
682 
683 	FT_Set_Pixel_Sizes(fFace,
684 		unsigned(size * 64.0) >> 6,		// pixel_width
685 		unsigned(size * 64.0) >> 6);	// pixel_height
686 
687 	if (charMap != FT_ENCODING_NONE) {
688 		fLastError = FT_Select_Charmap(fFace, charMap);
689 	} else {
690 		if (FT_Select_Charmap(fFace, FT_ENCODING_UNICODE) != 0)
691 			fLastError = FT_Select_Charmap(fFace, FT_ENCODING_NONE);
692 	}
693 
694 	return fLastError == 0;
695 }
696 
697