2 #include "ft_renderer.h"
3 #include "gdipp_lib/lock.h"
4 #include "gdipp_server/freetype.h"
5 #include "gdipp_server/global.h"
6 #include "gdipp_server/helper.h"
11 ft_renderer::ft_renderer(rpc_session
*render_session
)
12 : renderer(render_session
)
16 FT_F26Dot6
ft_renderer::get_embolden_diff(char font_weight_class
, char text_weight_class
)
18 // the embolden weight is based on the difference between demanded weight and the regular weight
20 const FT_F26Dot6 embolden_values
[] = {-32, -16, 0, 16, 32};
21 const char embolden_class_count
= sizeof(embolden_values
) / sizeof(FT_F26Dot6
);
22 const char regular_embolden_class
= (embolden_class_count
- 1) / 2;
24 char embolden_class
= text_weight_class
- font_weight_class
+ regular_embolden_class
;
26 if (embolden_class
< 0)
28 else if (embolden_class
>= embolden_class_count
)
29 embolden_class
= embolden_class_count
- 1;
31 return embolden_values
[embolden_class
];
34 void ft_renderer::get_font_width_height(const OUTLINETEXTMETRICW
*outline_metrics
, FT_Short xAvgCharWidth
, FT_UInt
&font_width
, FT_UInt
&font_height
)
37 while the height in FreeType scaler has the same meaning as the height value in LOGFONT structure, the width is different
38 what we know is, when the width in LOGFONT is the xAvgCharWidth (from the OS/2 table), the corresponding FreeType scaler width is the height
39 therefore we need conversion when LOGFONT width is not 0
40 simple calculation yields freetype_width = logfont_width * em_square / xAvgCharWidth
41 note that the tmAveCharWidth field in TEXTMETRIC is the actual LOGFONT width, which is never 0
44 assert(outline_metrics
!= NULL
);
46 font_height
= outline_metrics
->otmTextMetrics
.tmHeight
- outline_metrics
->otmTextMetrics
.tmInternalLeading
;
48 if (xAvgCharWidth
== 0)
49 font_width
= font_height
* outline_metrics
->otmTextMetrics
.tmDigitizedAspectX
/ outline_metrics
->otmTextMetrics
.tmDigitizedAspectY
;
52 // compare the xAvgCharWidth against the current average char width
53 font_width
= outline_metrics
->otmTextMetrics
.tmAveCharWidth
* outline_metrics
->otmEMSquare
/ xAvgCharWidth
;
57 FT_ULong
ft_renderer::make_load_flags(const render_config_static
*render_config
, FT_Render_Mode render_mode
)
59 FT_ULong load_flags
= FT_LOAD_CROP_BITMAP
| (render_config
->embedded_bitmap
? 0 : FT_LOAD_NO_BITMAP
);
61 if (render_config
->hinting
== 0)
62 load_flags
|= FT_LOAD_NO_HINTING
;
65 switch (render_config
->hinting
)
68 load_flags
|= FT_LOAD_TARGET_LIGHT
;
71 load_flags
|= FT_LOAD_TARGET_MONO
;
75 if (render_mode
== FT_RENDER_MODE_LCD
)
76 load_flags
|= FT_LOAD_TARGET_LCD
;
78 load_flags
|= FT_LOAD_TARGET_NORMAL
;
83 switch (render_config
->auto_hinting
)
86 load_flags
|= FT_LOAD_NO_AUTOHINT
;
89 load_flags
|= FT_LOAD_FORCE_AUTOHINT
;
92 load_flags
|= FT_LOAD_DEFAULT
;
100 void ft_renderer::oblique_outline(const FT_Outline
*outline
, double slant_adv
)
102 // advance of slant on x-axis
103 FT_Matrix oblique_mat
= {float_to_16dot16(1), float_to_16dot16(slant_adv
), 0, float_to_16dot16(1)};
104 FT_Outline_Transform(outline
, &oblique_mat
);
107 bool ft_renderer::generate_outline_glyph(FT_Glyph
*glyph
,
109 const FTC_Scaler scaler
,
112 bool is_italic
) const
116 FT_Glyph cached_glyph
;
119 // the FreeType function seems not thread-safe
120 lock
l(lock::SERVER_FREETYPE
);
121 ft_error
= FTC_ImageCache_LookupScaler(ft_glyph_cache
, scaler
, load_flags
, glyph_index
, &cached_glyph
, NULL
);
126 // some fonts are embedded with pre-rendered glyph bitmap
127 // in that case, use original ExtTextOutW
128 if (cached_glyph
->format
!= FT_GLYPH_FORMAT_OUTLINE
)
131 // if italic style is demanded, and the font has italic glyph, do oblique transformation
132 const OUTLINETEXTMETRICW
*outline_metrics
= reinterpret_cast<const OUTLINETEXTMETRICW
*>(_session
->outline_metrics_buf
);
133 const bool is_oblique
= ((outline_metrics
->otmTextMetrics
.tmItalic
!= 0) && !is_italic
);
134 const bool need_embolden
= (embolden
!= 0);
135 const bool need_glyph_copy
= (is_oblique
|| need_embolden
);
139 FT_Glyph_Copy(cached_glyph
, glyph
);
140 FT_Outline
*glyph_outline
= &(reinterpret_cast<FT_OutlineGlyph
>(*glyph
)->outline
);
142 // it seems faster if oblique first, and then embolden
144 oblique_outline(glyph_outline
, 0.3);
148 ft_error
= FT_Outline_Embolden(glyph_outline
, embolden
);
149 assert(ft_error
== 0);
153 *glyph
= cached_glyph
;
155 return need_glyph_copy
;
158 const FT_Glyph
ft_renderer::generate_bitmap_glyph(WORD glyph_index
,
159 const FTC_Scaler scaler
,
160 FT_Render_Mode render_mode
,
164 bool request_outline
,
165 uint128_t render_trait
) const
172 generate_outline_glyph(&glyph
, glyph_index
, scaler
, embolden
, load_flags
, is_italic
);
176 const glyph_cache::char_id_type char_id
= glyph_cache::get_char_id(render_trait
, glyph_index
, true);
177 glyph
= glyph_cache_instance
.lookup_glyph(char_id
);
180 // no cached glyph, or outline glyph is requested, generate outline
181 const bool is_local_glyph
= generate_outline_glyph(&glyph
, glyph_index
, scaler
, embolden
, load_flags
, is_italic
);
183 // outline -> bitmap conversion
185 // the FreeType function seems not thread-safe
186 lock
l(lock::SERVER_FREETYPE
);
187 ft_error
= FT_Glyph_To_Bitmap(&glyph
, render_mode
, NULL
, is_local_glyph
);
192 glyph_cache_instance
.store_glyph(char_id
, glyph
);
198 bool ft_renderer::generate_glyph_run(bool is_glyph_index
, LPCWSTR lpString
, UINT c
, glyph_run
*new_glyph_run
, bool request_outline
)
200 const OUTLINETEXTMETRICW
*curr_outline_metrics
= reinterpret_cast<const OUTLINETEXTMETRICW
*>(_session
->outline_metrics_buf
);
201 const render_config_static
*curr_render_config
= _session
->render_config
;
202 uint128_t curr_render_trait
= _session
->render_trait
;
203 const wchar_t *curr_font_face
= metric_face_name(curr_outline_metrics
);
204 const os2_metrics
*curr_os2
= font_mgr_instance
.lookup_os2_metrics(_session
->font_id
);
205 const wchar_t *session_font_family
= metric_family_name(curr_outline_metrics
);
207 FTC_ScalerRec scaler
= {};
208 scaler
.face_id
= _session
->font_id
;
210 get_font_width_height(curr_outline_metrics
, (_session
->log_font
.lfWidth
== 0 ? 0 : curr_os2
->get_xAvgCharWidth()), scaler
.height
, scaler
.width
);
212 FT_F26Dot6 curr_embolden
= 0;
213 if (_session
->log_font
.lfWeight
!= FW_DONTCARE
)
215 // embolden if some weight is demanded
216 curr_embolden
= curr_render_config
->embolden
+ get_embolden_diff(curr_os2
->get_weight_class(), static_cast<char>(_session
->log_font
.lfWeight
));
219 FT_ULong curr_load_flags
= make_load_flags(curr_render_config
, _session
->render_mode
);
223 // directly render glyph indices with the current DC font
225 for (UINT i
= 0; i
< c
; ++i
)
227 const FT_Glyph new_glyph
= generate_bitmap_glyph(lpString
[i
],
229 _session
->render_mode
,
232 curr_os2
->is_italic(),
235 RECT ctrl_box
= {}, black_box
= {};
237 if (new_glyph
== NULL
)
242 else if (curr_render_config
->kerning
&& i
> 0 && !request_outline
)
244 ctrl_box
.left
= get_freetype_kern(&scaler
, lpString
[i
-1], lpString
[i
]);
245 ctrl_box
.right
= ctrl_box
.left
;
248 new_glyph_run
->glyphs
.push_back(new_glyph
);
249 new_glyph_run
->ctrl_boxes
.push_back(ctrl_box
);
250 new_glyph_run
->black_boxes
.push_back(black_box
);
255 FT_Render_Mode curr_render_mode
= _session
->render_mode
;
257 UINT rendered_count
= 0;
258 int font_link_index
= 0;
259 std::wstring
final_string(lpString
, c
);
260 std::vector
<unsigned short> glyph_indices(c
);
262 new_glyph_run
->glyphs
.resize(c
);
263 new_glyph_run
->ctrl_boxes
.resize(c
);
264 new_glyph_run
->black_boxes
.resize(c
);
268 font_mgr_instance
.lookup_glyph_indices(scaler
.face_id
, final_string
.data(), c
, &glyph_indices
[0]);
270 std::vector
<FT_Glyph
>::iterator glyph_iter
;
271 std::vector
<RECT
>::iterator ctrl_iter
, black_iter
;
273 for (glyph_iter
= new_glyph_run
->glyphs
.begin(), ctrl_iter
= new_glyph_run
->ctrl_boxes
.begin(), black_iter
= new_glyph_run
->black_boxes
.begin(), i
= 0;
274 i
< c
; i
++, glyph_iter
++, ctrl_iter
++, black_iter
++)
276 if (final_string
[i
] == L
'\0')
279 // do not render control characters, even the corresponding glyphs exist in font
280 if (iswcntrl(final_string
[i
]) && !request_outline
)
282 else if (glyph_indices
[i
] != 0xffff)
284 *glyph_iter
= generate_bitmap_glyph(glyph_indices
[i
],
289 curr_os2
->is_italic(),
293 if (*glyph_iter
== NULL
)
298 else if (curr_render_config
->kerning
&& i
> 0 && !request_outline
)
300 ctrl_iter
->left
= get_freetype_kern(&scaler
, glyph_indices
[i
-1], glyph_indices
[i
]);
301 ctrl_iter
->right
= ctrl_iter
->left
;
307 final_string
[i
] = L
'\0';
311 if (rendered_count
>= c
)
313 assert(rendered_count
== c
);
319 const font_link_node
*curr_link
= font_link_instance
.lookup_link(session_font_family
, font_link_index
);
320 if (curr_link
== NULL
)
322 font_link_index
+= 1;
324 LOGFONTW linked_log_font
= _session
->log_font
;
326 this reset is essential to make GetGlyphIndices work correctly
327 for example, lfOutPrecision might be OUT_PS_ONLY_PRECIS for Myriad Pro
328 if create HFONT of Microsoft YaHei with such lfOutPrecision, GetGlyphIndices always fails
330 linked_log_font
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
331 wcsncpy_s(linked_log_font
.lfFaceName
, curr_link
->font_family
.c_str(), LF_FACESIZE
);
333 BYTE
*curr_outline_metrics_buf
;
334 unsigned long curr_outline_metrics_size
;
335 scaler
.face_id
= font_mgr_instance
.register_font(&linked_log_font
, &curr_outline_metrics_buf
, &curr_outline_metrics_size
);
336 assert(scaler
.face_id
!= NULL
);
338 // reload metrics for the linked font
340 const OUTLINETEXTMETRICW
*curr_outline_metrics
= reinterpret_cast<const OUTLINETEXTMETRICW
*>(curr_outline_metrics_buf
);
341 curr_font_face
= metric_face_name(curr_outline_metrics
);
343 if (curr_link
->scaling
!= 1.0)
345 // apply font linking scaling factor
346 scaler
.width
= static_cast<FT_UInt
>(scaler
.width
* curr_link
->scaling
);
347 scaler
.height
= static_cast<FT_UInt
>(scaler
.height
* curr_link
->scaling
);
350 curr_os2
= font_mgr_instance
.lookup_os2_metrics(scaler
.face_id
);
351 const char font_weight_class
= curr_os2
->get_weight_class();
352 const LONG point_size
= (linked_log_font
.lfHeight
> 0 ? linked_log_font
.lfHeight
: -MulDiv(linked_log_font
.lfHeight
, 72, curr_outline_metrics
->otmTextMetrics
.tmDigitizedAspectY
));
354 curr_render_config
= font_render_config_cache_instance
.get_font_render_config(!!font_weight_class
,
355 curr_os2
->is_italic(),
359 delete[] curr_outline_metrics_buf
;
361 if (!get_render_mode(curr_render_config
->render_mode
, _session
->bits_per_pixel
, _session
->log_font
.lfQuality
, &curr_render_mode
))
364 curr_render_trait
= generate_render_trait(&linked_log_font
, curr_render_mode
);
367 if (linked_log_font
.lfWeight
!= FW_DONTCARE
)
368 curr_embolden
= curr_render_config
->embolden
+ get_embolden_diff(font_weight_class
, static_cast<char>(linked_log_font
.lfWeight
));
370 curr_load_flags
= make_load_flags(curr_render_config
, _session
->render_mode
);
377 bool ft_renderer::render(bool is_glyph_index
, LPCWSTR lpString
, UINT c
, glyph_run
*new_glyph_run
)
381 b_ret
= generate_glyph_run(is_glyph_index
, lpString
, c
, new_glyph_run
, false);
387 std::vector
<FT_Glyph
>::iterator glyph_iter
;
388 std::vector
<RECT
>::iterator ctrl_iter
, black_iter
;
389 for (glyph_iter
= new_glyph_run
->glyphs
.begin(), ctrl_iter
= new_glyph_run
->ctrl_boxes
.begin(), black_iter
= new_glyph_run
->black_boxes
.begin();
390 glyph_iter
!= new_glyph_run
->glyphs
.end(); ++glyph_iter
, ++ctrl_iter
, ++black_iter
)
392 FT_Int glyph_left
= 0, glyph_width
= 0;
393 FT_Vector glyph_advance
= {};
395 const FT_BitmapGlyph bmp_glyph
= reinterpret_cast<FT_BitmapGlyph
>(*glyph_iter
);
396 if (bmp_glyph
!= NULL
)
398 glyph_left
= bmp_glyph
->left
;
399 glyph_width
= get_glyph_bmp_width(bmp_glyph
->bitmap
);
400 glyph_advance
= bmp_glyph
->root
.advance
;
403 ctrl_iter
->left
+= pen_pos
.x
;
404 ctrl_iter
->top
+= pen_pos
.y
;
405 black_iter
->left
= ctrl_iter
->left
+ glyph_left
;
406 black_iter
->top
= ctrl_iter
->top
;
408 pen_pos
.x
+= int_from_16dot16(glyph_advance
.x
);
409 pen_pos
.y
+= int_from_16dot16(glyph_advance
.y
);
411 ctrl_iter
->right
+= pen_pos
.x
;
412 ctrl_iter
->bottom
+= pen_pos
.y
;
413 black_iter
->right
= black_iter
->left
+ glyph_width
;
414 black_iter
->bottom
= ctrl_iter
->bottom
;