1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21#include "wrap_ImageData.h"
22
23#include "data/wrap_Data.h"
24#include "filesystem/File.h"
25#include "filesystem/Filesystem.h"
26
27// Shove the wrap_ImageData.lua code directly into a raw string literal.
28static const char imagedata_lua[] =
29#include "wrap_ImageData.lua"
30;
31
32namespace love
33{
34namespace image
35{
36
37/**
38 * NOTE: Additional wrapper code is in wrap_ImageData.lua. Be sure to keep it
39 * in sync with any changes made to this file!
40 **/
41
42ImageData *luax_checkimagedata(lua_State *L, int idx)
43{
44 return luax_checktype<ImageData>(L, idx);
45}
46
47int w_ImageData_clone(lua_State *L)
48{
49 ImageData *t = luax_checkimagedata(L, 1), *c = nullptr;
50 luax_catchexcept(L, [&](){ c = t->clone(); });
51 luax_pushtype(L, c);
52 c->release();
53 return 1;
54}
55
56int w_ImageData_getFormat(lua_State *L)
57{
58 ImageData *t = luax_checkimagedata(L, 1);
59 PixelFormat format = t->getFormat();
60 const char *fstr = nullptr;
61
62 if (!getConstant(format, fstr))
63 return luaL_error(L, "Unknown pixel format.");
64
65 lua_pushstring(L, fstr);
66 return 1;
67}
68
69int w_ImageData_getWidth(lua_State *L)
70{
71 ImageData *t = luax_checkimagedata(L, 1);
72 lua_pushinteger(L, t->getWidth());
73 return 1;
74}
75
76int w_ImageData_getHeight(lua_State *L)
77{
78 ImageData *t = luax_checkimagedata(L, 1);
79 lua_pushinteger(L, t->getHeight());
80 return 1;
81}
82
83int w_ImageData_getDimensions(lua_State *L)
84{
85 ImageData *t = luax_checkimagedata(L, 1);
86 lua_pushinteger(L, t->getWidth());
87 lua_pushinteger(L, t->getHeight());
88 return 2;
89}
90
91int w_ImageData_getPixel(lua_State *L)
92{
93 ImageData *t = luax_checkimagedata(L, 1);
94 int x = (int) luaL_checkinteger(L, 2);
95 int y = (int) luaL_checkinteger(L, 3);
96
97 Colorf c;
98 luax_catchexcept(L, [&](){ t->getPixel(x, y, c); });
99
100 lua_pushnumber(L, c.r);
101 lua_pushnumber(L, c.g);
102 lua_pushnumber(L, c.b);
103 lua_pushnumber(L, c.a);
104 return 4;
105}
106
107int w_ImageData_setPixel(lua_State *L)
108{
109 ImageData *t = luax_checkimagedata(L, 1);
110 int x = (int) luaL_checkinteger(L, 2);
111 int y = (int) luaL_checkinteger(L, 3);
112
113 int components = getPixelFormatColorComponents(t->getFormat());
114
115 Colorf c;
116
117 if (lua_istable(L, 4))
118 {
119 for (int i = 1; i <= components; i++)
120 lua_rawgeti(L, components, i);
121
122 c.r = (float) luaL_checknumber(L, -components);
123 if (components > 1)
124 c.g = (float) luaL_checknumber(L, (-components) + 1);
125 if (components > 2)
126 c.b = (float) luaL_checknumber(L, (-components) + 2);
127 if (components > 3)
128 c.a = (float) luaL_optnumber(L, (-components) + 3, 1.0);
129
130 lua_pop(L, components);
131 }
132 else
133 {
134 c.r = (float) luaL_checknumber(L, 4);
135 if (components > 1)
136 c.g = (float) luaL_checknumber(L, 5);
137 if (components > 2)
138 c.b = (float) luaL_checknumber(L, 6);
139 if (components > 3)
140 c.a = (float) luaL_optnumber(L, 7, 1.0);
141 }
142
143 luax_catchexcept(L, [&](){ t->setPixel(x, y, c); });
144 return 0;
145}
146
147// ImageData:mapPixel. Not thread-safe! See wrap_ImageData.lua for the thread-
148// safe wrapper function.
149int w_ImageData__mapPixelUnsafe(lua_State *L)
150{
151 ImageData *t = luax_checkimagedata(L, 1);
152 luaL_checktype(L, 2, LUA_TFUNCTION);
153
154 // No optints because we assume they're done in the wrapper function.
155 int sx = (int) lua_tonumber(L, 3);
156 int sy = (int) lua_tonumber(L, 4);
157 int w = (int) lua_tonumber(L, 5);
158 int h = (int) lua_tonumber(L, 6);
159
160 if (!(t->inside(sx, sy) && t->inside(sx+w-1, sy+h-1)))
161 return luaL_error(L, "Invalid rectangle dimensions.");
162
163 int iw = t->getWidth();
164
165 PixelFormat format = t->getFormat();
166 int components = getPixelFormatColorComponents(format);
167
168 auto pixelsetfunction = t->getPixelSetFunction();
169 auto pixelgetfunction = t->getPixelGetFunction();
170
171 uint8 *data = (uint8 *) t->getData();
172 size_t pixelsize = t->getPixelSize();
173
174 for (int y = sy; y < sy+h; y++)
175 {
176 for (int x = sx; x < sx+w; x++)
177 {
178 auto pixeldata = (ImageData::Pixel *) (data + (y * iw + x) * pixelsize);
179
180 Colorf c;
181 pixelgetfunction(pixeldata, c);
182
183 lua_pushvalue(L, 2); // ImageData
184
185 lua_pushnumber(L, x);
186 lua_pushnumber(L, y);
187
188 lua_pushnumber(L, c.r);
189 lua_pushnumber(L, c.g);
190 lua_pushnumber(L, c.b);
191 lua_pushnumber(L, c.a);
192
193 lua_call(L, 6, 4);
194
195 c.r = (float) luaL_checknumber(L, -4);
196 if (components > 1)
197 c.g = (float) luaL_checknumber(L, -3);
198 if (components > 2)
199 c.b = (float) luaL_checknumber(L, -2);
200 if (components > 3)
201 c.a = (float) luaL_optnumber(L, -1, 1.0);
202
203 pixelsetfunction(c, pixeldata);
204
205 lua_pop(L, 4); // Pop return values.
206 }
207 }
208
209 return 0;
210}
211
212int w_ImageData_paste(lua_State *L)
213{
214 ImageData *t = luax_checkimagedata(L, 1);
215 ImageData *src = luax_checkimagedata(L, 2);
216 int dx = (int) luaL_checkinteger(L, 3);
217 int dy = (int) luaL_checkinteger(L, 4);
218 int sx = (int) luaL_optinteger(L, 5, 0);
219 int sy = (int) luaL_optinteger(L, 6, 0);
220 int sw = (int) luaL_optinteger(L, 7, src->getWidth());
221 int sh = (int) luaL_optinteger(L, 8, src->getHeight());
222 t->paste((love::image::ImageData *)src, dx, dy, sx, sy, sw, sh);
223 return 0;
224}
225
226int w_ImageData_encode(lua_State *L)
227{
228 ImageData *t = luax_checkimagedata(L, 1);
229
230 FormatHandler::EncodedFormat format;
231 const char *fmt = luaL_checkstring(L, 2);
232 if (!ImageData::getConstant(fmt, format))
233 return luax_enumerror(L, "encoded image format", ImageData::getConstants(format), fmt);
234
235 bool hasfilename = false;
236
237 std::string filename = "Image." + std::string(fmt);
238 if (!lua_isnoneornil(L, 3))
239 {
240 hasfilename = true;
241 filename = luax_checkstring(L, 3);
242 }
243
244 love::filesystem::FileData *filedata = nullptr;
245 luax_catchexcept(L, [&](){ filedata = t->encode(format, filename.c_str(), hasfilename); });
246
247 luax_pushtype(L, filedata);
248 filedata->release();
249
250 return 1;
251}
252
253int w_ImageData__performAtomic(lua_State *L)
254{
255 ImageData *t = luax_checkimagedata(L, 1);
256 int err = 0;
257
258 {
259 love::thread::Lock lock(t->getMutex());
260 // call the function, passing any user-specified arguments.
261 err = lua_pcall(L, lua_gettop(L) - 2, LUA_MULTRET, 0);
262 }
263
264 // Unfortunately, this eats the stack trace, too bad.
265 if (err != 0)
266 return lua_error(L);
267
268 // The function and everything after it in the stack are eaten by the pcall,
269 // leaving only the ImageData object. Everything else is a return value.
270 return lua_gettop(L) - 1;
271}
272
273// C functions in a struct, necessary for the FFI versions of ImageData methods.
274struct FFI_ImageData
275{
276 void (*lockMutex)(Proxy *p);
277 void (*unlockMutex)(Proxy *p);
278
279 float (*float16to32)(float16 f);
280 float16 (*float32to16)(float f);
281
282 float (*float11to32)(float11 f);
283 float11 (*float32to11)(float f);
284
285 float (*float10to32)(float10 f);
286 float10 (*float32to10)(float f);
287};
288
289static FFI_ImageData ffifuncs =
290{
291 [](Proxy *p) // lockMutex
292 {
293 // We don't do any type-checking for the Proxy here since these functions
294 // are always called from code which has already done type checking.
295 ImageData *i = (ImageData *) p->object;
296 i->getMutex()->lock();
297 },
298
299 [](Proxy *p) // unlockMutex
300 {
301 ImageData *i = (ImageData *) p->object;
302 i->getMutex()->unlock();
303 },
304
305 float16to32,
306 float32to16,
307 float11to32,
308 float32to11,
309 float10to32,
310 float32to10,
311};
312
313static const luaL_Reg w_ImageData_functions[] =
314{
315 { "clone", w_ImageData_clone },
316 { "getFormat", w_ImageData_getFormat },
317 { "getWidth", w_ImageData_getWidth },
318 { "getHeight", w_ImageData_getHeight },
319 { "getDimensions", w_ImageData_getDimensions },
320 { "getPixel", w_ImageData_getPixel },
321 { "setPixel", w_ImageData_setPixel },
322 { "paste", w_ImageData_paste },
323 { "encode", w_ImageData_encode },
324
325 // Used in the Lua wrapper code.
326 { "_mapPixelUnsafe", w_ImageData__mapPixelUnsafe },
327 { "_performAtomic", w_ImageData__performAtomic },
328
329 { 0, 0 }
330};
331
332extern "C" int luaopen_imagedata(lua_State *L)
333{
334 int ret = luax_register_type(L, &ImageData::type, data::w_Data_functions, w_ImageData_functions, nullptr);
335
336 love::data::luax_rundatawrapper(L, ImageData::type);
337 luax_runwrapper(L, imagedata_lua, sizeof(imagedata_lua), "ImageData.lua", ImageData::type, &ffifuncs);
338
339 return ret;
340}
341
342} // image
343} // love
344