1 /*
2  *
3  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
4  *
5  * Solarus is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Solarus is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "solarus/core/CurrentQuest.h"
20 #include "solarus/graphics/Color.h"
21 #include "solarus/graphics/Sprite.h"
22 #include "solarus/graphics/Surface.h"
23 #include "solarus/graphics/TransitionFade.h"
24 #include "solarus/graphics/Video.h"
25 #include "solarus/lua/LuaContext.h"
26 #include "solarus/lua/LuaTools.h"
27 #include "solarus/movements/Movement.h"
28 
29 namespace Solarus {
30 
31 /**
32  * Name of the Lua table representing the surface module.
33  */
34 const std::string LuaContext::surface_module_name = "sol.surface";
35 
36 /**
37  * \brief Initializes the surface features provided to Lua.
38  */
register_surface_module()39 void LuaContext::register_surface_module() {
40 
41   const std::vector<luaL_Reg> functions = {
42       { "create", surface_api_create }
43   };
44 
45   std::vector<luaL_Reg> methods = {
46       { "get_size", surface_api_get_size },
47       { "clear", surface_api_clear },
48       { "fill_color", surface_api_fill_color },
49       { "draw", drawable_api_draw },
50       { "draw_region", drawable_api_draw_region },
51       { "get_blend_mode", drawable_api_get_blend_mode },
52       { "set_blend_mode", drawable_api_set_blend_mode },
53       { "get_opacity", drawable_api_get_opacity },
54       { "set_opacity", drawable_api_set_opacity },
55       { "fade_in", drawable_api_fade_in },
56       { "fade_out", drawable_api_fade_out },
57       { "get_xy", drawable_api_get_xy },
58       { "set_xy", drawable_api_set_xy },
59       { "get_movement", drawable_api_get_movement },
60       { "stop_movement", drawable_api_stop_movement },
61       { "get_pixels", surface_api_get_pixels } //Was already present in 1.5.x
62   };
63 
64   if (CurrentQuest::is_format_at_least({ 1, 6 })) {
65     methods.insert(methods.end(), {
66       { "set_color_modulation", drawable_api_set_color_modulation },
67       { "get_color_modulation", drawable_api_get_color_modulation },
68       { "set_pixels", surface_api_set_pixels },
69       { "set_shader", drawable_api_set_shader },
70       { "get_shader", drawable_api_get_shader },
71       { "set_rotation", drawable_api_set_rotation },
72       { "get_rotation", drawable_api_get_rotation },
73       { "set_scale", drawable_api_set_scale },
74       { "get_scale", drawable_api_get_scale },
75       { "set_transformation_origin", drawable_api_set_transformation_origin },
76       { "get_transformation_origin", drawable_api_get_transformation_origin },
77       { "gl_bind_as_texture", surface_api_gl_bind_as_texture},
78       { "gl_bind_as_target", surface_api_gl_bind_as_target}
79     });
80   }
81 
82   const std::vector<luaL_Reg> metamethods = {
83       { "__gc", drawable_meta_gc },
84       { "__newindex", userdata_meta_newindex_as_table },
85       { "__index", userdata_meta_index_as_table }
86   };
87 
88   register_type(surface_module_name, functions, methods, metamethods);
89 }
90 
91 /**
92  * \brief Returns whether a value is a userdata of type surface.
93  * \param l A Lua context.
94  * \param index An index in the stack.
95  * \return true if the value at this index is a surface.
96  */
is_surface(lua_State * l,int index)97 bool LuaContext::is_surface(lua_State* l, int index) {
98   return is_userdata(l, index, surface_module_name);
99 }
100 
101 /**
102  * \brief Checks that the userdata at the specified index of the stack is a
103  * surface and returns it.
104  * \param l a Lua context
105  * \param index an index in the stack
106  * \return the surface
107  */
check_surface(lua_State * l,int index)108 SurfacePtr LuaContext::check_surface(lua_State* l, int index) {
109   return std::static_pointer_cast<Surface>(check_userdata(
110       l, index, surface_module_name
111   ));
112 }
113 
114 /**
115  * \brief Pushes a surface userdata onto the stack.
116  * \param l a Lua context
117  * \param surface a surface
118  */
push_surface(lua_State * l,Surface & surface)119 void LuaContext::push_surface(lua_State* l, Surface& surface) {
120   push_userdata(l, surface);
121 }
122 
123 /**
124  * \brief Implementation of sol.surface.create().
125  * \param l The Lua context that is calling this function.
126  * \return Number of values to return to Lua.
127  */
surface_api_create(lua_State * l)128 int LuaContext::surface_api_create(lua_State* l) {
129 
130   return state_boundary_handle(l, [&] {
131     SurfacePtr surface;
132     if (lua_gettop(l) == 0) {
133       // create an empty surface with the screen size
134       surface = Surface::create(Video::get_quest_size());
135     }
136     else if (lua_type(l, 1) == LUA_TNUMBER) {
137       // create an empty surface with the specified size
138       int width = LuaTools::check_int(l, 1);
139       int height = LuaTools::check_int(l, 2);
140       surface = Surface::create(width, height);
141     }
142     else if (lua_type(l, 1) == LUA_TSTRING) {
143       // load from a file
144       const std::string& file_name = lua_tostring(l, 1);
145       bool language_specific = LuaTools::opt_boolean(l, 2, false);
146       surface = Surface::create(file_name, language_specific ?
147           Surface::DIR_LANGUAGE : Surface::DIR_SPRITES);
148     }
149     else {
150       LuaTools::type_error(l, 1, "number, string or no value");
151     }
152 
153     if (surface == nullptr) {
154       // Image file not found or not valid.
155       lua_pushnil(l);
156     }
157     else {
158       get().add_drawable(surface);
159       push_surface(l, *surface);
160     }
161     return 1;
162   });
163 }
164 
165 /**
166  * \brief Implementation of surface:get_size().
167  * \param l The Lua context that is calling this function.
168  * \return Number of values to return to Lua.
169  */
surface_api_get_size(lua_State * l)170 int LuaContext::surface_api_get_size(lua_State* l) {
171 
172   return state_boundary_handle(l, [&] {
173     Surface& surface = *check_surface(l, 1);
174 
175     lua_pushinteger(l, surface.get_width());
176     lua_pushinteger(l, surface.get_height());
177     return 2;
178   });
179 }
180 
181 /**
182  * \brief Implementation of surface:clear().
183  * \param l The Lua context that is calling this function.
184  * \return Number of values to return to Lua.
185  */
surface_api_clear(lua_State * l)186 int LuaContext::surface_api_clear(lua_State* l) {
187 
188   return state_boundary_handle(l, [&] {
189     Surface& surface = *check_surface(l, 1);
190 
191     surface.clear();
192 
193     return 0;
194   });
195 }
196 
197 /**
198  * \brief Implementation of surface:fill_color().
199  * \param l The Lua context that is calling this function.
200  * \return Number of values to return to Lua.
201  */
surface_api_fill_color(lua_State * l)202 int LuaContext::surface_api_fill_color(lua_State* l) {
203 
204   return state_boundary_handle(l, [&] {
205     Surface& surface = *check_surface(l, 1);
206     Color color = LuaTools::check_color(l, 2);
207 
208     if (lua_gettop(l) >= 3) {
209       int x = LuaTools::check_int(l, 3);
210       int y = LuaTools::check_int(l, 4);
211       int width = LuaTools::check_int(l, 5);
212       int height = LuaTools::check_int(l, 6);
213       Rectangle where(x, y, width, height);
214       surface.fill_with_color(color, where);
215     }
216     else {
217       surface.fill_with_color(color);
218     }
219 
220     return 0;
221   });
222 }
223 
224 /**
225  * \brief Implementation of surface:get_pixels().
226  * \param l The Lua context that is calling this function.
227  * \return Number of values to return to Lua.
228  */
surface_api_get_pixels(lua_State * l)229 int LuaContext::surface_api_get_pixels(lua_State* l) {
230 
231   return state_boundary_handle(l, [&] {
232     Surface& surface = *check_surface(l, 1);
233     // TODO optional parameters x, y, width, height
234 
235     push_string(l, surface.get_pixels());
236     return 1;
237   });
238 }
239 
240 /**
241  * \brief Implementation of surface:set_pixels().
242  * \param l The Lua context that is calling this function.
243  * \return Number of values to return to Lua.
244  */
surface_api_set_pixels(lua_State * l)245 int LuaContext::surface_api_set_pixels(lua_State* l) {
246   return state_boundary_handle(l, [&] {
247     Surface& surface = *check_surface(l,1);
248     const std::string& buffer = LuaTools::check_string(l,2);
249     surface.set_pixels(buffer);
250     return 0;
251   });
252 }
253 
254 /**
255  * \brief Implementation of surface:gl_bind_as_texture().
256  * \param l The Lua context that is calling this function.
257  * \return Number of values to return to Lua.
258  */
surface_api_gl_bind_as_texture(lua_State * l)259 int LuaContext::surface_api_gl_bind_as_texture(lua_State* l) {
260   return state_boundary_handle(l, [&] {
261       const Surface& surface = *check_surface(l,1);
262       surface.bind_as_texture();
263       return 0;
264   });
265 }
266 
267 /**
268  * \brief Implementation of surface:gl_bind_as_target().
269  * \param l The Lua context that is calling this function.
270  * \return Number of values to return to Lua.
271  */
surface_api_gl_bind_as_target(lua_State * l)272 int LuaContext::surface_api_gl_bind_as_target(lua_State* l) {
273   return state_boundary_handle(l, [&] {
274       Surface& surface = *check_surface(l,1);
275       surface.bind_as_target();
276       return 0;
277   });
278 }
279 
280 }
281 
282