2 * Copyright 2007, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
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>
12 //----------------------------------------------------------------------------
13 // Anti-Grain Geometry - Version 2.4
14 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
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.
21 //----------------------------------------------------------------------------
22 // Contact: mcseem@antigrain.com
23 // mcseemagg@yahoo.com
24 // http://www.antigrain.com
25 //----------------------------------------------------------------------------
28 #include "FontEngine.h"
32 #include FT_LCD_FILTER_H
36 #include <agg_bitset_iterator.h>
37 #include <agg_renderer_scanline.h>
39 #include "GlobalSubpixelSettings.h"
42 static const bool kFlipY
= true;
48 return double(p
) / 64.0;
53 dbl_to_int26p6(double p
)
55 return int(p
* 64.0 + 0.5);
59 template<class PathStorage
>
61 decompose_ft_outline(const FT_Outline
& outline
, bool flip_y
, PathStorage
& path
)
63 typedef typename
PathStorage::value_type value_type
;
68 double x1
, y1
, x2
, y2
, x3
, y3
;
74 int n
; // index of contour in outline
75 int first
; // index of first point in contour
76 char tag
; // current point's state
80 for (n
= 0; n
< outline
.n_contours
; n
++) {
81 int last
; // index of last point in contour
83 last
= outline
.contours
[n
];
84 limit
= outline
.points
+ last
;
86 v_start
= outline
.points
[first
];
87 v_last
= outline
.points
[last
];
91 point
= outline
.points
+ first
;
92 tags
= outline
.tags
+ first
;
93 tag
= FT_CURVE_TAG(tags
[0]);
95 // A contour cannot start with a cubic control point!
96 if (tag
== FT_CURVE_TAG_CUBIC
)
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
107 // if both first and last points are conic,
108 // start at their middle and record its position
110 v_start
.x
= (v_start
.x
+ v_last
.x
) / 2;
111 v_start
.y
= (v_start
.y
+ v_last
.y
) / 2;
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
)));
125 while(point
< limit
) {
129 tag
= FT_CURVE_TAG(tags
[0]);
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));
141 case FT_CURVE_TAG_CONIC
: { // consume conic arcs
142 v_control
.x
= point
->x
;
143 v_control
.y
= point
->y
;
152 tag
= FT_CURVE_TAG(tags
[0]);
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
)));
170 if (tag
!= FT_CURVE_TAG_CONIC
)
173 v_middle
.x
= (v_control
.x
+ vec
.x
) / 2;
174 v_middle
.y
= (v_control
.y
+ vec
.y
) / 2;
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
)));
186 //path.curve3(conv(v_control.x),
187 // flip_y ? -conv(v_control.y) : conv(v_control.y),
189 // flip_y ? -conv(v_middle.y) : conv(v_middle.y));
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
)));
205 //path.curve3(conv(v_control.x),
206 // flip_y ? -conv(v_control.y) : conv(v_control.y),
208 // flip_y ? -conv(v_start.y) : conv(v_start.y));
212 default: { // FT_CURVE_TAG_CUBIC
213 FT_Vector vec1
, vec2
;
215 if (point
+ 1 > limit
|| FT_CURVE_TAG(tags
[1]) != FT_CURVE_TAG_CUBIC
)
226 if (point
<= limit
) {
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
)));
246 //path.curve4(conv(vec1.x),
247 // flip_y ? -conv(vec1.y) : conv(vec1.y),
249 // flip_y ? -conv(vec2.y) : conv(vec2.y),
251 // flip_y ? -conv(vec.y) : conv(vec.y));
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
)));
269 //path.curve4(conv(vec1.x),
270 // flip_y ? -conv(vec1.y) : conv(vec1.y),
272 // flip_y ? -conv(vec2.y) : conv(vec2.y),
274 // flip_y ? -conv(v_start.y) : conv(v_start.y));
280 path
.close_polygon();
290 template<class Scanline
, class ScanlineStorage
>
292 decompose_ft_bitmap_mono(const FT_Bitmap
& bitmap
, int x
, int y
,
293 bool flip_y
, Scanline
& sl
, ScanlineStorage
& storage
)
295 const uint8
* buf
= (const uint8
*)bitmap
.buffer
;
296 int pitch
= bitmap
.pitch
;
297 sl
.reset(x
, x
+ bitmap
.width
);
300 buf
+= bitmap
.pitch
* (bitmap
.rows
- 1);
304 for (unsigned int i
= 0; i
< bitmap
.rows
; i
++) {
306 agg::bitset_iterator
bits(buf
, 0);
307 for (unsigned int j
= 0; j
< bitmap
.width
; j
++) {
309 sl
.add_cell(x
+ j
, agg::cover_full
);
313 if (sl
.num_spans()) {
314 sl
.finalize(y
- i
- 1);
321 template<class Scanline
, class ScanlineStorage
>
323 decompose_ft_bitmap_gray8(const FT_Bitmap
& bitmap
, int x
, int y
,
324 bool flip_y
, Scanline
& sl
, ScanlineStorage
& storage
)
326 const uint8
* buf
= (const uint8
*)bitmap
.buffer
;
327 int pitch
= bitmap
.pitch
;
328 sl
.reset(x
, x
+ bitmap
.width
);
331 buf
+= bitmap
.pitch
* (bitmap
.rows
- 1);
335 for (unsigned int i
= 0; i
< bitmap
.rows
; i
++) {
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
++) {
343 sl
.add_cell(x
+ j
, agg::cover_full
);
347 const uint8
* p
= buf
;
348 for (unsigned int j
= 0; j
< bitmap
.width
; j
++) {
350 sl
.add_cell(x
+ j
, *p
);
356 if (sl
.num_spans()) {
357 sl
.finalize(y
- i
- 1);
364 template<class Scanline
, class ScanlineStorage
>
366 decompose_ft_bitmap_subpix(const FT_Bitmap
& bitmap
, int x
, int y
,
367 bool flip_y
, Scanline
& sl
, ScanlineStorage
& storage
)
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);
376 buf
+= bitmap
.pitch
* (bitmap
.rows
- 1);
381 for (unsigned int i
= 0; i
< bitmap
.rows
; i
++) {
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 (unsigned int j
= 0; j
< bitmap
.width
; j
++) {
390 agg::cover_full
, agg::cover_full
, agg::cover_full
);
395 const uint8
* p
= buf
;
396 int w
= bitmap
.width
/ 3;
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]);
406 if (sl
.num_spans()) {
407 sl
.finalize(y
- i
- 1);
412 // filter based anti-colored edges method
414 const uint8 filter
[5] = { 0x10, 0x40, 0x70, 0x40, 0x10 };
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
422 buf
+= bitmap
.pitch
* (bitmap
.rows
- 1);
426 for (int i
= 0; i
< bitmap
.rows
; i
++) {
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
++) {
435 agg::cover_full
, agg::cover_full
, agg::cover_full
);
440 const uint8
* p
= buf
;
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])))) {
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));
455 if (coverR
|| coverG
|| coverB
)
456 sl
.add_cell(x
- 1, coverR
, coverG
, coverB
);
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]))) {
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]))) {
471 coverR
= ((j
> 0 ? p
[-2] * filter
[4]
472 + p
[-1] * filter
[3] : 0)
473 + p
[0] * filter
[2] + p
[1] * filter
[1]
476 coverG
= ((j
> 0 ? p
[-1] * filter
[4] : 0)
477 + p
[0] * filter
[3] + p
[1] * filter
[2]
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));
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]))) {
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]
494 + (j
< w
-1 ? p
[3] * filter
[0] : 0))
496 coverB
= (p
[0] * filter
[4] + p
[1] * filter
[3]
498 + (j
< w
-1 ? p
[3] * filter
[1]
499 + p
[4] * filter
[0] : 0))
501 coverR
= coverR
| ( -(coverR
>> 8));
502 coverG
= coverG
| ( -(coverG
>> 8));
503 coverB
= coverB
| ( -(coverB
>> 8));
507 coverR
= ((j
> 0 ? p
[-2] * filter
[4]
508 + p
[-1] * filter
[3] : 0)
509 + p
[0] * filter
[2] + p
[1] * filter
[1]
512 coverG
= ((j
> 0 ? p
[-1] * filter
[4] : 0)
513 + p
[0] * filter
[3] + p
[1] * filter
[2]
515 + (j
< w
-1 ? p
[3] * filter
[0] : 0))
517 coverB
= (p
[0] * filter
[4] + p
[1] * filter
[3]
519 + (j
< w
-1 ? p
[3] * filter
[1]
520 + p
[4] * filter
[0] : 0))
522 coverR
= coverR
| ( -(coverR
>> 8));
523 coverG
= coverG
| ( -(coverG
>> 8));
524 coverB
= coverB
| ( -(coverB
>> 8));
527 if (coverR
|| coverG
|| coverB
)
528 sl
.add_cell(x
+ j
, coverR
, coverG
, coverB
);
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])))) {
535 coverR
= (p
[-2] * filter
[4] + p
[-1] * filter
[3]) >> 8;
536 coverG
= (p
[-1] * filter
[4]) >> 8;
538 coverR
= coverR
| ( -(coverR
>> 8));
539 coverG
= coverG
| ( -(coverG
>> 8));
541 if (coverR
|| coverG
|| coverB
)
542 sl
.add_cell(x
+ w
, coverR
, coverG
, coverB
);
547 if (sl
.num_spans()) {
548 sl
.finalize(y
- i
- 1);
559 FontEngine::FontEngine()
562 fLibraryInitialized(false),
566 fGlyphRendering(glyph_ren_native_gray8
),
570 fDataType(glyph_data_invalid
),
582 fScanlineStorageAA(),
583 fScanlineStorageBin(),
584 fScanlineStorageSubpix()
586 fCurves
.approximation_scale(4.0);
588 fLastError
= FT_Init_FreeType(&fLibrary
);
590 fLibraryInitialized
= true;
594 FontEngine::~FontEngine()
598 if (fLibraryInitialized
)
599 FT_Done_FreeType(fLibrary
);
604 FontEngine::CountFaces() const
607 return fFace
->num_faces
;
614 FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode
) const
616 return FT_Get_Char_Index(fFace
, glyphCode
);
621 FontEngine::PrepareGlyph(uint32 glyphIndex
)
623 FT_Int32 loadFlags
= fHinting
? FT_LOAD_DEFAULT
: FT_LOAD_NO_HINTING
;
624 loadFlags
|= fGlyphRendering
== glyph_ren_subpix
?
625 FT_LOAD_TARGET_LCD
: FT_LOAD_TARGET_NORMAL
;
627 // Load unscaled and without hinting to get precise advance values
628 // for B_CHAR_SPACING
629 fLastError
= FT_Load_Glyph(fFace
, glyphIndex
, loadFlags
630 | FT_LOAD_NO_HINTING
| FT_LOAD_NO_SCALE
);
632 fPreciseAdvanceX
= (double)fFace
->glyph
->advance
.x
/ fFace
->units_per_EM
;
633 fPreciseAdvanceY
= (double)fFace
->glyph
->advance
.y
/ fFace
->units_per_EM
;
635 // Need to load again with hinting.
636 fLastError
= FT_Load_Glyph(fFace
, glyphIndex
, loadFlags
);
641 fAdvanceX
= int26p6_to_dbl(fFace
->glyph
->advance
.x
);
642 fAdvanceY
= int26p6_to_dbl(fFace
->glyph
->advance
.y
);
644 fInsetLeft
= int26p6_to_dbl(fFace
->glyph
->metrics
.horiBearingX
);
645 fInsetRight
= int26p6_to_dbl(fFace
->glyph
->metrics
.horiBearingX
646 + fFace
->glyph
->metrics
.width
- fFace
->glyph
->metrics
.horiAdvance
);
648 switch(fGlyphRendering
) {
649 case glyph_ren_native_mono
:
650 fLastError
= FT_Render_Glyph(fFace
->glyph
, FT_RENDER_MODE_MONO
);
651 if (fLastError
== 0) {
652 decompose_ft_bitmap_mono(fFace
->glyph
->bitmap
,
653 fFace
->glyph
->bitmap_left
, kFlipY
?
654 -fFace
->glyph
->bitmap_top
: fFace
->glyph
->bitmap_top
,
655 kFlipY
, fScanlineBin
, fScanlineStorageBin
);
656 fBounds
.x1
= fScanlineStorageBin
.min_x();
657 fBounds
.y1
= fScanlineStorageBin
.min_y();
658 fBounds
.x2
= fScanlineStorageBin
.max_x();
659 fBounds
.y2
= fScanlineStorageBin
.max_y();
660 fDataSize
= fScanlineStorageBin
.byte_size();
661 fDataType
= glyph_data_mono
;
667 case glyph_ren_native_gray8
:
668 fLastError
= FT_Render_Glyph(fFace
->glyph
, FT_RENDER_MODE_NORMAL
);
669 if (fLastError
== 0) {
670 decompose_ft_bitmap_gray8(fFace
->glyph
->bitmap
,
671 fFace
->glyph
->bitmap_left
, kFlipY
?
672 -fFace
->glyph
->bitmap_top
: fFace
->glyph
->bitmap_top
,
673 kFlipY
, fScanlineAA
, fScanlineStorageAA
);
674 fBounds
.x1
= fScanlineStorageAA
.min_x();
675 fBounds
.y1
= fScanlineStorageAA
.min_y();
676 fBounds
.x2
= fScanlineStorageAA
.max_x();
677 fBounds
.y2
= fScanlineStorageAA
.max_y();
678 fDataSize
= fScanlineStorageAA
.byte_size();
679 fDataType
= glyph_data_gray8
;
685 case glyph_ren_subpix
:
686 fLastError
= FT_Render_Glyph(fFace
->glyph
, FT_RENDER_MODE_LCD
);
687 if (fLastError
== 0) {
688 decompose_ft_bitmap_subpix(fFace
->glyph
->bitmap
,
689 fFace
->glyph
->bitmap_left
, kFlipY
?
690 -fFace
->glyph
->bitmap_top
: fFace
->glyph
->bitmap_top
,
691 kFlipY
, fScanlineSubpix
, fScanlineStorageSubpix
);
692 fBounds
.x1
= fScanlineStorageSubpix
.min_x();
693 fBounds
.y1
= fScanlineStorageSubpix
.min_y();
694 fBounds
.x2
= fScanlineStorageSubpix
.max_x();
695 fBounds
.y2
= fScanlineStorageSubpix
.max_y();
696 fDataSize
= fScanlineStorageSubpix
.byte_size();
697 fDataType
= glyph_data_subpix
;
703 case glyph_ren_outline
:
705 if (decompose_ft_outline(fFace
->glyph
->outline
, kFlipY
, fPath
)) {
706 agg::rect_d bounds
= fPath
.bounding_rect();
707 fBounds
.x1
= int(floor(bounds
.x1
));
708 fBounds
.y1
= int(floor(bounds
.y1
));
709 fBounds
.x2
= int(ceil(bounds
.x2
));
710 fBounds
.y2
= int(ceil(bounds
.y2
));
711 fDataSize
= fPath
.byte_size();
712 fDataType
= glyph_data_outline
;
724 FontEngine::WriteGlyphTo(uint8
* data
) const
726 if (data
&& fDataSize
) {
728 case glyph_data_mono
:
729 fScanlineStorageBin
.serialize(data
);
732 case glyph_data_gray8
:
733 fScanlineStorageAA
.serialize(data
);
736 case glyph_data_subpix
:
737 fScanlineStorageSubpix
.serialize(data
);
740 case glyph_data_outline
:
741 fPath
.serialize(data
);
744 case glyph_data_invalid
:
754 FontEngine::GetKerning(uint32 first
, uint32 second
, double* x
, double* y
)
756 if (fFace
&& first
&& second
&& FT_HAS_KERNING(fFace
)) {
758 FT_Get_Kerning(fFace
, first
, second
, FT_KERNING_DEFAULT
, &delta
);
760 double dx
= int26p6_to_dbl(delta
.x
);
761 double dy
= int26p6_to_dbl(delta
.y
);
776 FontEngine::Init(const char* fontFilePath
, unsigned faceIndex
, double size
,
777 FT_Encoding charMap
, glyph_rendering ren_type
, bool hinting
,
778 const char* fontFileBuffer
, const long fontFileBufferSize
)
780 if (!fLibraryInitialized
)
788 if (fontFileBuffer
&& fontFileBufferSize
) {
789 fLastError
= FT_New_Memory_Face(fLibrary
,
790 (const FT_Byte
*)fontFileBuffer
, fontFileBufferSize
,
793 fLastError
= FT_New_Face(fLibrary
, fontFilePath
, faceIndex
, &fFace
);
800 case glyph_ren_native_mono
:
801 fGlyphRendering
= glyph_ren_native_mono
;
804 case glyph_ren_native_gray8
:
805 fGlyphRendering
= glyph_ren_native_gray8
;
808 case glyph_ren_subpix
:
809 fGlyphRendering
= glyph_ren_subpix
;
812 case glyph_ren_outline
:
813 if (FT_IS_SCALABLE(fFace
))
814 fGlyphRendering
= glyph_ren_outline
;
816 fGlyphRendering
= glyph_ren_native_gray8
;
820 FT_Set_Pixel_Sizes(fFace
,
821 unsigned(size
* 64.0) >> 6, // pixel_width
822 unsigned(size
* 64.0) >> 6); // pixel_height
824 if (charMap
!= FT_ENCODING_NONE
) {
825 fLastError
= FT_Select_Charmap(fFace
, charMap
);
827 if (FT_Select_Charmap(fFace
, FT_ENCODING_UNICODE
) != 0)
828 fLastError
= FT_Select_Charmap(fFace
, FT_ENCODING_NONE
);
831 return fLastError
== 0;