xref: /haiku/src/servers/app/font/FontEngine.cpp (revision e7e3b6c14af93058fc5aab68ffa695bbcdd77053)
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 	fPreciseAdvanceX = (double)fFace->glyph->advance.x / fFace->units_per_EM;
493 	fPreciseAdvanceY = (double)fFace->glyph->advance.y / fFace->units_per_EM;
494 
495 	// Need to load again with hinting.
496 	fLastError = FT_Load_Glyph(fFace, glyphIndex, loadFlags);
497 
498 	if (fLastError != 0)
499 		return false;
500 
501 	fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
502 	fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
503 
504 	fInsetLeft = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX);
505 	fInsetRight = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX
506 		+ fFace->glyph->metrics.width - fFace->glyph->metrics.horiAdvance);
507 
508 	switch(fGlyphRendering) {
509 		case glyph_ren_native_mono:
510 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO);
511 			if (fLastError == 0) {
512 				decompose_ft_bitmap_mono(fFace->glyph->bitmap,
513 					fFace->glyph->bitmap_left, kFlipY ?
514 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
515 					kFlipY, fScanlineBin, fScanlineStorageBin);
516 				fBounds.x1 = fScanlineStorageBin.min_x();
517 				fBounds.y1 = fScanlineStorageBin.min_y();
518 				fBounds.x2 = fScanlineStorageBin.max_x();
519 				fBounds.y2 = fScanlineStorageBin.max_y();
520 				fDataSize = fScanlineStorageBin.byte_size();
521 				fDataType = glyph_data_mono;
522 				return true;
523 			}
524 			break;
525 
526 
527 		case glyph_ren_native_gray8:
528 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_NORMAL);
529 			if (fLastError == 0) {
530 				decompose_ft_bitmap_gray8(fFace->glyph->bitmap,
531 					fFace->glyph->bitmap_left, kFlipY ?
532 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
533 					kFlipY, fScanlineAA, fScanlineStorageAA);
534 				fBounds.x1 = fScanlineStorageAA.min_x();
535 				fBounds.y1 = fScanlineStorageAA.min_y();
536 				fBounds.x2 = fScanlineStorageAA.max_x();
537 				fBounds.y2 = fScanlineStorageAA.max_y();
538 				fDataSize = fScanlineStorageAA.byte_size();
539 				fDataType = glyph_data_gray8;
540 				return true;
541 			}
542 			break;
543 
544 
545 		case glyph_ren_subpix:
546 			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
547 			if (fLastError == 0) {
548 				decompose_ft_bitmap_subpix(fFace->glyph->bitmap,
549 					fFace->glyph->bitmap_left, kFlipY ?
550 					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
551 					kFlipY, fScanlineSubpix, fScanlineStorageSubpix);
552 				fBounds.x1 = fScanlineStorageSubpix.min_x();
553 				fBounds.y1 = fScanlineStorageSubpix.min_y();
554 				fBounds.x2 = fScanlineStorageSubpix.max_x();
555 				fBounds.y2 = fScanlineStorageSubpix.max_y();
556 				fDataSize = fScanlineStorageSubpix.byte_size();
557 				fDataType = glyph_data_subpix;
558 				return true;
559 			}
560 			break;
561 
562 
563 		case glyph_ren_outline:
564 			fPath.remove_all();
565 			if (decompose_ft_outline(fFace->glyph->outline, kFlipY, fPath)) {
566 				agg::rect_d bounds = fPath.bounding_rect();
567 				fBounds.x1 = int(floor(bounds.x1));
568 				fBounds.y1 = int(floor(bounds.y1));
569 				fBounds.x2 = int(ceil(bounds.x2));
570 				fBounds.y2 = int(ceil(bounds.y2));
571 				fDataSize = fPath.byte_size();
572 				fDataType = glyph_data_outline;
573 				return true;
574 			}
575 			break;
576 	}
577 	return false;
578 }
579 
580 // #pragma mark -
581 
582 // WriteGlyphTo
583 void
584 FontEngine::WriteGlyphTo(uint8* data) const
585 {
586 	if (data && fDataSize) {
587 		switch(fDataType) {
588 			case glyph_data_mono:
589 				fScanlineStorageBin.serialize(data);
590 				break;
591 
592 			case glyph_data_gray8:
593 				fScanlineStorageAA.serialize(data);
594 				break;
595 
596 			case glyph_data_subpix:
597 				fScanlineStorageSubpix.serialize(data);
598 				break;
599 
600 			case glyph_data_outline:
601 				fPath.serialize(data);
602 				break;
603 
604 			case glyph_data_invalid:
605 			default:
606 				break;
607 		}
608 	}
609 }
610 
611 
612 // GetKerning
613 bool
614 FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
615 {
616 	if (fFace && first && second && FT_HAS_KERNING(fFace)) {
617 		FT_Vector delta;
618 		FT_Get_Kerning(fFace, first, second, FT_KERNING_DEFAULT, &delta);
619 
620 		double dx = int26p6_to_dbl(delta.x);
621 		double dy = int26p6_to_dbl(delta.y);
622 
623 		*x += dx;
624 		*y += dy;
625 
626 		return true;
627 	}
628 	return false;
629 }
630 
631 
632 // #pragma mark -
633 
634 
635 bool
636 FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size,
637 	FT_Encoding charMap, glyph_rendering ren_type, bool hinting,
638 	const char* fontFileBuffer, const long fontFileBufferSize)
639 {
640 	if (!fLibraryInitialized)
641 		return false;
642 
643 	fHinting = hinting;
644 
645 	fLastError = 0;
646 
647 	FT_Done_Face(fFace);
648 	if (fontFileBuffer && fontFileBufferSize) {
649 		fLastError = FT_New_Memory_Face(fLibrary,
650 			(const FT_Byte*)fontFileBuffer, fontFileBufferSize,
651 			faceIndex, &fFace);
652 	} else {
653 		fLastError = FT_New_Face(fLibrary, fontFilePath, faceIndex, &fFace);
654 	}
655 
656 	if (fLastError != 0)
657 		return false;
658 
659 	switch(ren_type) {
660 		case glyph_ren_native_mono:
661 			fGlyphRendering = glyph_ren_native_mono;
662 			break;
663 
664 		case glyph_ren_native_gray8:
665 			fGlyphRendering = glyph_ren_native_gray8;
666 			break;
667 
668 		case glyph_ren_subpix:
669 			fGlyphRendering = glyph_ren_subpix;
670 			break;
671 
672 		case glyph_ren_outline:
673 			if (FT_IS_SCALABLE(fFace))
674 				fGlyphRendering = glyph_ren_outline;
675 			else
676 				fGlyphRendering = glyph_ren_native_gray8;
677 			break;
678 	}
679 
680 	FT_Set_Pixel_Sizes(fFace,
681 		unsigned(size * 64.0) >> 6,		// pixel_width
682 		unsigned(size * 64.0) >> 6);	// pixel_height
683 
684 	if (charMap != FT_ENCODING_NONE) {
685 		fLastError = FT_Select_Charmap(fFace, charMap);
686 	} else {
687 		if (FT_Select_Charmap(fFace, FT_ENCODING_UNICODE) != 0)
688 			fLastError = FT_Select_Charmap(fFace, FT_ENCODING_NONE);
689 	}
690 
691 	return fLastError == 0;
692 }
693 
694