Basic FreeType renderer implementation.
[gdipp.git] / gdipp_server / rpc_server.cpp
bloba79e2d32aa6042eb243f5937a3f9454ef622a0e5
1 #include "stdafx.h"
2 #include "rpc_server.h"
3 #include "gdipp_lib/helper.h"
4 #include "gdipp_lib/lock.h"
5 #include "gdipp_rpc/gdipp_rpc.h"
6 #include "gdipp_server/freetype.h"
7 #include "gdipp_server/ft_renderer.h"
8 #include "gdipp_server/ggo_renderer.h"
9 #include "gdipp_server/global.h"
10 #include "gdipp_server/helper.h"
12 namespace gdipp
15 HANDLE process_heap = GetProcessHeap();
18 // rpc index functions
20 bool rpc_index_initialize()
22 int i_ret;
24 i_ret = sqlite3_initialize();
25 if (i_ret != SQLITE_OK)
26 return false;
28 // open SQLite database in memory
29 i_ret = sqlite3_open(":memory:", &index_db_instance);
30 if (i_ret != SQLITE_OK)
31 return false;
33 // create index tables
34 i_ret = sqlite3_exec(index_db_instance, "CREATE TABLE session_index ('session_address' INTEGER NOT NULL)", NULL, NULL, NULL);
35 if (i_ret != SQLITE_OK)
36 return false;
38 i_ret = sqlite3_exec(index_db_instance, "CREATE TABLE glyph_run_index ('glyph_run_address' INTEGER NOT NULL)", NULL, NULL, NULL);
39 if (i_ret != SQLITE_OK)
40 return false;
42 return true;
45 bool rpc_index_shutdown()
47 return (sqlite3_shutdown() == SQLITE_OK);
50 const rpc_session *rpc_index_lookup_session(GDIPP_RPC_SESSION_HANDLE h_session)
52 int i_ret;
54 const int session_id = reinterpret_cast<int>(h_session);
55 const rpc_session *curr_session = NULL;
57 sqlite3_stmt *select_stmt;
59 i_ret = sqlite3_prepare_v2(index_db_instance, "SELECT session_address FROM session_index WHERE ROWID = ?", -1, &select_stmt, NULL);
60 assert(i_ret == SQLITE_OK);
62 i_ret = sqlite3_bind_int(select_stmt, 0, session_id);
63 assert(i_ret == SQLITE_OK);
65 i_ret = sqlite3_step(select_stmt);
66 if (i_ret == SQLITE_ROW)
68 #ifdef _M_X64
69 curr_session = reinterpret_cast<const rpc_session *>(sqlite3_column_int64(select_stmt, 0));
70 #else
71 curr_session = reinterpret_cast<const rpc_session *>(sqlite3_column_int(select_stmt, 0));
72 #endif
75 i_ret = sqlite3_finalize(select_stmt);
76 assert(i_ret == SQLITE_OK);
78 return curr_session;
81 const glyph_run *rpc_index_lookup_glyph_run(GDIPP_RPC_GLYPH_RUN_HANDLE h_glyph_run)
83 int i_ret;
85 const int glyph_run_id = reinterpret_cast<int>(h_glyph_run);
86 const glyph_run *curr_glyph_run = NULL;
88 sqlite3_stmt *select_stmt;
90 i_ret = sqlite3_prepare_v2(index_db_instance, "SELECT glyph_run_address FROM glyph_run_index WHERE ROWID = ?", -1, &select_stmt, NULL);
91 assert(i_ret == SQLITE_OK);
93 i_ret = sqlite3_bind_int(select_stmt, 0, glyph_run_id);
94 assert(i_ret == SQLITE_OK);
96 i_ret = sqlite3_step(select_stmt);
97 if (i_ret == SQLITE_ROW)
99 #ifdef _M_X64
100 curr_glyph_run = reinterpret_cast<const glyph_run *>(sqlite3_column_int64(select_stmt, 0));
101 #else
102 curr_glyph_run = reinterpret_cast<const glyph_run *>(sqlite3_column_int(select_stmt, 0));
103 #endif
106 i_ret = sqlite3_finalize(select_stmt);
107 assert(i_ret == SQLITE_OK);
109 return curr_glyph_run;
112 DWORD WINAPI start_gdipp_rpc_server(LPVOID lpParameter)
114 if (process_heap == NULL)
115 return 1;
117 //bool b_ret;
118 RPC_STATUS rpc_status;
120 lock::initialize_locks();
121 server_cache_size = min(config_instance.get_number(L"/gdipp/server/cache_size/text()", server_config::CACHE_SIZE), 24);
122 glyph_cache_instance.initialize();
123 initialize_freetype();
125 //b_ret = rpc_index_initialize();
126 //if (!b_ret)
127 // return 1;
129 rpc_status = RpcServerUseProtseqEpW(reinterpret_cast<RPC_WSTR>(L"ncalrpc"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, reinterpret_cast<RPC_WSTR>(L"gdipp"), NULL);
130 if (rpc_status != RPC_S_OK)
131 return 1;
133 rpc_status = RpcServerRegisterIf(gdipp_rpc_v1_0_s_ifspec, NULL, NULL);
134 if (rpc_status != RPC_S_OK)
135 return 1;
137 rpc_status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
138 if (rpc_status != RPC_S_OK)
139 return 1;
141 rpc_status = RpcMgmtWaitServerListen();
142 if (rpc_status != RPC_S_OK)
143 return 1;
145 return 0;
148 bool stop_gdipp_rpc_server()
150 //bool b_ret;
151 RPC_STATUS rpc_status;
153 rpc_status = RpcMgmtStopServerListening(NULL);
154 if (rpc_status != RPC_S_OK)
155 return false;
157 //b_ret = rpc_index_shutdown();
158 //assert(b_ret);
160 destroy_freetype();
161 lock::destory_locks();
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 HDC session_hdc = gdipp::dc_pool_instance.claim();
189 if (session_hdc == NULL)
190 return RPC_S_OUT_OF_MEMORY;
192 // register font with given LOGFONT structure
193 const LOGFONTW *logfont = reinterpret_cast<const LOGFONTW *>(logfont_buf);
194 BYTE *outline_metrics_buf;
195 unsigned long outline_metrics_size;
196 void *session_font_id = gdipp::font_mgr_instance.register_font(logfont, &outline_metrics_buf, &outline_metrics_size, session_hdc);
197 if (session_font_id == NULL)
199 // revert allocations
200 gdipp::dc_pool_instance.free(session_hdc);
201 return RPC_S_INVALID_ARG;
204 const OUTLINETEXTMETRICW *outline_metrics = reinterpret_cast<const OUTLINETEXTMETRICW *>(outline_metrics_buf);
205 // generate config trait and retrieve font-specific config
206 const LONG point_size = (logfont->lfHeight > 0 ? logfont->lfHeight : -MulDiv(logfont->lfHeight, 72, outline_metrics->otmTextMetrics.tmDigitizedAspectY));
207 const char weight_class = gdipp::get_gdi_weight_class(static_cast<unsigned short>(outline_metrics->otmTextMetrics.tmWeight));
208 const gdipp::render_config_static *session_render_config = gdipp::font_render_config_cache_instance.get_font_render_config(!!weight_class,
209 !!outline_metrics->otmTextMetrics.tmItalic,
210 point_size,
211 metric_face_name(outline_metrics));
212 if (session_render_config->renderer == gdipp::server_config::RENDERER_CLEARTYPE)
214 gdipp::dc_pool_instance.free(session_hdc);
215 return RPC_S_OK;
218 FT_Render_Mode session_render_mode;
219 if (!gdipp::get_render_mode(session_render_config->render_mode, bits_per_pixel, logfont->lfQuality, &session_render_mode))
221 gdipp::dc_pool_instance.free(session_hdc);
222 return RPC_S_INVALID_ARG;
225 gdipp::rpc_session *new_session = reinterpret_cast<gdipp::rpc_session *>(MIDL_user_allocate(sizeof(gdipp::rpc_session)));
227 new_session->bits_per_pixel = bits_per_pixel;
228 new_session->font_id = session_font_id;
229 new_session->hdc = session_hdc;
230 new_session->log_font = *reinterpret_cast<const LOGFONTW *>(logfont_buf);
231 new_session->outline_metrics_buf = outline_metrics_buf;
232 new_session->outline_metrics_size = outline_metrics_size;
233 new_session->render_config = session_render_config;
234 new_session->render_mode = session_render_mode;
235 new_session->render_trait = gdipp::generate_render_trait(logfont, new_session->render_mode);
237 // create session renderer
238 switch (session_render_config->renderer)
240 case gdipp::server_config::RENDERER_DIRECTWRITE:
241 //break;
242 case gdipp::server_config::RENDERER_FREETYPE:
243 new_session->renderer = new gdipp::ft_renderer(new_session);
244 break;
245 case gdipp::server_config::RENDERER_GETGLYPHOUTLINE:
246 new_session->renderer = new gdipp::ggo_renderer(new_session);
247 break;
248 case gdipp::server_config::RENDERER_WIC:
249 break;
250 default:
251 break;
254 *h_session = reinterpret_cast<GDIPP_RPC_SESSION_HANDLE>(new_session);
255 return RPC_S_OK;
258 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_size(
259 /* [in] */ handle_t h_gdipp_rpc,
260 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
261 /* [in] */ unsigned long table,
262 /* [in] */ unsigned long offset,
263 /* [out] */ unsigned long *font_size)
265 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
266 *font_size = gdipp::font_mgr_instance.lookup_font_data(curr_session->font_id, table, offset, NULL, 0);
268 return RPC_S_OK;
271 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_get_font_data(
272 /* [in] */ handle_t h_gdipp_rpc,
273 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
274 /* [in] */ unsigned long table,
275 /* [in] */ unsigned long offset,
276 /* [size_is][out] */ byte *data_buf,
277 /* [in] */ unsigned long buf_size)
279 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
281 // TODO: output pointer is not allocated with MIDL_user_allocate
282 gdipp::font_mgr_instance.lookup_font_data(curr_session->font_id, 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 gdipp::font_mgr_instance.lookup_glyph_indices(curr_session->font_id, str, count, gi);
323 return RPC_S_OK;
326 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_make_bitmap_glyph_run(
327 /* [in] */ handle_t h_gdipp_rpc,
328 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
329 /* [string][in] */ const wchar_t *string,
330 /* [in] */ unsigned int count,
331 /* [in] */ boolean is_glyph_index,
332 /* [out] */ gdipp_rpc_bitmap_glyph_run *glyph_run_ptr)
334 if (count == 0)
335 return RPC_S_INVALID_ARG;
337 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(h_session);
338 bool b_ret;
340 // generate unique identifier for the string
341 const uint128_t string_id = gdipp::glyph_cache::get_string_id(string, count, !!is_glyph_index);
343 // check if a glyph run cached for the same rendering environment and string
344 const gdipp::glyph_run *glyph_run = gdipp::glyph_cache_instance.lookup_glyph_run(string_id, curr_session->render_trait);
345 if (!glyph_run)
347 // no cached glyph run. render new glyph run
348 gdipp::glyph_run *new_glyph_run = new gdipp::glyph_run();
349 b_ret = curr_session->renderer->render(!!is_glyph_index, string, count, new_glyph_run);
350 if (!b_ret)
351 return RPC_S_OUT_OF_MEMORY;
353 // and cache it
354 gdipp::glyph_cache_instance.store_glyph_run(string_id, curr_session->render_trait, new_glyph_run);
355 glyph_run = new_glyph_run;
358 // convert internal glyph run to RPC exchangable format
360 // allocate space for glyph run
361 glyph_run_ptr->count = static_cast<UINT>(glyph_run->glyphs.size());
362 glyph_run_ptr->glyphs = reinterpret_cast<gdipp_rpc_bitmap_glyph *>(MIDL_user_allocate(sizeof(gdipp_rpc_bitmap_glyph) * glyph_run_ptr->count));
363 glyph_run_ptr->ctrl_boxes = reinterpret_cast<RECT *>(MIDL_user_allocate(sizeof(RECT) * glyph_run_ptr->count));
364 glyph_run_ptr->black_boxes = reinterpret_cast<RECT *>(MIDL_user_allocate(sizeof(RECT) * glyph_run_ptr->count));
365 glyph_run_ptr->render_mode = curr_session->render_mode;
367 for (unsigned int i = 0; i < glyph_run_ptr->count; ++i)
369 glyph_run_ptr->ctrl_boxes[i] = glyph_run->ctrl_boxes[i];
370 glyph_run_ptr->black_boxes[i] = glyph_run->black_boxes[i];
372 if (glyph_run->glyphs[i] == NULL)
374 glyph_run_ptr->glyphs[i].buffer = NULL;
375 continue;
378 const FT_BitmapGlyph bmp_glyph = reinterpret_cast<const FT_BitmapGlyph>(glyph_run->glyphs[i]);
379 glyph_run_ptr->glyphs[i].left = bmp_glyph->left;
380 glyph_run_ptr->glyphs[i].top = bmp_glyph->top;
381 glyph_run_ptr->glyphs[i].rows = bmp_glyph->bitmap.rows;
382 glyph_run_ptr->glyphs[i].width = bmp_glyph->bitmap.width;
383 glyph_run_ptr->glyphs[i].pitch = bmp_glyph->bitmap.pitch;
384 const int buffer_size = bmp_glyph->bitmap.rows * abs(bmp_glyph->bitmap.pitch);
385 glyph_run_ptr->glyphs[i].buffer = reinterpret_cast<byte *>(MIDL_user_allocate(buffer_size));
386 memcpy(glyph_run_ptr->glyphs[i].buffer, bmp_glyph->bitmap.buffer, buffer_size);
389 return RPC_S_OK;
392 /* [fault_status][comm_status] */ error_status_t gdipp_rpc_make_outline_glyph_run(
393 /* [in] */ handle_t h_gdipp_rpc,
394 /* [context_handle_noserialize][in] */ GDIPP_RPC_SESSION_HANDLE h_session,
395 /* [string][in] */ const wchar_t *string,
396 /* [in] */ unsigned int count,
397 /* [in] */ boolean is_glyph_index,
398 /* [out] */ gdipp_rpc_outline_glyph_run *glyph_run_ptr)
400 return ERROR_CALL_NOT_IMPLEMENTED;
403 error_status_t gdipp_rpc_end_session(
404 /* [in] */ handle_t h_gdipp_rpc,
405 /* [out][in] */ GDIPP_RPC_SESSION_HANDLE *h_session)
407 const gdipp::rpc_session *curr_session = reinterpret_cast<const gdipp::rpc_session *>(*h_session);
408 if (curr_session == NULL)
409 return RPC_S_INVALID_ARG;
411 gdipp::dc_pool_instance.free(curr_session->hdc);
412 delete[] curr_session->outline_metrics_buf;
413 delete curr_session->renderer;
414 MIDL_user_free(*h_session);
416 *h_session = NULL;
417 return RPC_S_OK;
420 void __RPC_USER GDIPP_RPC_SESSION_HANDLE_rundown(GDIPP_RPC_SESSION_HANDLE h_session)
422 error_status_t e = gdipp_rpc_end_session(NULL, &h_session);
423 assert(e == 0);