1 /*
2 Copyright (c) 2010-2013 Peter "Corsix" Cawley and Edvin "Lego3" Linge
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23 #include <SDL.h>
24
25 #include <cstring>
26 #include <exception>
27
28 #include "th_gfx.h"
29 #include "th_gfx_font.h"
30 #include "th_lua_internal.h"
31
32 namespace {
33
l_palette_new(lua_State * L)34 int l_palette_new(lua_State* L) {
35 luaT_stdnew<palette>(L);
36 return 1;
37 }
38
l_palette_load(lua_State * L)39 int l_palette_load(lua_State* L) {
40 palette* pPalette = luaT_testuserdata<palette>(L);
41 size_t iDataLen;
42 const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
43
44 if (pPalette->load_from_th_file(pData, iDataLen))
45 lua_pushboolean(L, 1);
46 else
47 lua_pushboolean(L, 0);
48 return 1;
49 }
50
l_palette_set_entry(lua_State * L)51 int l_palette_set_entry(lua_State* L) {
52 palette* pPalette = luaT_testuserdata<palette>(L);
53 lua_pushboolean(
54 L, pPalette->set_entry(static_cast<int>(luaL_checkinteger(L, 2)),
55 static_cast<uint8_t>(luaL_checkinteger(L, 3)),
56 static_cast<uint8_t>(luaL_checkinteger(L, 4)),
57 static_cast<uint8_t>(luaL_checkinteger(L, 5)))
58 ? 1
59 : 0);
60 return 1;
61 }
62
l_rawbitmap_new(lua_State * L)63 int l_rawbitmap_new(lua_State* L) {
64 luaT_stdnew<raw_bitmap>(L, luaT_environindex, true);
65 return 1;
66 }
67
l_rawbitmap_set_pal(lua_State * L)68 int l_rawbitmap_set_pal(lua_State* L) {
69 raw_bitmap* pBitmap = luaT_testuserdata<raw_bitmap>(L);
70 palette* pPalette = luaT_testuserdata<palette>(L, 2);
71 lua_settop(L, 2);
72
73 pBitmap->set_palette(pPalette);
74 luaT_setenvfield(L, 1, "palette");
75 return 1;
76 }
77
l_rawbitmap_load(lua_State * L)78 int l_rawbitmap_load(lua_State* L) {
79 raw_bitmap* pBitmap = luaT_testuserdata<raw_bitmap>(L);
80 size_t iDataLen;
81 const uint8_t* pData = luaT_checkfile(L, 2, &iDataLen);
82 int iWidth = static_cast<int>(luaL_checkinteger(L, 3));
83 render_target* pSurface =
84 luaT_testuserdata<render_target>(L, 4, luaT_upvalueindex(1), false);
85
86 try {
87 pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface);
88 } catch (const std::exception& ex) {
89 lua_pushstring(L, ex.what());
90 lua_error(L);
91 return 1;
92 }
93
94 lua_pushboolean(L, 1);
95 return 1;
96 }
97
l_rawbitmap_draw(lua_State * L)98 int l_rawbitmap_draw(lua_State* L) {
99 raw_bitmap* pBitmap = luaT_testuserdata<raw_bitmap>(L);
100 render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
101
102 if (lua_gettop(L) >= 8) {
103 pBitmap->draw(pCanvas, static_cast<int>(luaL_checkinteger(L, 3)),
104 static_cast<int>(luaL_checkinteger(L, 4)),
105 static_cast<int>(luaL_checkinteger(L, 5)),
106 static_cast<int>(luaL_checkinteger(L, 6)),
107 static_cast<int>(luaL_checkinteger(L, 7)),
108 static_cast<int>(luaL_checkinteger(L, 8)));
109 } else
110 pBitmap->draw(pCanvas, static_cast<int>(luaL_optinteger(L, 3, 0)),
111 static_cast<int>(luaL_optinteger(L, 4, 0)));
112
113 lua_settop(L, 1);
114 return 1;
115 }
116
l_spritesheet_new(lua_State * L)117 int l_spritesheet_new(lua_State* L) {
118 luaT_stdnew<sprite_sheet>(L, luaT_environindex, true);
119 return 1;
120 }
121
l_spritesheet_set_pal(lua_State * L)122 int l_spritesheet_set_pal(lua_State* L) {
123 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
124 palette* pPalette = luaT_testuserdata<palette>(L, 2);
125 lua_settop(L, 2);
126
127 pSheet->set_palette(pPalette);
128 luaT_setenvfield(L, 1, "palette");
129 return 1;
130 }
131
l_spritesheet_load(lua_State * L)132 int l_spritesheet_load(lua_State* L) {
133 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
134 size_t iDataLenTable, iDataLenChunk;
135 const uint8_t* pDataTable = luaT_checkfile(L, 2, &iDataLenTable);
136 const uint8_t* pDataChunk = luaT_checkfile(L, 3, &iDataLenChunk);
137 bool bComplex = lua_toboolean(L, 4) != 0;
138 render_target* pSurface =
139 luaT_testuserdata<render_target>(L, 5, luaT_upvalueindex(1), false);
140
141 if (pSheet->load_from_th_file(pDataTable, iDataLenTable, pDataChunk,
142 iDataLenChunk, bComplex, pSurface))
143 lua_pushboolean(L, 1);
144 else
145 lua_pushboolean(L, 0);
146
147 return 1;
148 }
149
l_spritesheet_count(lua_State * L)150 int l_spritesheet_count(lua_State* L) {
151 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
152
153 lua_pushinteger(L, pSheet->get_sprite_count());
154 return 1;
155 }
156
l_spritesheet_size(lua_State * L)157 int l_spritesheet_size(lua_State* L) {
158 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
159 lua_Integer iSprite = luaL_checkinteger(L, 2); // No array adjustment
160 if (iSprite < 0 ||
161 iSprite >= static_cast<lua_Integer>(pSheet->get_sprite_count()))
162 return luaL_argerror(L, 2, "Sprite index out of bounds");
163
164 int iWidth;
165 int iHeight;
166 pSheet->get_sprite_size_unchecked(static_cast<size_t>(iSprite), &iWidth,
167 &iHeight);
168
169 lua_pushinteger(L, iWidth);
170 lua_pushinteger(L, iHeight);
171 return 2;
172 }
173
l_spritesheet_draw(lua_State * L)174 int l_spritesheet_draw(lua_State* L) {
175 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
176 render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
177 int iSprite =
178 static_cast<int>(luaL_checkinteger(L, 3)); // No array adjustment
179
180 pSheet->draw_sprite(pCanvas, iSprite,
181 static_cast<int>(luaL_optinteger(L, 4, 0)),
182 static_cast<int>(luaL_optinteger(L, 5, 0)),
183 static_cast<int>(luaL_optinteger(L, 6, 0)));
184
185 lua_settop(L, 1);
186 return 1;
187 }
188
l_spritesheet_hittest(lua_State * L)189 int l_spritesheet_hittest(lua_State* L) {
190 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
191 size_t iSprite = luaL_checkinteger(L, 2);
192 int iX = static_cast<int>(luaL_checkinteger(L, 3));
193 int iY = static_cast<int>(luaL_checkinteger(L, 4));
194 uint32_t iFlags = static_cast<uint32_t>(luaL_optinteger(L, 5, 0));
195 return pSheet->hit_test_sprite(iSprite, iX, iY, iFlags);
196 }
197
l_spritesheet_isvisible(lua_State * L)198 int l_spritesheet_isvisible(lua_State* L) {
199 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L);
200 size_t iSprite = luaL_checkinteger(L, 2);
201 argb_colour oDummy;
202 lua_pushboolean(L,
203 pSheet->get_sprite_average_colour(iSprite, &oDummy) ? 1 : 0);
204 return 1;
205 }
206
l_font_new(lua_State * L)207 int l_font_new(lua_State* L) {
208 return luaL_error(L, "Cannot instantiate an interface");
209 }
210
l_bitmap_font_new(lua_State * L)211 int l_bitmap_font_new(lua_State* L) {
212 luaT_stdnew<bitmap_font>(L, luaT_environindex, true);
213 return 1;
214 }
215
l_bitmap_font_set_spritesheet(lua_State * L)216 int l_bitmap_font_set_spritesheet(lua_State* L) {
217 bitmap_font* pFont = luaT_testuserdata<bitmap_font>(L);
218 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L, 2);
219 lua_settop(L, 2);
220
221 pFont->set_sprite_sheet(pSheet);
222 luaT_setenvfield(L, 1, "sprites");
223 return 1;
224 }
225
l_bitmap_font_get_spritesheet(lua_State * L)226 int l_bitmap_font_get_spritesheet(lua_State* L) {
227 luaT_testuserdata<bitmap_font>(L);
228 luaT_getenvfield(L, 1, "sprites");
229 return 1;
230 }
231
l_bitmap_font_set_sep(lua_State * L)232 int l_bitmap_font_set_sep(lua_State* L) {
233 bitmap_font* pFont = luaT_testuserdata<bitmap_font>(L);
234
235 pFont->set_separation(static_cast<int>(luaL_checkinteger(L, 2)),
236 static_cast<int>(luaL_optinteger(L, 3, 0)));
237
238 lua_settop(L, 1);
239 return 1;
240 }
241
242 #ifdef CORSIX_TH_USE_FREETYPE2
l_freetype_throw_error_code(lua_State * L,FT_Error e)243 void l_freetype_throw_error_code(lua_State* L, FT_Error e) {
244 if (e != FT_Err_Ok) {
245 switch (e) {
246 #undef __FTERRORS_H__
247 #define FT_ERRORDEF(e, v, s) \
248 case e: \
249 lua_pushliteral(L, s); \
250 break;
251 #define FT_ERROR_START_LIST
252 #define FT_ERROR_END_LIST
253 #include FT_ERRORS_H
254 default:
255 lua_pushliteral(L, "Unrecognised FreeType2 error");
256 break;
257 };
258 lua_error(L);
259 }
260 }
261
l_freetype_font_new(lua_State * L)262 int l_freetype_font_new(lua_State* L) {
263 freetype_font* pFont = luaT_stdnew<freetype_font>(L, luaT_environindex, true);
264 l_freetype_throw_error_code(L, pFont->initialise());
265 return 1;
266 }
267
l_freetype_font_set_spritesheet(lua_State * L)268 int l_freetype_font_set_spritesheet(lua_State* L) {
269 freetype_font* pFont = luaT_testuserdata<freetype_font>(L);
270 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L, 2);
271 lua_settop(L, 2);
272
273 l_freetype_throw_error_code(L, pFont->match_bitmap_font(pSheet));
274 lua_settop(L, 1);
275 return 1;
276 }
277
l_freetype_font_get_copyright(lua_State * L)278 int l_freetype_font_get_copyright(lua_State* L) {
279 lua_pushstring(L, freetype_font::get_copyright_notice());
280 return 1;
281 }
282
l_freetype_font_set_face(lua_State * L)283 int l_freetype_font_set_face(lua_State* L) {
284 freetype_font* pFont = luaT_testuserdata<freetype_font>(L);
285 size_t iLength;
286 const uint8_t* pData = luaT_checkfile(L, 2, &iLength);
287 lua_settop(L, 2);
288
289 l_freetype_throw_error_code(L, pFont->set_face(pData, iLength));
290 luaT_setenvfield(L, 1, "face");
291 return 1;
292 }
293
l_freetype_font_clear_cache(lua_State * L)294 int l_freetype_font_clear_cache(lua_State* L) {
295 freetype_font* pFont = luaT_testuserdata<freetype_font>(L);
296 pFont->clear_cache();
297 return 0;
298 }
299
300 #endif
301
l_font_get_size(lua_State * L)302 int l_font_get_size(lua_State* L) {
303 font* pFont = luaT_testuserdata<font>(L);
304 size_t iMsgLen;
305 const char* sMsg = luaT_checkstring(L, 2, &iMsgLen);
306
307 int iMaxWidth = INT_MAX;
308 if (!lua_isnoneornil(L, 3))
309 iMaxWidth = static_cast<int>(luaL_checkinteger(L, 3));
310
311 text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen, iMaxWidth);
312
313 lua_pushinteger(L, oDrawArea.end_x);
314 lua_pushinteger(L, oDrawArea.end_y);
315 lua_pushinteger(L, oDrawArea.row_count);
316
317 return 3;
318 }
319
l_font_draw(lua_State * L)320 int l_font_draw(lua_State* L) {
321 font* pFont = luaT_testuserdata<font>(L);
322 render_target* pCanvas = nullptr;
323 if (!lua_isnoneornil(L, 2)) {
324 pCanvas = luaT_testuserdata<render_target>(L, 2);
325 }
326 size_t iMsgLen;
327 const char* sMsg = luaT_checkstring(L, 3, &iMsgLen);
328 int iX = static_cast<int>(luaL_checkinteger(L, 4));
329 int iY = static_cast<int>(luaL_checkinteger(L, 5));
330
331 text_alignment eAlign = text_alignment::center;
332 if (!lua_isnoneornil(L, 8)) {
333 const char* sAlign = luaL_checkstring(L, 8);
334 if (std::strcmp(sAlign, "right") == 0) {
335 eAlign = text_alignment::right;
336 } else if (std::strcmp(sAlign, "left") == 0) {
337 eAlign = text_alignment::left;
338 } else if (std::strcmp(sAlign, "center") == 0 ||
339 std::strcmp(sAlign, "centre") == 0 ||
340 std::strcmp(sAlign, "middle") == 0) {
341 eAlign = text_alignment::center;
342 } else {
343 return luaL_error(L, "Invalid alignment: \"%s\"", sAlign);
344 }
345 }
346
347 text_layout oDrawArea = pFont->get_text_dimensions(sMsg, iMsgLen);
348 if (!lua_isnoneornil(L, 7)) {
349 int iW = static_cast<int>(luaL_checkinteger(L, 6));
350 int iH = static_cast<int>(luaL_checkinteger(L, 7));
351 if (iW > oDrawArea.end_x && eAlign != text_alignment::left) {
352 iX +=
353 (iW - oDrawArea.end_x) / ((eAlign == text_alignment::center) ? 2 : 1);
354 }
355 if (iH > oDrawArea.end_y) {
356 iY += (iH - oDrawArea.end_y) / 2;
357 }
358 }
359 if (pCanvas != nullptr) {
360 pFont->draw_text(pCanvas, sMsg, iMsgLen, iX, iY);
361 }
362 lua_pushinteger(L, iY + oDrawArea.end_y);
363 lua_pushinteger(L, iX + oDrawArea.end_x);
364
365 return 2;
366 }
367
l_font_draw_wrapped(lua_State * L)368 int l_font_draw_wrapped(lua_State* L) {
369 font* pFont = luaT_testuserdata<font>(L);
370 render_target* pCanvas = nullptr;
371 if (!lua_isnoneornil(L, 2)) {
372 pCanvas = luaT_testuserdata<render_target>(L, 2);
373 }
374 size_t iMsgLen;
375 const char* sMsg = luaT_checkstring(L, 3, &iMsgLen);
376 int iX = static_cast<int>(luaL_checkinteger(L, 4));
377 int iY = static_cast<int>(luaL_checkinteger(L, 5));
378 int iW = static_cast<int>(luaL_checkinteger(L, 6));
379
380 text_alignment eAlign = text_alignment::left;
381 if (!lua_isnoneornil(L, 7)) {
382 const char* sAlign = luaL_checkstring(L, 7);
383 if (std::strcmp(sAlign, "right") == 0) {
384 eAlign = text_alignment::right;
385 } else if (std::strcmp(sAlign, "left") == 0) {
386 eAlign = text_alignment::left;
387 } else if (std::strcmp(sAlign, "center") == 0 ||
388 std::strcmp(sAlign, "centre") == 0 ||
389 std::strcmp(sAlign, "middle") == 0) {
390 eAlign = text_alignment::center;
391 } else {
392 return luaL_error(L, "Invalid alignment: \"%s\"", sAlign);
393 }
394 }
395
396 int iMaxRows = INT_MAX;
397 if (!lua_isnoneornil(L, 8)) {
398 iMaxRows = static_cast<int>(luaL_checkinteger(L, 8));
399 }
400
401 int iSkipRows = 0;
402 if (!lua_isnoneornil(L, 9)) {
403 iSkipRows = static_cast<int>(luaL_checkinteger(L, 9));
404 }
405
406 text_layout oDrawArea = pFont->draw_text_wrapped(
407 pCanvas, sMsg, iMsgLen, iX, iY, iW, iMaxRows, iSkipRows, eAlign);
408 lua_pushinteger(L, oDrawArea.end_y);
409 lua_pushinteger(L, oDrawArea.end_x);
410 lua_pushinteger(L, oDrawArea.row_count);
411
412 return 3;
413 }
414
l_font_draw_tooltip(lua_State * L)415 int l_font_draw_tooltip(lua_State* L) {
416 font* pFont = luaT_testuserdata<font>(L);
417 render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
418 size_t iMsgLen;
419 const char* sMsg = luaT_checkstring(L, 3, &iMsgLen);
420 int iX = static_cast<int>(luaL_checkinteger(L, 4));
421 int iY = static_cast<int>(luaL_checkinteger(L, 5));
422 int iScreenWidth = pCanvas->get_width();
423
424 int iW = 200; // (for now) hardcoded width of tooltips
425 uint32_t iBlack = render_target::map_colour(0x00, 0x00, 0x00);
426 uint32_t iWhite = render_target::map_colour(0xFF, 0xFF, 0xFF);
427 text_layout oArea = pFont->draw_text_wrapped(nullptr, sMsg, iMsgLen, iX + 2,
428 iY + 1, iW - 4, INT_MAX, 0);
429 int iLastX = iX + oArea.width + 3;
430 int iFirstY = iY - (oArea.end_y - iY) - 1;
431
432 int iXOffset = iLastX > iScreenWidth ? iScreenWidth - iLastX : 0;
433 int iYOffset = iFirstY < 0 ? -iFirstY : 0;
434
435 pCanvas->fill_rect(iBlack, iX + iXOffset, iFirstY + iYOffset, oArea.width + 3,
436 oArea.end_y - iY + 2);
437 pCanvas->fill_rect(iWhite, iX + iXOffset + 1, iFirstY + 1 + iYOffset,
438 oArea.width + 1, oArea.end_y - iY);
439
440 pFont->draw_text_wrapped(pCanvas, sMsg, iMsgLen, iX + 2 + iXOffset,
441 iFirstY + 1 + iYOffset, iW - 4);
442
443 lua_pushinteger(L, oArea.end_y);
444
445 return 1;
446 }
447
l_layers_new(lua_State * L)448 int l_layers_new(lua_State* L) {
449 layers* pLayers = luaT_stdnew<layers>(L, luaT_environindex, false);
450 for (int i = 0; i < 13; ++i) pLayers->layer_contents[i] = 0;
451 return 1;
452 }
453
l_layers_get(lua_State * L)454 int l_layers_get(lua_State* L) {
455 layers* pLayers = luaT_testuserdata<layers>(L);
456 lua_Integer iLayer = luaL_checkinteger(L, 2);
457 if (0 <= iLayer && iLayer < 13)
458 lua_pushinteger(L, pLayers->layer_contents[iLayer]);
459 else
460 lua_pushnil(L);
461 return 1;
462 }
463
l_layers_set(lua_State * L)464 int l_layers_set(lua_State* L) {
465 layers* pLayers = luaT_testuserdata<layers>(L);
466 lua_Integer iLayer = luaL_checkinteger(L, 2);
467 uint8_t iValue = static_cast<uint8_t>(luaL_checkinteger(L, 3));
468 if (0 <= iLayer && iLayer < 13) pLayers->layer_contents[iLayer] = iValue;
469 return 0;
470 }
471
l_layers_persist(lua_State * L)472 int l_layers_persist(lua_State* L) {
473 layers* pLayers = luaT_testuserdata<layers>(L);
474 lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 2);
475
476 int iNumLayers = 13;
477 for (; iNumLayers >= 1; --iNumLayers) {
478 if (pLayers->layer_contents[iNumLayers - 1] != 0) break;
479 }
480 pWriter->write_uint(iNumLayers);
481 pWriter->write_byte_stream(pLayers->layer_contents, iNumLayers);
482 return 0;
483 }
484
l_layers_depersist(lua_State * L)485 int l_layers_depersist(lua_State* L) {
486 layers* pLayers = luaT_testuserdata<layers>(L);
487 lua_settop(L, 2);
488 lua_insert(L, 1);
489 lua_persist_reader* pReader =
490 static_cast<lua_persist_reader*>(lua_touserdata(L, 1));
491
492 std::memset(pLayers->layer_contents, 0, sizeof(pLayers->layer_contents));
493 int iNumLayers;
494 if (!pReader->read_uint(iNumLayers)) return 0;
495 if (iNumLayers > 13) {
496 if (!pReader->read_byte_stream(pLayers->layer_contents, 13)) return 0;
497 if (!pReader->read_byte_stream(nullptr, iNumLayers - 13)) return 0;
498 } else {
499 if (!pReader->read_byte_stream(pLayers->layer_contents, iNumLayers))
500 return 0;
501 }
502 return 0;
503 }
504
l_cursor_new(lua_State * L)505 int l_cursor_new(lua_State* L) {
506 luaT_stdnew<cursor>(L, luaT_environindex, false);
507 return 1;
508 }
509
l_cursor_load(lua_State * L)510 int l_cursor_load(lua_State* L) {
511 cursor* pCursor = luaT_testuserdata<cursor>(L);
512 sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L, 2);
513 if (pCursor->create_from_sprite(pSheet,
514 static_cast<int>(luaL_checkinteger(L, 3)),
515 static_cast<int>(luaL_optinteger(L, 4, 0)),
516 static_cast<int>(luaL_optinteger(L, 5, 0)))) {
517 lua_settop(L, 1);
518 return 1;
519 } else {
520 lua_pushboolean(L, 0);
521 return 1;
522 }
523 }
524
l_cursor_use(lua_State * L)525 int l_cursor_use(lua_State* L) {
526 cursor* pCursor = luaT_testuserdata<cursor>(L);
527 render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
528 pCursor->use(pCanvas);
529 return 0;
530 }
531
l_cursor_position(lua_State * L)532 int l_cursor_position(lua_State* L) {
533 render_target* pCanvas =
534 luaT_testuserdata<render_target>(L, 1, luaT_upvalueindex(1));
535 lua_pushboolean(L, cursor::set_position(
536 pCanvas, static_cast<int>(luaL_checkinteger(L, 2)),
537 static_cast<int>(luaL_checkinteger(L, 3)))
538 ? 1
539 : 0);
540 return 1;
541 }
542
543 /** Construct the helper structure for making a #THRenderTarget. */
l_surface_creation_params(lua_State * L,int iArgStart)544 render_target_creation_params l_surface_creation_params(lua_State* L,
545 int iArgStart) {
546 render_target_creation_params oParams;
547 oParams.width = static_cast<int>(luaL_checkinteger(L, iArgStart));
548 oParams.height = static_cast<int>(luaL_checkinteger(L, iArgStart + 1));
549
550 oParams.fullscreen = false;
551 oParams.present_immediate = false;
552 oParams.direct_zoom = false;
553
554 // Parse string arguments, looking for matching parameter names.
555 for (int iArg = iArgStart + 2, iArgCount = lua_gettop(L); iArg <= iArgCount;
556 ++iArg) {
557 const char* sOption = luaL_checkstring(L, iArg);
558 if (sOption[0] == 0) continue;
559
560 if (std::strcmp(sOption, "fullscreen") == 0) {
561 oParams.fullscreen = true;
562 }
563 if (std::strcmp(sOption, "present immediate") == 0) {
564 oParams.present_immediate = true;
565 }
566 if (std::strcmp(sOption, "direct zoom") == 0) {
567 oParams.direct_zoom = true;
568 }
569 }
570
571 return oParams;
572 }
573
l_surface_new(lua_State * L)574 int l_surface_new(lua_State* L) {
575 lua_remove(L, 1); // Value inserted by __call
576
577 render_target_creation_params oParams = l_surface_creation_params(L, 1);
578 render_target* pCanvas = luaT_stdnew<render_target>(L);
579 if (pCanvas->create(&oParams)) return 1;
580
581 lua_pushnil(L);
582 lua_pushstring(L, pCanvas->get_last_error());
583 return 2;
584 }
585
l_surface_update(lua_State * L)586 int l_surface_update(lua_State* L) {
587 render_target* pCanvas = luaT_testuserdata<render_target>(L);
588 render_target_creation_params oParams = l_surface_creation_params(L, 2);
589 if (pCanvas->update(&oParams)) {
590 lua_pushnil(L);
591 return 1;
592 }
593
594 lua_pushstring(L, pCanvas->get_last_error());
595 return 1;
596 }
597
l_surface_destroy(lua_State * L)598 int l_surface_destroy(lua_State* L) {
599 render_target* pCanvas = luaT_testuserdata<render_target>(L);
600 pCanvas->end_frame();
601 pCanvas->destroy();
602 return 1;
603 }
604
l_surface_fill_black(lua_State * L)605 int l_surface_fill_black(lua_State* L) {
606 render_target* pCanvas = luaT_testuserdata<render_target>(L);
607 lua_settop(L, 1);
608 if (pCanvas->fill_black()) return 1;
609 lua_pushnil(L);
610 lua_pushstring(L, pCanvas->get_last_error());
611 return 2;
612 }
613
l_surface_start_frame(lua_State * L)614 int l_surface_start_frame(lua_State* L) {
615 render_target* pCanvas = luaT_testuserdata<render_target>(L);
616 lua_settop(L, 1);
617 if (pCanvas->start_frame()) return 1;
618 lua_pushnil(L);
619 lua_pushstring(L, pCanvas->get_last_error());
620 return 2;
621 }
622
l_surface_end_frame(lua_State * L)623 int l_surface_end_frame(lua_State* L) {
624 render_target* pCanvas = luaT_testuserdata<render_target>(L);
625 lua_settop(L, 1);
626 if (pCanvas->end_frame()) return 1;
627 lua_pushnil(L);
628 lua_pushstring(L, pCanvas->get_last_error());
629 return 2;
630 }
631
l_surface_nonoverlapping(lua_State * L)632 int l_surface_nonoverlapping(lua_State* L) {
633 render_target* pCanvas = luaT_testuserdata<render_target>(L);
634 if (lua_isnone(L, 2) || lua_toboolean(L, 2) != 0)
635 pCanvas->start_nonoverlapping_draws();
636 else
637 pCanvas->finish_nonoverlapping_draws();
638 lua_settop(L, 1);
639 return 1;
640 }
641
l_surface_set_blue_filter_active(lua_State * L)642 int l_surface_set_blue_filter_active(lua_State* L) {
643 render_target* pCanvas = luaT_testuserdata<render_target>(L);
644 pCanvas->set_blue_filter_active(
645 (lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0));
646 return 1;
647 }
648
l_surface_map(lua_State * L)649 int l_surface_map(lua_State* L) {
650 lua_pushnumber(
651 L, (lua_Number)render_target::map_colour((Uint8)luaL_checkinteger(L, 2),
652 (Uint8)luaL_checkinteger(L, 3),
653 (Uint8)luaL_checkinteger(L, 4)));
654 return 1;
655 }
656
l_surface_rect(lua_State * L)657 int l_surface_rect(lua_State* L) {
658 render_target* pCanvas = luaT_testuserdata<render_target>(L);
659 if (pCanvas->fill_rect(static_cast<uint32_t>(luaL_checkinteger(L, 2)),
660 static_cast<int>(luaL_checkinteger(L, 3)),
661 static_cast<int>(luaL_checkinteger(L, 4)),
662 static_cast<int>(luaL_checkinteger(L, 5)),
663 static_cast<int>(luaL_checkinteger(L, 6)))) {
664 lua_settop(L, 1);
665 return 1;
666 }
667 lua_pushnil(L);
668 lua_pushstring(L, pCanvas->get_last_error());
669 return 2;
670 }
671
l_surface_screenshot(lua_State * L)672 int l_surface_screenshot(lua_State* L) {
673 render_target* pCanvas = luaT_testuserdata<render_target>(L);
674 const char* sFile = luaL_checkstring(L, 2);
675 if (pCanvas->take_screenshot(sFile)) {
676 lua_settop(L, 1);
677 return 1;
678 }
679 lua_pushnil(L);
680 lua_pushstring(L, pCanvas->get_last_error());
681 return 2;
682 }
683
l_surface_get_clip(lua_State * L)684 int l_surface_get_clip(lua_State* L) {
685 render_target* pCanvas = luaT_testuserdata<render_target>(L);
686 clip_rect rcClip;
687 pCanvas->get_clip_rect(&rcClip);
688 lua_pushinteger(L, rcClip.x);
689 lua_pushinteger(L, rcClip.y);
690 lua_pushinteger(L, rcClip.w);
691 lua_pushinteger(L, rcClip.h);
692 return 4;
693 }
694
l_surface_set_clip(lua_State * L)695 int l_surface_set_clip(lua_State* L) {
696 render_target* pCanvas = luaT_testuserdata<render_target>(L);
697 clip_rect rcClip;
698 rcClip.x = static_cast<clip_rect::x_y_type>(luaL_checkinteger(L, 2));
699 rcClip.y = static_cast<clip_rect::x_y_type>(luaL_checkinteger(L, 3));
700 rcClip.w = static_cast<clip_rect::w_h_type>(luaL_checkinteger(L, 4));
701 rcClip.h = static_cast<clip_rect::w_h_type>(luaL_checkinteger(L, 5));
702 if (lua_toboolean(L, 6) != 0) {
703 clip_rect rcExistingClip;
704 pCanvas->get_clip_rect(&rcExistingClip);
705 clip_rect_intersection(rcClip, rcExistingClip);
706 }
707 pCanvas->set_clip_rect(&rcClip);
708 lua_settop(L, 1);
709 return 1;
710 }
711
l_surface_scale(lua_State * L)712 int l_surface_scale(lua_State* L) {
713 render_target* pCanvas = luaT_testuserdata<render_target>(L);
714 scaled_items eToScale = scaled_items::none;
715 if (lua_isnoneornil(L, 3)) {
716 eToScale = scaled_items::all;
717 } else {
718 size_t iLength;
719 const char* sOption = lua_tolstring(L, 3, &iLength);
720 if (sOption && iLength >= 6 && std::memcmp(sOption, "bitmap", 6) == 0) {
721 eToScale = scaled_items::bitmaps;
722 } else
723 luaL_error(L, "Expected \"bitmap\" as 2nd argument");
724 }
725 lua_pushboolean(L, pCanvas->set_scale_factor(
726 static_cast<float>(luaL_checknumber(L, 2)), eToScale)
727 ? 1
728 : 0);
729 return 1;
730 }
731
l_surface_set_caption(lua_State * L)732 int l_surface_set_caption(lua_State* L) {
733 render_target* pCanvas = luaT_testuserdata<render_target>(L);
734 pCanvas->set_caption(luaL_checkstring(L, 2));
735
736 lua_settop(L, 1);
737 return 1;
738 }
739
l_surface_get_renderer_details(lua_State * L)740 int l_surface_get_renderer_details(lua_State* L) {
741 render_target* pCanvas = luaT_testuserdata<render_target>(L);
742 lua_pushstring(L, pCanvas->get_renderer_details());
743 return 1;
744 }
745
746 // Lua to THRenderTarget->setWindowGrab
l_surface_set_capture_mouse(lua_State * L)747 int l_surface_set_capture_mouse(lua_State* L) {
748 render_target* pCanvas = luaT_testuserdata<render_target>(L);
749 pCanvas->set_window_grab(
750 (lua_isnoneornil(L, 2) != 0) ? false : (lua_toboolean(L, 2) != 0));
751 return 0;
752 }
753
l_line_new(lua_State * L)754 int l_line_new(lua_State* L) {
755 luaT_stdnew<line>(L);
756 return 1;
757 }
758
l_move_to(lua_State * L)759 int l_move_to(lua_State* L) {
760 line* pLine = luaT_testuserdata<line>(L);
761 pLine->move_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0));
762
763 lua_settop(L, 1);
764 return 1;
765 }
766
l_line_to(lua_State * L)767 int l_line_to(lua_State* L) {
768 line* pLine = luaT_testuserdata<line>(L);
769 pLine->line_to(luaL_optnumber(L, 2, 0), luaL_optnumber(L, 3, 0));
770
771 lua_settop(L, 1);
772 return 1;
773 }
774
l_set_width(lua_State * L)775 int l_set_width(lua_State* L) {
776 line* pLine = luaT_testuserdata<line>(L);
777 pLine->set_width(luaL_optnumber(L, 2, 1));
778
779 lua_settop(L, 1);
780 return 1;
781 }
782
l_set_colour(lua_State * L)783 int l_set_colour(lua_State* L) {
784 line* pLine = luaT_testuserdata<line>(L);
785 pLine->set_colour(static_cast<uint8_t>(luaL_optinteger(L, 2, 0)),
786 static_cast<uint8_t>(luaL_optinteger(L, 3, 0)),
787 static_cast<uint8_t>(luaL_optinteger(L, 4, 0)),
788 static_cast<uint8_t>(luaL_optinteger(L, 5, 255)));
789
790 lua_settop(L, 1);
791 return 1;
792 }
793
l_line_draw(lua_State * L)794 int l_line_draw(lua_State* L) {
795 line* pLine = luaT_testuserdata<line>(L);
796 render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
797 pLine->draw(pCanvas, static_cast<int>(luaL_optinteger(L, 3, 0)),
798 static_cast<int>(luaL_optinteger(L, 4, 0)));
799
800 lua_settop(L, 1);
801 return 1;
802 }
803
l_line_persist(lua_State * L)804 int l_line_persist(lua_State* L) {
805 line* pLine = luaT_testuserdata<line>(L);
806 lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 2);
807 pLine->persist(pWriter);
808 return 0;
809 }
810
l_line_depersist(lua_State * L)811 int l_line_depersist(lua_State* L) {
812 line* pLine = luaT_testuserdata<line>(L);
813 lua_settop(L, 2);
814 lua_insert(L, 1);
815 lua_persist_reader* pReader =
816 static_cast<lua_persist_reader*>(lua_touserdata(L, 1));
817 pLine->depersist(pReader);
818 return 0;
819 }
820
821 } // namespace
822
lua_register_gfx(const lua_register_state * pState)823 void lua_register_gfx(const lua_register_state* pState) {
824 // Palette
825 {
826 lua_class_binding<palette> lcb(pState, "palette", l_palette_new,
827 lua_metatable::palette);
828 lcb.add_function(l_palette_load, "load");
829 lcb.add_function(l_palette_set_entry, "setEntry");
830 }
831
832 // Raw bitmap
833 {
834 lua_class_binding<raw_bitmap> lcb(pState, "bitmap", l_rawbitmap_new,
835 lua_metatable::bitmap);
836 lcb.add_function(l_rawbitmap_load, "load", lua_metatable::surface);
837 lcb.add_function(l_rawbitmap_set_pal, "setPalette", lua_metatable::palette);
838 lcb.add_function(l_rawbitmap_draw, "draw", lua_metatable::surface);
839 }
840
841 // Sprite sheet
842 {
843 lua_class_binding<sprite_sheet> lcb(pState, "sheet", l_spritesheet_new,
844 lua_metatable::sheet);
845 lcb.add_metamethod(l_spritesheet_count, "len");
846 lcb.add_function(l_spritesheet_load, "load", lua_metatable::surface);
847 lcb.add_function(l_spritesheet_set_pal, "setPalette",
848 lua_metatable::palette);
849 lcb.add_function(l_spritesheet_size, "size");
850 lcb.add_function(l_spritesheet_draw, "draw", lua_metatable::surface);
851 lcb.add_function(l_spritesheet_hittest, "hitTest");
852 lcb.add_function(l_spritesheet_isvisible, "isVisible");
853 }
854
855 // Font
856 // Also adapt the font proxy meta table (font_proxy_mt) in graphics.lua.
857 {
858 lua_class_binding<font> lcb(pState, "font", l_font_new,
859 lua_metatable::font);
860 lcb.add_function(l_font_get_size, "sizeOf");
861 lcb.add_function(l_font_draw, "draw", lua_metatable::surface);
862 lcb.add_function(l_font_draw_wrapped, "drawWrapped",
863 lua_metatable::surface);
864 lcb.add_function(l_font_draw_tooltip, "drawTooltip",
865 lua_metatable::surface);
866 }
867
868 // BitmapFont
869 {
870 lua_class_binding<bitmap_font> lcb(pState, "bitmap_font", l_bitmap_font_new,
871 lua_metatable::bitmap_font);
872 lcb.set_superclass(lua_metatable::font);
873 lcb.add_function(l_bitmap_font_set_spritesheet, "setSheet",
874 lua_metatable::sheet);
875 lcb.add_function(l_bitmap_font_get_spritesheet, "getSheet",
876 lua_metatable::sheet);
877 lcb.add_function(l_bitmap_font_set_sep, "setSeparation");
878 }
879
880 #ifdef CORSIX_TH_USE_FREETYPE2
881 // FreeTypeFont
882 {
883 lua_class_binding<freetype_font> lcb(pState, "freetype_font",
884 l_freetype_font_new,
885 lua_metatable::freetype_font);
886 lcb.set_superclass(lua_metatable::font);
887 lcb.add_function(l_freetype_font_set_spritesheet, "setSheet",
888 lua_metatable::sheet);
889 lcb.add_function(l_freetype_font_set_face, "setFace");
890 lcb.add_function(l_freetype_font_get_copyright, "getCopyrightNotice");
891 lcb.add_function(l_freetype_font_clear_cache, "clearCache");
892 }
893 #endif
894
895 // Layers
896 {
897 lua_class_binding<layers> lcb(pState, "layers", l_layers_new,
898 lua_metatable::layers);
899 lcb.add_metamethod(l_layers_get, "index");
900 lcb.add_metamethod(l_layers_set, "newindex");
901 lcb.add_metamethod(l_layers_persist, "persist");
902 lcb.add_metamethod(l_layers_depersist, "depersist");
903 }
904
905 // Cursor
906 {
907 lua_class_binding<cursor> lcb(pState, "cursor", l_cursor_new,
908 lua_metatable::cursor);
909 lcb.add_function(l_cursor_load, "load", lua_metatable::sheet);
910 lcb.add_function(l_cursor_use, "use", lua_metatable::surface);
911 lcb.add_function(l_cursor_position, "setPosition", lua_metatable::surface);
912 }
913
914 // Surface
915 {
916 lua_class_binding<render_target> lcb(pState, "surface", l_surface_new,
917 lua_metatable::surface);
918 lcb.add_function(l_surface_update, "update");
919 lcb.add_function(l_surface_destroy, "destroy");
920 lcb.add_function(l_surface_fill_black, "fillBlack");
921 lcb.add_function(l_surface_start_frame, "startFrame");
922 lcb.add_function(l_surface_end_frame, "endFrame");
923 lcb.add_function(l_surface_nonoverlapping, "nonOverlapping");
924 lcb.add_function(l_surface_map, "mapRGB");
925 lcb.add_function(l_surface_set_blue_filter_active, "setBlueFilterActive");
926 lcb.add_function(l_surface_rect, "drawRect");
927 lcb.add_function(l_surface_get_clip, "getClip");
928 lcb.add_function(l_surface_set_clip, "setClip");
929 lcb.add_function(l_surface_screenshot, "takeScreenshot");
930 lcb.add_function(l_surface_scale, "scale");
931 lcb.add_function(l_surface_set_caption, "setCaption");
932 lcb.add_function(l_surface_get_renderer_details, "getRendererDetails");
933 lcb.add_function(l_surface_set_capture_mouse, "setCaptureMouse");
934 }
935
936 // Line
937 {
938 lua_class_binding<line> lcb(pState, "line", l_line_new,
939 lua_metatable::line);
940 lcb.add_function(l_move_to, "moveTo");
941 lcb.add_function(l_line_to, "lineTo");
942 lcb.add_function(l_set_width, "setWidth");
943 lcb.add_function(l_set_colour, "setColour");
944 lcb.add_function(l_line_draw, "draw", lua_metatable::surface);
945 lcb.add_metamethod(l_line_persist, "persist");
946 lcb.add_metamethod(l_line_depersist, "depersist");
947 }
948 }
949