Use One-Time Initialization for glyph run caching to avoid duplicate glyph run genera...
[gdipp.git] / gdipp_server / dw_renderer.cpp
blob28161d79bcd482cc3c3ad332e744cb9a46d923ec
1 #include "stdafx.h"
2 #include "dw_renderer.h"
3 #include "helper_func.h"
4 #include "gdimm.h"
6 IDWriteFactory *dw_renderer::_dw_factory = NULL;
7 IDWriteGdiInterop *dw_renderer::_dw_gdi_interop = NULL;
9 dw_renderer::dw_renderer()
11 HRESULT hr;
13 if (_dw_factory == NULL)
15 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&_dw_factory));
16 assert(hr == S_OK);
18 mem_man_instance.register_com_ptr(_dw_factory);
21 if (_dw_gdi_interop == NULL)
23 hr = _dw_factory->GetGdiInterop(&_dw_gdi_interop);
24 assert(hr == S_OK);
26 mem_man_instance.register_com_ptr(_dw_gdi_interop);
30 bool dw_renderer::begin(const dc_context *context, FT_Render_Mode render_mode)
32 if (!renderer::begin(context, render_mode))
33 return false;
35 // ignore rotated DC
36 if (_context->log_font.lfEscapement % 3600 != 0)
37 return false;
39 switch (_context->setting_cache->hinting)
41 case 2:
42 _dw_measuring_mode = DWRITE_MEASURING_MODE_GDI_NATURAL;
43 break;
44 case 3:
45 _dw_measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
46 break;
47 default:
48 _dw_measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
49 break;
51 _use_gdi_natural = (_dw_measuring_mode != DWRITE_MEASURING_MODE_GDI_CLASSIC);
53 _advances.clear();
54 _em_size = static_cast<FLOAT>(_context->outline_metrics->otmTextMetrics.tmHeight - _context->outline_metrics->otmTextMetrics.tmInternalLeading);
55 _pixels_per_dip = GetDeviceCaps(_context->hdc, LOGPIXELSY) / 96.0f;
57 return true;
60 //////////////////////////////////////////////////////////////////////////
62 bool dw_renderer::make_glyph_texture(FLOAT x, FLOAT y, const DWRITE_GLYPH_RUN *dw_glyph_run, glyph_run *a_glyph_run)
64 HRESULT hr;
66 DWRITE_RENDERING_MODE dw_render_mode;
67 if (_render_mode == FT_RENDER_MODE_LCD)
69 switch (_context->setting_cache->hinting)
71 case 0:
72 dw_render_mode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
73 break;
74 case 2:
75 dw_render_mode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;
76 break;
77 case 3:
78 dw_render_mode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
79 break;
80 default:
81 dw_render_mode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
82 break;
85 else
86 dw_render_mode = DWRITE_RENDERING_MODE_ALIASED;
88 DWRITE_TEXTURE_TYPE dw_texture_type;
89 int bytes_per_pixel;
90 char ft_pixel_mode;
91 if (_render_mode == FT_RENDER_MODE_MONO)
92 return false;
93 else if (_render_mode == FT_RENDER_MODE_LCD)
95 dw_texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
96 bytes_per_pixel = 3;
97 ft_pixel_mode = FT_PIXEL_MODE_LCD;
99 else
101 dw_texture_type = DWRITE_TEXTURE_ALIASED_1x1;
102 bytes_per_pixel = 1;
103 ft_pixel_mode = FT_PIXEL_MODE_GRAY;
106 CComPtr<IDWriteGlyphRunAnalysis> dw_analysis;
107 hr = _dw_factory->CreateGlyphRunAnalysis(dw_glyph_run,
108 _pixels_per_dip,
109 NULL,
110 dw_render_mode,
111 _dw_measuring_mode,
114 &dw_analysis);
115 assert(hr == S_OK);
117 RECT texture_rect;
118 hr = dw_analysis->GetAlphaTextureBounds(dw_texture_type, &texture_rect);
119 assert(hr == S_OK);
121 if (IsRectEmpty(&texture_rect))
122 return false;
124 const LONG texture_width = texture_rect.right - texture_rect.left;
125 const LONG texture_height = texture_rect.bottom - texture_rect.top;
127 FT_BitmapGlyph new_bmp_glyph = new FT_BitmapGlyphRec();
128 new_bmp_glyph->left = texture_rect.left;
129 new_bmp_glyph->top = -texture_rect.top;
130 new_bmp_glyph->bitmap.rows = texture_rect.bottom - texture_rect.top;
131 new_bmp_glyph->bitmap.width = texture_width * bytes_per_pixel;
132 new_bmp_glyph->bitmap.pitch = new_bmp_glyph->bitmap.width;
133 new_bmp_glyph->bitmap.pixel_mode = ft_pixel_mode;
135 const int bmp_size = new_bmp_glyph->bitmap.pitch * new_bmp_glyph->bitmap.rows;
136 new_bmp_glyph->bitmap.buffer = new BYTE[bmp_size];
137 hr = dw_analysis->CreateAlphaTexture(dw_texture_type, &texture_rect, new_bmp_glyph->bitmap.buffer, bmp_size);
138 assert(hr == S_OK);
140 RECT ctrl_box, black_box;
141 ctrl_box.left = static_cast<LONG>(x);
142 ctrl_box.top = static_cast<LONG>(y);
143 ctrl_box.right = texture_width;
144 ctrl_box.bottom = ctrl_box.top;
145 black_box.left = ctrl_box.left + texture_rect.left;
146 black_box.top = ctrl_box.top;
147 black_box.right = black_box.left + texture_width;
148 black_box.bottom = ctrl_box.bottom;
150 a_glyph_run->glyphs.push_back(reinterpret_cast<FT_Glyph>(new_bmp_glyph));
151 a_glyph_run->ctrl_boxes.push_back(ctrl_box);
152 a_glyph_run->black_boxes.push_back(black_box);
154 return true;
157 bool dw_renderer::render_glyph(LPCWSTR lpString, UINT c, glyph_run &new_glyph_run)
159 HRESULT hr;
161 CComPtr<IDWriteFontFace> dw_font_face;
162 hr = _dw_gdi_interop->CreateFontFaceFromHdc(_context->hdc, &dw_font_face);
163 assert(hr == S_OK);
165 DWRITE_GLYPH_RUN dw_glyph_run;
166 dw_glyph_run.fontFace = dw_font_face;
167 dw_glyph_run.fontEmSize = _em_size;
168 dw_glyph_run.glyphCount = c;
169 dw_glyph_run.glyphIndices = reinterpret_cast<const UINT16 *>(lpString);
170 dw_glyph_run.glyphAdvances = (_advances.empty() ? NULL : _advances.data());
171 dw_glyph_run.glyphOffsets = NULL;
172 dw_glyph_run.isSideways = FALSE;
173 dw_glyph_run.bidiLevel = 0;
175 return make_glyph_texture(0, 0, &dw_glyph_run, &new_glyph_run);
178 bool dw_renderer::render_text(LPCWSTR lpString, UINT c, glyph_run &new_glyph_run)
180 HRESULT hr;
182 const long font_id = font_man_instance.register_font(_context->hdc, metric_face_name(_context->outline_metrics));
183 assert(font_id < 0);
184 const os2_metrics *os2_metrics = font_man_instance.lookup_os2_metrics(font_id);
186 DWRITE_FONT_STYLE dw_font_style;
187 if (!_context->outline_metrics->otmTextMetrics.tmItalic)
188 dw_font_style = DWRITE_FONT_STYLE_NORMAL;
189 else if (os2_metrics->is_italic())
190 dw_font_style = DWRITE_FONT_STYLE_ITALIC;
191 else
192 dw_font_style = DWRITE_FONT_STYLE_OBLIQUE;
194 CComPtr<IDWriteTextFormat> dw_text_format;
195 hr = _dw_factory->CreateTextFormat(metric_family_name(_context->outline_metrics),
196 NULL,
197 static_cast<DWRITE_FONT_WEIGHT>(_context->outline_metrics->otmTextMetrics.tmWeight),
198 dw_font_style,
199 static_cast<DWRITE_FONT_STRETCH>(os2_metrics->get_usWidthClass()),
200 _em_size,
201 L"",
202 &dw_text_format);
203 assert(hr == S_OK);
205 hr = dw_text_format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
206 assert(hr == S_OK);
208 CComPtr<IDWriteTextLayout> dw_text_layout;
209 if (_dw_measuring_mode == DWRITE_MEASURING_MODE_NATURAL)
211 hr = _dw_factory->CreateTextLayout(lpString,
213 dw_text_format,
214 static_cast<FLOAT>(_context->bmp_header.biWidth),
216 &dw_text_layout);
218 else
220 hr = _dw_factory->CreateGdiCompatibleTextLayout(lpString,
222 dw_text_format,
223 static_cast<FLOAT>(_context->bmp_header.biWidth),
225 _pixels_per_dip,
226 NULL,
227 _use_gdi_natural,
228 &dw_text_layout);
230 assert(hr == S_OK);
232 UINT glyph_run_start = 0;
233 bool draw_success;
234 void *drawing_context[3] = {&new_glyph_run, &glyph_run_start, &draw_success};
235 hr = dw_text_layout->Draw(drawing_context, this, 0, 0);
236 assert(hr == S_OK);
237 assert(glyph_run_start == c);
239 return draw_success;
242 bool dw_renderer::render(bool is_glyph_index, bool is_pdy, LPCWSTR lpString, UINT c, CONST INT *lpDx, glyph_run &new_glyph_run)
244 if (lpDx != NULL)
246 const BYTE dx_skip = (is_pdy ? 2 : 1);
247 _advances.resize(c);
248 for (UINT i = 0; i < c; ++i)
249 _advances[i] = static_cast<FLOAT>(lpDx[i * dx_skip]);// - 0.1f; // small adjustment to emulate GDI metrics
252 if (is_glyph_index)
253 return render_glyph(lpString, c, new_glyph_run);
254 else
255 return render_text(lpString, c, new_glyph_run);
259 // IUnknown methods
261 // These methods are never called in this scenario so we just use stub
262 // implementations.
264 IFACEMETHODIMP dw_renderer::QueryInterface(
265 /* [in] */ REFIID riid,
266 /* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject
269 return E_NOTIMPL;
272 IFACEMETHODIMP_(ULONG) dw_renderer::AddRef( void)
274 return 0;
277 IFACEMETHODIMP_(ULONG) dw_renderer::Release( void)
279 return 0;
282 /******************************************************************
284 * dw_renderer::IsPixelSnappingDisabled *
286 * Determines whether pixel snapping is disabled. The recommended *
287 * default is FALSE, unless doing animation that requires *
288 * subpixel vertical placement. *
290 ******************************************************************/
292 IFACEMETHODIMP dw_renderer::IsPixelSnappingDisabled(
293 __maybenull void* clientDrawingContext,
294 __out BOOL* isDisabled
297 *isDisabled = FALSE;
299 return S_OK;
302 /******************************************************************
304 * dw_renderer::GetCurrentTransform *
306 * Returns the current transform applied to the render target.. *
308 ******************************************************************/
310 IFACEMETHODIMP dw_renderer::GetCurrentTransform(
311 __maybenull void* clientDrawingContext,
312 __out DWRITE_MATRIX* transform
315 transform = NULL;
317 return S_OK;
320 /******************************************************************
322 * dw_renderer::GetPixelsPerDip *
324 * This returns the number of pixels per DIP. *
326 ******************************************************************/
328 IFACEMETHODIMP dw_renderer::GetPixelsPerDip(
329 __maybenull void* clientDrawingContext,
330 __out FLOAT* pixelsPerDip
333 *pixelsPerDip = _pixels_per_dip;
335 return S_OK;
338 /******************************************************************
340 * dw_renderer::DrawGlyphRun *
342 * Gets GlyphRun outlines via IDWriteFontFace::GetGlyphRunOutline *
343 * and then draws and fills them using Direct2D path geometries *
345 ******************************************************************/
347 IFACEMETHODIMP dw_renderer::DrawGlyphRun(
348 __maybenull void* clientDrawingContext,
349 FLOAT baselineOriginX,
350 FLOAT baselineOriginY,
351 DWRITE_MEASURING_MODE measuringMode,
352 __in DWRITE_GLYPH_RUN const* glyphRun,
353 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
354 __maybenull IUnknown* clientDrawingEffect
357 void **drawing_context = static_cast<void **>(clientDrawingContext);
358 glyph_run *new_glyph_run = static_cast<glyph_run *>(drawing_context[0]);
359 UINT *glyph_run_start = static_cast<UINT *>(drawing_context[1]);
360 bool *draw_success = static_cast<bool *>(drawing_context[2]);
362 if (_advances.empty())
363 *draw_success &= make_glyph_texture(baselineOriginX, 0, glyphRun, new_glyph_run);
364 else
366 DWRITE_GLYPH_RUN final_glyph_run = *glyphRun;
367 final_glyph_run.glyphAdvances = &_advances[*glyph_run_start];
369 *draw_success &= make_glyph_texture(baselineOriginX, 0, &final_glyph_run, new_glyph_run);
372 *glyph_run_start += glyphRunDescription->stringLength;
374 return S_OK;
377 /******************************************************************
379 * dw_renderer::DrawUnderline *
381 * This function is not implemented for the purposes of this *
382 * sample. *
384 ******************************************************************/
386 IFACEMETHODIMP dw_renderer::DrawUnderline(
387 __maybenull void* clientDrawingContext,
388 FLOAT baselineOriginX,
389 FLOAT baselineOriginY,
390 __in DWRITE_UNDERLINE const* underline,
391 __maybenull IUnknown* clientDrawingEffect
394 return E_NOTIMPL;
397 /******************************************************************
399 * dw_renderer::DrawStrikethrough *
401 * This function is not implemented for the purposes of this *
402 * sample. *
404 ******************************************************************/
406 IFACEMETHODIMP dw_renderer::DrawStrikethrough(
407 __maybenull void* clientDrawingContext,
408 FLOAT baselineOriginX,
409 FLOAT baselineOriginY,
410 __in DWRITE_STRIKETHROUGH const* strikethrough,
411 __maybenull IUnknown* clientDrawingEffect
414 return E_NOTIMPL;
417 /******************************************************************
419 * dw_renderer::DrawInlineObject *
421 * This function is not implemented for the purposes of this *
422 * sample. *
424 ******************************************************************/
426 IFACEMETHODIMP dw_renderer::DrawInlineObject(
427 __maybenull void* clientDrawingContext,
428 FLOAT originX,
429 FLOAT originY,
430 IDWriteInlineObject* inlineObject,
431 BOOL isSideways,
432 BOOL isRightToLeft,
433 __maybenull IUnknown* clientDrawingEffect
436 return E_NOTIMPL;