Use One-Time Initialization for glyph run caching to avoid duplicate glyph run genera...
[gdipp.git] / gdipp_server / ggo_renderer.cpp
blob41ef309c900b92dca5412a571b1855aa41ae5ebe
1 #include "stdafx.h"
2 #include "ggo_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 ggo_renderer::ggo_renderer(rpc_session *render_session)
12 : renderer(render_session)
16 bool ggo_renderer::render(bool is_glyph_index, LPCWSTR lpString, UINT c, glyph_run *new_glyph_run)
18 bool b_ret;
20 // identity matrix
21 memset(&_matrix, 0, sizeof(MAT2));
22 _matrix.eM11.value = 1;
23 _matrix.eM22.value = 1;
26 GetGlyphOutline is capable of returning cubic B¨¦zier curves
27 although it generally require less points to define a curve with cubic rather than quadratic B¨¦zier curves,
28 TrueType fonts internally store curves with quadratic B¨¦zier curves
29 GetGlyphOutline has to do conversion, which takes time, and generates more points
30 therefore, quadratic B¨¦zier curves are more favored
32 _ggo_format = GGO_NATIVE;
33 if (is_glyph_index)
34 _ggo_format |= GGO_GLYPH_INDEX;
36 if (_session->render_config->hinting == 0)
37 _ggo_format |= GGO_UNHINTED;
39 POINT pen_pos = {};
41 for (UINT i = 0; i < c; ++i)
43 GLYPHMETRICS glyph_metrics = {};
44 FT_Glyph new_glyph;
46 // we do not care about non-printable characters
47 // solution for Windows Vista/7 Date glitch
48 if (is_glyph_index || !iswcntrl(lpString[i]))
50 const glyph_cache::char_id_type char_id = glyph_cache::get_char_id(_session->render_trait, lpString[i], is_glyph_index);
51 new_glyph = glyph_cache_instance.lookup_glyph(char_id);
52 if (new_glyph == NULL)
54 new_glyph = outline_to_bitmap(lpString[i], glyph_metrics);
55 glyph_cache_instance.store_glyph(char_id, new_glyph);
57 else
59 b_ret = get_glyph_metrics(lpString[i], glyph_metrics);
60 if (!b_ret)
61 return b_ret;
65 FT_Int glyph_left = 0, glyph_width = 0;
67 if (new_glyph != NULL)
69 const FT_BitmapGlyph bmp_glyph = reinterpret_cast<FT_BitmapGlyph>(new_glyph);
70 glyph_left = bmp_glyph->left;
71 glyph_width = get_glyph_bmp_width(bmp_glyph->bitmap);
74 RECT ctrl_box, black_box;
75 ctrl_box.left = pen_pos.x;
76 ctrl_box.top = pen_pos.y;
77 black_box.left = ctrl_box.left + glyph_left;
78 black_box.top = ctrl_box.top;
80 pen_pos.x += glyph_metrics.gmCellIncX;
81 pen_pos.y += glyph_metrics.gmCellIncY;
83 ctrl_box.right = pen_pos.x;
84 ctrl_box.bottom = pen_pos.y;
85 black_box.right = black_box.left + glyph_width;
86 black_box.bottom = ctrl_box.bottom;
88 new_glyph_run->glyphs.push_back(new_glyph);
89 new_glyph_run->ctrl_boxes.push_back(ctrl_box);
90 new_glyph_run->black_boxes.push_back(black_box);
93 return true;
96 void ggo_renderer::outline_ggo_to_ft(DWORD ggo_outline_buf_len, const BYTE *ggo_outline_buf, std::vector<FT_Vector> &curve_points, std::vector<char> &curve_tags, std::vector<short> &contour_indices)
98 // parse outline coutours
99 DWORD header_off = 0;
102 const BYTE *header_ptr = ggo_outline_buf + header_off;
103 const TTPOLYGONHEADER *header = reinterpret_cast<const TTPOLYGONHEADER *>(header_ptr);
105 // FreeType uses 26.6 format, while Windows gives logical units
106 const FT_Vector start_point = {fixed_to_26dot6(header->pfxStart.x), fixed_to_26dot6(header->pfxStart.y)};
108 DWORD curve_off = sizeof(TTPOLYGONHEADER);
109 while (curve_off < header->cb)
111 // the starting point of each curve is the last point of the previous curve or the starting point of the contour
112 if (curve_off == sizeof(TTPOLYGONHEADER))
114 curve_points.push_back(start_point);
115 // the first point is on the curve
116 curve_tags.push_back(FT_CURVE_TAG_ON);
119 const TTPOLYCURVE *curve = reinterpret_cast<const TTPOLYCURVE *>(header_ptr + curve_off);
120 char curve_tag;
121 switch (curve->wType)
123 case TT_PRIM_LINE:
124 curve_tag = FT_CURVE_TAG_ON;
125 break;
126 case TT_PRIM_QSPLINE:
127 curve_tag = FT_CURVE_TAG_CONIC;
128 break;
129 case TT_PRIM_CSPLINE:
130 curve_tag = FT_CURVE_TAG_CUBIC;
131 break;
134 for (int j = 0; j < curve->cpfx; ++j)
136 const FT_Vector curr_point = {fixed_to_26dot6(curve->apfx[j].x), fixed_to_26dot6(curve->apfx[j].y)};
137 curve_points.push_back(curr_point);
138 curve_tags.push_back(curve_tag);
140 // the last point is on the curve
141 curve_tags[curve_tags.size() - 1] = FT_CURVE_TAG_ON;
143 curve_off += sizeof(TTPOLYCURVE) + (curve->cpfx - 1) * sizeof(POINTFX);
146 contour_indices.push_back(static_cast<short>(curve_points.size() - 1));
147 header_off += header->cb;
148 } while (header_off < ggo_outline_buf_len);
150 assert(curve_points.size() <= FT_OUTLINE_POINTS_MAX);
153 bool ggo_renderer::get_glyph_metrics(wchar_t ch, GLYPHMETRICS &glyph_metrics) const
155 DWORD outline_buf_len = GetGlyphOutline(_session->font_holder, ch, (_ggo_format | GGO_METRICS), &glyph_metrics, 0, NULL, &_matrix);
156 return (outline_buf_len != GDI_ERROR);
159 const FT_Glyph ggo_renderer::outline_to_bitmap(wchar_t ch, GLYPHMETRICS &glyph_metrics) const
161 bool b_ret;
162 FT_Error ft_error;
164 FT_OutlineGlyphRec outline_glyph = {*empty_outline_glyph, {}};
165 outline_glyph.root.format = FT_GLYPH_FORMAT_OUTLINE;
167 DWORD outline_buf_len = GetGlyphOutline(_session->font_holder, ch, _ggo_format, &glyph_metrics, 0, NULL, &_matrix);
168 assert(outline_buf_len != GDI_ERROR);
170 if (outline_buf_len == 0)
172 // the glyph outline of this character is empty (e.g. space)
173 b_ret = get_glyph_metrics(ch, glyph_metrics);
174 assert(b_ret);
176 return NULL;
178 else
180 BYTE *outline_buf = new BYTE[outline_buf_len];
181 outline_buf_len = GetGlyphOutline(_session->font_holder, ch, _ggo_format, &glyph_metrics, outline_buf_len, outline_buf, &_matrix);
182 assert(outline_buf_len != GDI_ERROR);
184 std::vector<FT_Vector> curve_points;
185 std::vector<char> curve_tags;
186 std::vector<short> contour_indices;
187 outline_ggo_to_ft(outline_buf_len, outline_buf, curve_points, curve_tags, contour_indices);
189 delete[] outline_buf;
191 outline_glyph.outline.n_contours = static_cast<short>(contour_indices.size());
192 outline_glyph.outline.n_points = static_cast<short>(curve_points.size());
193 outline_glyph.outline.points = &curve_points[0];
194 outline_glyph.outline.tags = &curve_tags[0];
195 outline_glyph.outline.contours = &contour_indices[0];
196 outline_glyph.outline.flags = FT_OUTLINE_NONE;
199 once in possess of FT_Outline, there are several way to get FT_Bitmap
201 1. FT_Outline_Render: could pass a callback span function to directly draw scanlines to DC
202 unfortunately it only output 8-bit bitmap
203 2. FT_Outline_Get_Bitmap: merely a wrapper of FT_Outline_Render
204 3. FT_Glyph_To_Bitmap: first conglyph_indicesuct FT_OutlineGlyph from FT_Outline, then render glyph to get FT_Bitmap
205 when conglyph_indicesucting FreeType glyph, the private clazz field must be provided
206 support 24-bit bitmap
208 we use method 3
211 if (_session->render_config->embolden != 0)
213 ft_error = FT_Outline_Embolden(&outline_glyph.outline, _session->render_config->embolden);
214 assert(ft_error == 0);
217 // convert outline to bitmap
218 FT_Glyph generic_glyph = reinterpret_cast<FT_Glyph>(&outline_glyph);
221 // the FreeType function seems not thread-safe
222 const scoped_rw_lock lock_w(scoped_rw_lock::SERVER_FREETYPE, false);
223 ft_error = FT_Glyph_To_Bitmap(&generic_glyph, _session->render_mode, NULL, false);
224 if (ft_error != 0)
225 return NULL;
228 return generic_glyph;