Use One-Time Initialization for glyph run caching to avoid duplicate glyph run genera...
[gdipp.git] / gdipp_server / rpc_server.cpp
blob63314b21ca5c410af41cf240c2c9a587507d6c34
1 #include "stdafx.h"
2 #include "rpc_server.h"
3 #include "gdipp_lib/helper.h"
4 #include "gdipp_lib/scoped_rw_lock.h"
5 #include "gdipp_lib/scoped_rw_lock.h"
6 #include "gdipp_rpc/gdipp_rpc.h"
7 #include "gdipp_server/freetype.h"
8 #include "gdipp_server/ft_renderer.h"
9 #include "gdipp_server/ggo_renderer.h"
10 #include "gdipp_server/global.h"
11 #include "gdipp_server/helper.h"
13 namespace gdipp
16 HANDLE process_heap = GetProcessHeap();
19 // rpc index functions
21 bool rpc_index_initialize()
23 int i_ret;
25 i_ret = sqlite3_initialize();
26 if (i_ret != SQLITE_OK)
27 return false;
29 // open SQLite database in memory
30 i_ret = sqlite3_open(":memory:", &index_db_instance);
31 if (i_ret != SQLITE_OK)
32 return false;
34 // create index tables
35 i_ret = sqlite3_exec(index_db_instance, "CREATE TABLE session_index ('session_address' INTEGER NOT NULL)", NULL, NULL, NULL);
36 if (i_ret != SQLITE_OK)
37 return false;
39 i_ret = sqlite3_exec(index_db_instance, "CREATE TABLE glyph_run_index ('glyph_run_address' INTEGER NOT NULL)", NULL, NULL, NULL);
40 if (i_ret != SQLITE_OK)
41 return false;
43 return true;
46 bool rpc_index_shutdown()
48 return (sqlite3_shutdown() == SQLITE_OK);
51 const rpc_session *rpc_index_lookup_session(GDIPP_RPC_SESSION_HANDLE h_session)
53 int i_ret;
55 const int session_id = reinterpret_cast<int>(h_session);
56 const rpc_session *curr_session = NULL;
58 sqlite3_stmt *select_stmt;
60 i_ret = sqlite3_prepare_v2(index_db_instance, "SELECT session_address FROM session_index WHERE ROWID = ?", -1, &select_stmt, NULL);
61 assert(i_ret == SQLITE_OK);
63 i_ret = sqlite3_bind_int(select_stmt, 0, session_id);
64 assert(i_ret == SQLITE_OK);
66 i_ret = sqlite3_step(select_stmt);
67 if (i_ret == SQLITE_ROW)
69 #ifdef _M_X64
70 curr_session = reinterpret_cast<const rpc_session *>(sqlite3_column_int64(select_stmt, 0));
71 #else
72 curr_session = reinterpret_cast<const rpc_session *>(sqlite3_column_int(select_stmt, 0));
73 #endif
76 i_ret = sqlite3_finalize(select_stmt);
77 assert(i_ret == SQLITE_OK);
79 return curr_session;
82 const glyph_run *rpc_index_lookup_glyph_run(GDIPP_RPC_GLYPH_RUN_HANDLE h_glyph_run)
84 int i_ret;
86 const int glyph_run_id = reinterpret_cast<int>(h_glyph_run);
87 const glyph_run *curr_glyph_run = NULL;
89 sqlite3_stmt *select_stmt;
91 i_ret = sqlite3_prepare_v2(index_db_instance, "SELECT glyph_run_address FROM glyph_run_index WHERE ROWID = ?", -1, &select_stmt, NULL);
92 assert(i_ret == SQLITE_OK);
94 i_ret = sqlite3_bind_int(select_stmt, 0, glyph_run_id);
95 assert(i_ret == SQLITE_OK);
97 i_ret = sqlite3_step(select_stmt);
98 if (i_ret == SQLITE_ROW)
100 #ifdef _M_X64
101 curr_glyph_run = reinterpret_cast<const glyph_run *>(sqlite3_column_int64(select_stmt, 0));
102 #else
103 curr_glyph_run = reinterpret_cast<const glyph_run *>(sqlite3_column_int(select_stmt, 0));
104 #endif
107 i_ret = sqlite3_finalize(select_stmt);
108 assert(i_ret == SQLITE_OK);
110 return curr_glyph_run;
113 DWORD WINAPI start_gdipp_rpc_server(LPVOID lpParameter)
115 if (process_heap == NULL)
116 return 1;
118 //bool b_ret;
119 RPC_STATUS rpc_status;
121 scoped_rw_lock::initialize();
122 server_cache_size = min(config_instance.get_number(L"/gdipp/server/cache_size/text()", server_config::CACHE_SIZE), 24);
123 glyph_cache_instance.initialize();
124 initialize_freetype();
126 //b_ret = rpc_index_initialize();
127 //if (!b_ret)
128 // return 1;
130 rpc_status = RpcServerUseProtseqEpW(reinterpret_cast<RPC_WSTR>(L"ncalrpc"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, reinterpret_cast<RPC_WSTR>(L"gdipp"), NULL);
131 if (rpc_status != RPC_S_OK)
132 return 1;
134 rpc_status = RpcServerRegisterIf(gdipp_rpc_v1_0_s_ifspec, NULL, NULL);
135 if (rpc_status != RPC_S_OK)
136 return 1;
138 rpc_status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
139 if (rpc_status != RPC_S_OK)
140 return 1;
142 rpc_status = RpcMgmtWaitServerListen();
143 if (rpc_status != RPC_S_OK)
144 return 1;
146 return 0;
149 bool stop_gdipp_rpc_server()
151 //bool b_ret;
152 RPC_STATUS rpc_status;
154 rpc_status = RpcMgmtStopServerListening(NULL);
155 if (rpc_status != RPC_S_OK)
156 return false;
158 //b_ret = rpc_index_shutdown();
159 //assert(b_ret);
161 destroy_freetype();
163 return true;
168 void __RPC_FAR *__RPC_USER MIDL_user_allocate(size_t size)
170 return HeapAlloc(gdipp::process_heap, HEAP_GENERATE_EXCEPTIONS, size);
173 void __RPC_USER MIDL_user_free(void __RPC_FAR *ptr)
175 HeapFree(gdipp::process_heap, 0, ptr);
178 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_begin_session(
179 /* [in] */ handle_t h_gdipp_rpc,
180 /* [size_is][in] */ const byte *logfont_buf,
181 /* [in] */ unsigned long logfont_size,
182 /* [in] */ unsigned short bits_per_pixel,
183 /* [out] */ GDIPP_RPC_SESSION_HANDLE *h_session)
185 if (logfont_size != sizeof(LOGFONTW))
186 return RPC_S_INVALID_ARG;
188 const HDC session_font_holder = gdipp::dc_pool_instance.claim();
189 assert(session_font_holder != NULL);
191 // register font with given LOGFONT structure
192 const LOGFONTW *logfont = reinterpret_cast<const LOGFONTW *>(logfont_buf);
193 BYTE *outline_metrics_buf;
194 unsigned long outline_metrics_size;
196 void *session_font_id = gdipp::font_mgr_instance.register_font(session_font_holder, logfont, &outline_metrics_buf, &outline_metrics_size);
197 if (session_font_id == NULL)
199 gdipp::dc_pool_instance.free(session_font_holder);
200 return RPC_S_INVALID_ARG;
203 const OUTLINETEXTMETRICW *outline_metrics = reinterpret_cast<const OUTLINETEXTMETRICW *>(outline_metrics_buf);
204 // generate config trait and retrieve font-specific config
205 const LONG point_size = (logfont->lfHeight > 0 ? logfont->lfHeight : -MulDiv(logfont->lfHeight, 72, outline_metrics->otmTextMetrics.tmDigitizedAspectY));
206 const char weight_class = gdipp::get_gdi_weight_class(static_cast<unsigned short>(outline_metrics->otmTextMetrics.tmWeight));
207 const gdipp::render_config_static *session_render_config = gdipp::font_render_config_cache_instance.get_font_render_config(!!weight_class,
208 !!outline_metrics->otmTextMetrics.tmItalic,
209 point_size,
210 metric_face_name(outline_metrics));
211 if (session_render_config->renderer == gdipp::server_config::RENDERER_CLEARTYPE)
213 gdipp::dc_pool_instance.free(session_font_holder);
214 return RPC_S_OK;
217 FT_Render_Mode session_render_mode;
218 if (!gdipp::get_render_mode(session_render_config->render_mode, bits_per_pixel, logfont->lfQuality, &session_render_mode))
220 gdipp::dc_pool_instance.free(session_font_holder);
221 return RPC_S_INVALID_ARG;
224 gdipp::rpc_session *new_session = reinterpret_cast<gdipp::rpc_session *>(MIDL_user_allocate(sizeof(gdipp::rpc_session)));
226 new_session->bits_per_pixel = bits_per_pixel;
227 new_session->font_holder = session_font_holder;
228 new_session->font_id = session_font_id;
229 new_session->log_font = *reinterpret_cast<const LOGFONTW *>(logfont_buf);
230 new_session->outline_metrics_buf = outline_metrics_buf;
231 new_session->outline_metrics_size = outline_metrics_size;
232 new_session->render_config = session_render_config;
233 new_session->render_mode = session_render_mode;
234 new_session->render_trait = gdipp::generate_render_trait(logfont, new_session->render_mode);
236 // create session renderer
237 switch (session_render_config->renderer)
239 case gdipp::server_config::RENDERER_DIRECTWRITE:
240 //break;
241 case gdipp::server_config::RENDERER_FREETYPE:
242 new_session->renderer = new gdipp::ft_renderer(new_session);
243 break;
244 case gdipp::server_config::RENDERER_GETGLYPHOUTLINE:
245 new_session->renderer = new gdipp::ggo_renderer(new_session);
246 break;
247 case gdipp::server_config::RENDERER_WIC:
248 break;
249 default:
250 break;
253 *h_session = reinterpret_cast<GDIPP_RPC_SESSION_HANDLE>(new_session);
254 return RPC_S_OK;
257 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_size(
258 /* [in] */ handle_t h_gdipp_rpc,
259 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
260 /* [in] */ unsigned long table,
261 /* [in] */ unsigned long offset,
262 /* [out] */ unsigned long *font_size)
264 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
265 *font_size = GetFontData(curr_session->font_holder, table, offset, NULL, 0);
267 return RPC_S_OK;
270 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_data(
271 /* [in] */ handle_t h_gdipp_rpc,
272 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
273 /* [in] */ unsigned long table,
274 /* [in] */ unsigned long offset,
275 /* [size_is][out] */ byte *data_buf,
276 /* [in] */ unsigned long buf_size)
278 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
280 // TODO: output pointer is not allocated with MIDL_user_allocate
281 // TODO: return value not returned
282 GetFontData(curr_session->font_holder, table, offset, data_buf, buf_size);
284 return RPC_S_OK;
287 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_metrics_size(
288 /* [in] */ handle_t h_gdipp_rpc,
289 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
290 /* [out] */ unsigned long *metrics_size)
292 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
293 *metrics_size = curr_session->outline_metrics_size;
295 return RPC_S_OK;
298 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_metrics_data(
299 /* [in] */ handle_t h_gdipp_rpc,
300 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
301 /* [size_is][out] */ byte *metrics_buf,
302 /* [in] */ unsigned long buf_size)
304 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
305 const DWORD copy_size = min(curr_session->outline_metrics_size, buf_size);
306 CopyMemory(metrics_buf, curr_session->outline_metrics_buf, copy_size);
308 return RPC_S_OK;
311 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_glyph_indices(
312 /* [in] */ handle_t h_gdipp_rpc,
313 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
314 /* [size_is][string][in] */ const wchar_t *str,
315 /* [in] */ int count,
316 /* [size_is][out] */ unsigned short *gi)
318 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
320 // TODO: output pointer is not allocated with MIDL_user_allocate
321 // TODO: return value not returned
322 GetGlyphIndices(curr_session->font_holder, str, count, gi, GGI_MARK_NONEXISTING_GLYPHS);
324 return RPC_S_OK;
327 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_make_bitmap_glyph_run(
328 /* [in] */ handle_t h_gdipp_rpc,
329 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
330 /* [string][in] */ const wchar_t *string,
331 /* [in] */ unsigned int count,
332 /* [in] */ boolean is_glyph_index,
333 /* [out] */ gdipp_rpc_bitmap_glyph_run *glyph_run_ptr)
335 if (count == 0)
336 return RPC_S_INVALID_ARG;
338 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
339 bool b_ret;
341 // generate unique identifier for the string
342 const gdipp::glyph_cache::string_id_type string_id = gdipp::glyph_cache::get_string_id(string, count, !!is_glyph_index);
344 // check if a glyph run cached for the same rendering environment and string
345 const gdipp::glyph_run *glyph_run = gdipp::glyph_cache_instance.lookup_glyph_run(string_id, curr_session->render_trait);
346 if (!glyph_run)
348 // no cached glyph run. render new glyph run
349 gdipp::glyph_run *new_glyph_run = new gdipp::glyph_run();
350 b_ret = curr_session->renderer->render(!!is_glyph_index, string, count, new_glyph_run);
351 if (!b_ret)
352 return RPC_S_OUT_OF_MEMORY;
354 // and cache it
355 gdipp::glyph_cache_instance.store_glyph_run(string_id, curr_session->render_trait, new_glyph_run);
356 glyph_run = new_glyph_run;
359 // convert internal glyph run to RPC exchangable format
361 // allocate space for glyph run
362 glyph_run_ptr->count = static_cast<UINT>(glyph_run->glyphs.size());
363 glyph_run_ptr->glyphs = reinterpret_cast<gdipp_rpc_bitmap_glyph *>(MIDL_user_allocate(sizeof(gdipp_rpc_bitmap_glyph) * glyph_run_ptr->count));
364 glyph_run_ptr->ctrl_boxes = reinterpret_cast<RECT *>(MIDL_user_allocate(sizeof(RECT) * glyph_run_ptr->count));
365 glyph_run_ptr->black_boxes = reinterpret_cast<RECT *>(MIDL_user_allocate(sizeof(RECT) * glyph_run_ptr->count));
366 glyph_run_ptr->render_mode = curr_session->render_mode;
368 for (unsigned int i = 0; i < glyph_run_ptr->count; ++i)
370 glyph_run_ptr->ctrl_boxes[i] = glyph_run->ctrl_boxes[i];
371 glyph_run_ptr->black_boxes[i] = glyph_run->black_boxes[i];
373 if (glyph_run->glyphs[i] == NULL)
375 glyph_run_ptr->glyphs[i].buffer = NULL;
376 continue;
379 const FT_BitmapGlyph bmp_glyph = reinterpret_cast<const FT_BitmapGlyph>(glyph_run->glyphs[i]);
380 glyph_run_ptr->glyphs[i].left = bmp_glyph->left;
381 glyph_run_ptr->glyphs[i].top = bmp_glyph->top;
382 glyph_run_ptr->glyphs[i].rows = bmp_glyph->bitmap.rows;
383 glyph_run_ptr->glyphs[i].width = bmp_glyph->bitmap.width;
384 glyph_run_ptr->glyphs[i].pitch = bmp_glyph->bitmap.pitch;
385 const int buffer_size = bmp_glyph->bitmap.rows * abs(bmp_glyph->bitmap.pitch);
386 glyph_run_ptr->glyphs[i].buffer = reinterpret_cast<byte *>(MIDL_user_allocate(buffer_size));
387 memcpy(glyph_run_ptr->glyphs[i].buffer, bmp_glyph->bitmap.buffer, buffer_size);
390 return RPC_S_OK;
393 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_make_outline_glyph_run(
394 /* [in] */ handle_t h_gdipp_rpc,
395 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
396 /* [string][in] */ const wchar_t *string,
397 /* [in] */ unsigned int count,
398 /* [in] */ boolean is_glyph_index,
399 /* [out] */ gdipp_rpc_outline_glyph_run *glyph_run_ptr)
401 return ERROR_CALL_NOT_IMPLEMENTED;
404 error_status_t gdipp_rpc_end_session(
405 /* [in] */ handle_t h_gdipp_rpc,
406 /* [out][in] */ GDIPP_RPC_SESSION_HANDLE *h_session)
408 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(*h_session);
409 if (curr_session == NULL)
410 return RPC_S_INVALID_ARG;
412 delete[] curr_session->outline_metrics_buf;
413 delete curr_session->renderer;
414 gdipp::dc_pool_instance.free(curr_session->font_holder);
415 MIDL_user_free(*h_session);
417 *h_session = NULL;
418 return RPC_S_OK;
421 void __RPC_USER GDIPP_RPC_SESSION_HANDLE_rundown(GDIPP_RPC_SESSION_HANDLE h_session)
423 error_status_t e = gdipp_rpc_end_session(NULL, &h_session);
424 assert(e == 0);