Use Slim Reader/Writer lock to replace CRITICAL_SECTION (better performance).
[gdipp.git] / gdipp_server / ft_renderer.cpp
blob772ba0a9de37b339deb5fba3aca45e16cdaee2ee
1 #include "stdafx.h"
2 #include "ft_renderer.h"
3 #include "gdipp_lib/scoped_rw_lock.h"
4 #include "gdipp_server/freetype.h"
5 #include "gdipp_server/global.h"
6 #include "gdipp_server/helper.h"
8 namespace gdipp
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)
27 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;
50 else
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;
63 else
65 switch (render_config->hinting)
67 case 1:
68 load_flags |= FT_LOAD_TARGET_LIGHT;
69 break;
70 case 3:
71 load_flags |= FT_LOAD_TARGET_MONO;
72 break;
73 default:
75 if (render_mode == FT_RENDER_MODE_LCD)
76 load_flags |= FT_LOAD_TARGET_LCD;
77 else
78 load_flags |= FT_LOAD_TARGET_NORMAL;
79 break;
83 switch (render_config->auto_hinting)
85 case 0:
86 load_flags |= FT_LOAD_NO_AUTOHINT;
87 break;
88 case 2:
89 load_flags |= FT_LOAD_FORCE_AUTOHINT;
90 break;
91 default:
92 load_flags |= FT_LOAD_DEFAULT;
93 break;
97 return load_flags;
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,
108 WORD glyph_index,
109 const FTC_Scaler scaler,
110 FT_F26Dot6 embolden,
111 FT_ULong load_flags,
112 bool is_italic) const
114 FT_Error ft_error;
116 FT_Glyph cached_glyph;
119 // the FreeType function seems not thread-safe
120 const scoped_rw_lock lock_w(scoped_rw_lock::SERVER_FREETYPE, false);
121 ft_error = FTC_ImageCache_LookupScaler(ft_glyph_cache, scaler, load_flags, glyph_index, &cached_glyph, NULL);
122 if (ft_error != 0)
123 return 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)
129 return NULL;
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);
137 if (need_glyph_copy)
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
143 if (is_oblique)
144 oblique_outline(glyph_outline, 0.3);
146 if (need_embolden)
148 ft_error = FT_Outline_Embolden(glyph_outline, embolden);
149 assert(ft_error == 0);
152 else
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,
161 FT_F26Dot6 embolden,
162 FT_ULong load_flags,
163 bool is_italic,
164 bool request_outline,
165 uint128_t render_trait) const
167 FT_Error ft_error;
168 FT_Glyph glyph;
170 if (request_outline)
172 generate_outline_glyph(&glyph, glyph_index, scaler, embolden, load_flags, is_italic);
173 return glyph;
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);
178 if (glyph == NULL)
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 const scoped_rw_lock lock_w(scoped_rw_lock::SERVER_FREETYPE, false);
187 ft_error = FT_Glyph_To_Bitmap(&glyph, render_mode, NULL, is_local_glyph);
188 if (ft_error != 0)
189 return NULL;
192 glyph_cache_instance.store_glyph(char_id, glyph);
195 return 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;
209 scaler.pixel = 1;
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);
221 if (is_glyph_index)
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],
228 &scaler,
229 _session->render_mode,
230 curr_embolden,
231 curr_load_flags,
232 curr_os2->is_italic(),
233 request_outline,
234 curr_render_trait);
235 RECT ctrl_box = {}, black_box = {};
237 if (new_glyph == NULL)
239 if (request_outline)
240 return false;
242 else if (curr_render_config->kerning && i > 0 && !request_outline)
244 ctrl_box.left = freetype_get_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);
253 else
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);
266 while (true)
268 GetGlyphIndices(_curr_font_holder, final_string.data(), c, &glyph_indices[0], GGI_MARK_NONEXISTING_GLYPHS);
270 std::vector<FT_Glyph>::iterator glyph_iter;
271 std::vector<RECT>::iterator ctrl_iter, black_iter;
272 UINT i;
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')
277 continue;
279 // do not render control characters, even the corresponding glyphs exist in font
280 if (iswcntrl(final_string[i]) && !request_outline)
281 *glyph_iter = NULL;
282 else if (glyph_indices[i] != 0xffff)
284 *glyph_iter = generate_bitmap_glyph(glyph_indices[i],
285 &scaler,
286 curr_render_mode,
287 curr_embolden,
288 curr_load_flags,
289 curr_os2->is_italic(),
290 request_outline,
291 curr_render_trait);
293 if (*glyph_iter == NULL)
295 if (request_outline)
296 return false;
298 else if (curr_render_config->kerning && i > 0 && !request_outline)
300 ctrl_iter->left = freetype_get_kern(&scaler, glyph_indices[i-1], glyph_indices[i]);
301 ctrl_iter->right = ctrl_iter->left;
304 else
305 continue;
307 final_string[i] = L'\0';
308 rendered_count += 1;
311 if (rendered_count >= c)
313 assert(rendered_count == c);
314 break;
317 // font linking
319 const font_link_node *curr_link = font_link_instance.lookup_link(session_font_family, font_link_index);
320 if (curr_link == NULL)
321 return false;
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(_curr_font_holder, &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(),
356 point_size,
357 curr_font_face);
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))
362 return false;
364 curr_render_trait = generate_render_trait(&linked_log_font, curr_render_mode);
366 curr_embolden = 0;
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);
373 dc_pool_instance.free(_curr_font_holder);
376 return true;
379 bool ft_renderer::render(bool is_glyph_index, LPCWSTR lpString, UINT c, glyph_run *new_glyph_run)
381 bool b_ret;
383 if (is_glyph_index)
385 _curr_font_holder = _session->font_holder;
387 else
389 // font link is possible
390 // we need an extra DC to hold linked font and not affect the session font holder
391 _curr_font_holder = dc_pool_instance.claim();
392 assert(_curr_font_holder != NULL);
393 font_mgr_instance.select_font(_session->font_id, _curr_font_holder);
395 font_mgr_instance.set_thread_font_holder(_curr_font_holder);
397 b_ret = generate_glyph_run(is_glyph_index, lpString, c, new_glyph_run, false);
399 if (!is_glyph_index)
400 dc_pool_instance.free(_curr_font_holder);
402 if (!b_ret)
403 return false;
405 POINT pen_pos = {};
407 std::vector<FT_Glyph>::iterator glyph_iter;
408 std::vector<RECT>::iterator ctrl_iter, black_iter;
409 for (glyph_iter = new_glyph_run->glyphs.begin(), ctrl_iter = new_glyph_run->ctrl_boxes.begin(), black_iter = new_glyph_run->black_boxes.begin();
410 glyph_iter != new_glyph_run->glyphs.end(); ++glyph_iter, ++ctrl_iter, ++black_iter)
412 FT_Int glyph_left = 0, glyph_width = 0;
413 FT_Vector glyph_advance = {};
415 const FT_BitmapGlyph bmp_glyph = reinterpret_cast<FT_BitmapGlyph>(*glyph_iter);
416 if (bmp_glyph != NULL)
418 glyph_left = bmp_glyph->left;
419 glyph_width = get_glyph_bmp_width(bmp_glyph->bitmap);
420 glyph_advance = bmp_glyph->root.advance;
423 ctrl_iter->left += pen_pos.x;
424 ctrl_iter->top += pen_pos.y;
425 black_iter->left = ctrl_iter->left + glyph_left;
426 black_iter->top = ctrl_iter->top;
428 pen_pos.x += int_from_16dot16(glyph_advance.x);
429 pen_pos.y += int_from_16dot16(glyph_advance.y);
431 ctrl_iter->right += pen_pos.x;
432 ctrl_iter->bottom += pen_pos.y;
433 black_iter->right = black_iter->left + glyph_width;
434 black_iter->bottom = ctrl_iter->bottom;
437 return true;