2 #include "ggo_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 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
)
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
;
34 _ggo_format
|= GGO_GLYPH_INDEX
;
36 if (_session
->render_config
->hinting
== 0)
37 _ggo_format
|= GGO_UNHINTED
;
41 for (UINT i
= 0; i
< c
; ++i
)
43 GLYPHMETRICS glyph_metrics
= {};
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
);
59 b_ret
= get_glyph_metrics(lpString
[i
], glyph_metrics
);
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
);
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
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
);
121 switch (curve
->wType
)
124 curve_tag
= FT_CURVE_TAG_ON
;
126 case TT_PRIM_QSPLINE
:
127 curve_tag
= FT_CURVE_TAG_CONIC
;
129 case TT_PRIM_CSPLINE
:
130 curve_tag
= FT_CURVE_TAG_CUBIC
;
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
->hdc
, 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
164 FT_OutlineGlyphRec outline_glyph
= {*empty_outline_glyph
, {}};
165 outline_glyph
.root
.format
= FT_GLYPH_FORMAT_OUTLINE
;
167 DWORD outline_buf_len
= GetGlyphOutline(_session
->hdc
, 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
);
180 BYTE
*outline_buf
= new BYTE
[outline_buf_len
];
181 outline_buf_len
= GetGlyphOutline(_session
->hdc
, 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
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 lock
l(lock::SERVER_FREETYPE
);
223 ft_error
= FT_Glyph_To_Bitmap(&generic_glyph
, _session
->render_mode
, NULL
, false);
228 return generic_glyph
;