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