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 "common/config.h"
22#include "wrap_Graphics.h"
23#include "Texture.h"
24#include "image/ImageData.h"
25#include "image/Image.h"
26#include "font/Rasterizer.h"
27#include "filesystem/Filesystem.h"
28#include "filesystem/wrap_Filesystem.h"
29#include "video/VideoStream.h"
30#include "image/wrap_Image.h"
31#include "common/Reference.h"
32#include "math/wrap_Transform.h"
33#include "thread/wrap_Channel.h"
34
35#include "opengl/Graphics.h"
36
37#include <cassert>
38#include <cstring>
39#include <cstdlib>
40
41#include <algorithm>
42
43// Shove the wrap_Graphics.lua code directly into a raw string literal.
44static const char graphics_lua[] =
45#include "wrap_Graphics.lua"
46;
47
48// This is in a separate file because VS2013 has a 16KB limit for raw strings..
49static const char graphics_shader_lua[] =
50#include "wrap_GraphicsShader.lua"
51;
52
53namespace love
54{
55namespace graphics
56{
57
58#define instance() (Module::getInstance<Graphics>(Module::M_GRAPHICS))
59
60static int luax_checkgraphicscreated(lua_State *L)
61{
62 if (!instance()->isCreated())
63 return luaL_error(L, "love.graphics cannot function without a window!");
64 return 0;
65}
66
67int w_reset(lua_State *)
68{
69 instance()->reset();
70 return 0;
71}
72
73int w_clear(lua_State *L)
74{
75 OptionalColorf color(Colorf(0.0f, 0.0f, 0.0f, 0.0f));
76 std::vector<OptionalColorf> colors;
77
78 OptionalInt stencil(0);
79 OptionalDouble depth(1.0);
80
81 int argtype = lua_type(L, 1);
82 int startidx = -1;
83
84 if (argtype == LUA_TTABLE)
85 {
86 int maxn = lua_gettop(L);
87 colors.reserve(maxn);
88
89 for (int i = 0; i < maxn; i++)
90 {
91 argtype = lua_type(L, i + 1);
92
93 if (argtype == LUA_TNUMBER || argtype == LUA_TBOOLEAN)
94 {
95 startidx = i + 1;
96 break;
97 }
98 else if (argtype == LUA_TNIL || argtype == LUA_TNONE || luax_objlen(L, i + 1) == 0)
99 {
100 colors.push_back(OptionalColorf());
101 continue;
102 }
103
104 for (int j = 1; j <= 4; j++)
105 lua_rawgeti(L, i + 1, j);
106
107 OptionalColorf c;
108 c.hasValue = true;
109 c.value.r = (float) luaL_checknumber(L, -4);
110 c.value.g = (float) luaL_checknumber(L, -3);
111 c.value.b = (float) luaL_checknumber(L, -2);
112 c.value.a = (float) luaL_optnumber(L, -1, 1.0);
113 colors.push_back(c);
114
115 lua_pop(L, 4);
116 }
117 }
118 else if (argtype == LUA_TBOOLEAN)
119 {
120 color.hasValue = luax_toboolean(L, 1);
121 startidx = 2;
122 }
123 else if (argtype != LUA_TNONE && argtype != LUA_TNIL)
124 {
125 color.hasValue = true;
126 color.value.r = (float) luaL_checknumber(L, 1);
127 color.value.g = (float) luaL_checknumber(L, 2);
128 color.value.b = (float) luaL_checknumber(L, 3);
129 color.value.a = (float) luaL_optnumber(L, 4, 1.0);
130 startidx = 5;
131 }
132
133 if (startidx >= 0)
134 {
135 argtype = lua_type(L, startidx);
136 if (argtype == LUA_TBOOLEAN)
137 stencil.hasValue = luax_toboolean(L, startidx);
138 else if (argtype == LUA_TNUMBER)
139 stencil.value = (int) luaL_checkinteger(L, startidx);
140
141 argtype = lua_type(L, startidx + 1);
142 if (argtype == LUA_TBOOLEAN)
143 depth.hasValue = luax_toboolean(L, startidx + 1);
144 else if (argtype == LUA_TNUMBER)
145 depth.value = luaL_checknumber(L, startidx + 1);
146 }
147
148 if (colors.empty())
149 luax_catchexcept(L, [&]() { instance()->clear(color, stencil, depth); });
150 else
151 luax_catchexcept(L, [&]() { instance()->clear(colors, stencil, depth); });
152
153 return 0;
154}
155
156int w_discard(lua_State *L)
157{
158 std::vector<bool> colorbuffers;
159
160 if (lua_istable(L, 1))
161 {
162 for (size_t i = 1; i <= luax_objlen(L, 1); i++)
163 {
164 lua_rawgeti(L, 1, i);
165 colorbuffers.push_back(luax_optboolean(L, -1, true));
166 lua_pop(L, 1);
167 }
168 }
169 else
170 {
171 bool discardcolor = luax_optboolean(L, 1, true);
172 size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().colors.size());
173 colorbuffers = std::vector<bool>(numbuffers, discardcolor);
174 }
175
176 bool depthstencil = luax_optboolean(L, 2, true);
177 instance()->discard(colorbuffers, depthstencil);
178 return 0;
179}
180
181int w_present(lua_State *L)
182{
183 luax_catchexcept(L, [&]() { instance()->present(L); });
184 return 0;
185}
186
187int w_isCreated(lua_State *L)
188{
189 luax_pushboolean(L, instance()->isCreated());
190 return 1;
191}
192
193int w_isActive(lua_State *L)
194{
195 luax_pushboolean(L, instance()->isActive());
196 return 1;
197}
198
199int w_isGammaCorrect(lua_State *L)
200{
201 luax_pushboolean(L, graphics::isGammaCorrect());
202 return 1;
203}
204
205int w_getWidth(lua_State *L)
206{
207 lua_pushinteger(L, instance()->getWidth());
208 return 1;
209}
210
211int w_getHeight(lua_State *L)
212{
213 lua_pushinteger(L, instance()->getHeight());
214 return 1;
215}
216
217int w_getDimensions(lua_State *L)
218{
219 lua_pushinteger(L, instance()->getWidth());
220 lua_pushinteger(L, instance()->getHeight());
221 return 2;
222}
223
224int w_getPixelWidth(lua_State *L)
225{
226 lua_pushinteger(L, instance()->getPixelWidth());
227 return 1;
228}
229
230int w_getPixelHeight(lua_State *L)
231{
232 lua_pushinteger(L, instance()->getPixelHeight());
233 return 1;
234}
235
236int w_getPixelDimensions(lua_State *L)
237{
238 lua_pushinteger(L, instance()->getPixelWidth());
239 lua_pushinteger(L, instance()->getPixelHeight());
240 return 2;
241}
242
243int w_getDPIScale(lua_State *L)
244{
245 lua_pushnumber(L, instance()->getScreenDPIScale());
246 return 1;
247}
248
249static Graphics::RenderTarget checkRenderTarget(lua_State *L, int idx)
250{
251 lua_rawgeti(L, idx, 1);
252 Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
253 lua_pop(L, 1);
254
255 TextureType type = target.canvas->getTextureType();
256 if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
257 target.slice = luax_checkintflag(L, idx, "layer") - 1;
258 else if (type == TEXTURE_CUBE)
259 target.slice = luax_checkintflag(L, idx, "face") - 1;
260
261 target.mipmap = luax_intflag(L, idx, "mipmap", 1) - 1;
262
263 return target;
264}
265
266int w_setCanvas(lua_State *L)
267{
268 // Disable stencil writes.
269 luax_catchexcept(L, [](){ instance()->stopDrawToStencilBuffer(); });
270
271 // called with none -> reset to default buffer
272 if (lua_isnoneornil(L, 1))
273 {
274 instance()->setCanvas();
275 return 0;
276 }
277
278 bool is_table = lua_istable(L, 1);
279 Graphics::RenderTargets targets;
280
281 if (is_table)
282 {
283 lua_rawgeti(L, 1, 1);
284 bool table_of_tables = lua_istable(L, -1);
285 lua_pop(L, 1);
286
287 for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
288 {
289 lua_rawgeti(L, 1, i);
290
291 if (table_of_tables)
292 targets.colors.push_back(checkRenderTarget(L, -1));
293 else
294 {
295 targets.colors.emplace_back(luax_checkcanvas(L, -1), 0);
296
297 if (targets.colors.back().canvas->getTextureType() != TEXTURE_2D)
298 return luaL_error(L, "Non-2D canvases must use the table-of-tables variant of setCanvas.");
299 }
300
301 lua_pop(L, 1);
302 }
303
304 uint32 tempdepthflag = Graphics::TEMPORARY_RT_DEPTH;
305 uint32 tempstencilflag = Graphics::TEMPORARY_RT_STENCIL;
306
307 lua_getfield(L, 1, "depthstencil");
308 int dstype = lua_type(L, -1);
309 if (dstype == LUA_TTABLE)
310 targets.depthStencil = checkRenderTarget(L, -1);
311 else if (dstype == LUA_TBOOLEAN)
312 targets.temporaryRTFlags |= luax_toboolean(L, -1) ? (tempdepthflag | tempstencilflag) : 0;
313 else if (dstype != LUA_TNONE && dstype != LUA_TNIL)
314 targets.depthStencil.canvas = luax_checkcanvas(L, -1);
315 lua_pop(L, 1);
316
317 if (targets.depthStencil.canvas == nullptr && (targets.temporaryRTFlags & tempdepthflag) == 0)
318 targets.temporaryRTFlags |= luax_boolflag(L, 1, "depth", false) ? tempdepthflag : 0;
319
320 if (targets.depthStencil.canvas == nullptr && (targets.temporaryRTFlags & tempstencilflag) == 0)
321 targets.temporaryRTFlags |= luax_boolflag(L, 1, "stencil", false) ? tempstencilflag : 0;
322 }
323 else
324 {
325 for (int i = 1; i <= lua_gettop(L); i++)
326 {
327 Graphics::RenderTarget target(luax_checkcanvas(L, i), 0);
328 TextureType type = target.canvas->getTextureType();
329
330 if (i == 1 && type != TEXTURE_2D)
331 {
332 target.slice = (int) luaL_checkinteger(L, i + 1) - 1;
333 target.mipmap = (int) luaL_optinteger(L, i + 2, 1) - 1;
334 targets.colors.push_back(target);
335 break;
336 }
337 else if (type == TEXTURE_2D && lua_isnumber(L, i + 1))
338 {
339 target.mipmap = (int) luaL_optinteger(L, i + 1, 1) - 1;
340 i++;
341 }
342
343 if (i > 1 && type != TEXTURE_2D)
344 return luaL_error(L, "This variant of setCanvas only supports 2D texture types.");
345
346 targets.colors.push_back(target);
347 }
348 }
349
350 luax_catchexcept(L, [&]() {
351 if (targets.getFirstTarget().canvas != nullptr)
352 instance()->setCanvas(targets);
353 else
354 instance()->setCanvas();
355 });
356
357 return 0;
358}
359
360static void pushRenderTarget(lua_State *L, const Graphics::RenderTarget &rt)
361{
362 lua_createtable(L, 1, 2);
363
364 luax_pushtype(L, rt.canvas);
365 lua_rawseti(L, -2, 1);
366
367 TextureType type = rt.canvas->getTextureType();
368
369 if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
370 {
371 lua_pushnumber(L, rt.slice + 1);
372 lua_setfield(L, -2, "layer");
373 }
374 else if (type == TEXTURE_CUBE)
375 {
376 lua_pushnumber(L, rt.slice + 1);
377 lua_setfield(L, -2, "face");
378 }
379
380 lua_pushnumber(L, rt.mipmap + 1);
381 lua_setfield(L, -2, "mipmap");
382}
383
384int w_getCanvas(lua_State *L)
385{
386 Graphics::RenderTargets targets = instance()->getCanvas();
387 int ntargets = (int) targets.colors.size();
388
389 if (ntargets == 0)
390 {
391 lua_pushnil(L);
392 return 1;
393 }
394
395 bool shouldUseTablesVariant = targets.depthStencil.canvas != nullptr;
396
397 if (!shouldUseTablesVariant)
398 {
399 for (const auto &rt : targets.colors)
400 {
401 if (rt.mipmap != 0 || rt.canvas->getTextureType() != TEXTURE_2D)
402 {
403 shouldUseTablesVariant = true;
404 break;
405 }
406 }
407 }
408
409 if (shouldUseTablesVariant)
410 {
411 lua_createtable(L, ntargets, 0);
412
413 for (int i = 0; i < ntargets; i++)
414 {
415 pushRenderTarget(L, targets.colors[i]);
416 lua_rawseti(L, -2, i + 1);
417 }
418
419 if (targets.depthStencil.canvas != nullptr)
420 {
421 pushRenderTarget(L, targets.depthStencil);
422 lua_setfield(L, -2, "depthstencil");
423 }
424
425 return 1;
426 }
427 else
428 {
429 for (const auto &rt : targets.colors)
430 luax_pushtype(L, rt.canvas);
431
432 return ntargets;
433 }
434}
435
436static void screenshotFunctionCallback(const Graphics::ScreenshotInfo *info, love::image::ImageData *i, void *gd)
437{
438 if (info == nullptr)
439 return;
440
441 lua_State *L = (lua_State *) gd;
442 Reference *ref = (Reference *) info->data;
443
444 if (i != nullptr && L != nullptr)
445 {
446 if (ref == nullptr)
447 luaL_error(L, "Internal error in screenshot callback.");
448
449 ref->push(L);
450 delete ref;
451 luax_pushtype(L, i);
452 lua_call(L, 1, 0);
453 }
454 else
455 delete ref;
456}
457
458struct ScreenshotFileInfo
459{
460 std::string filename;
461 image::FormatHandler::EncodedFormat format;
462};
463
464static void screenshotFileCallback(const Graphics::ScreenshotInfo *info, love::image::ImageData *i, void * /*gd*/)
465{
466 if (info == nullptr)
467 return;
468
469 ScreenshotFileInfo *fileinfo = (ScreenshotFileInfo *) info->data;
470
471 if (i != nullptr && fileinfo != nullptr)
472 {
473 try
474 {
475 i->encode(fileinfo->format, fileinfo->filename.c_str(), true);
476 }
477 catch (love::Exception &e)
478 {
479 printf("Screenshot encoding or saving failed: %s", e.what());
480 // Do nothing...
481 }
482 }
483
484 delete fileinfo;
485}
486
487static void screenshotChannelCallback(const Graphics::ScreenshotInfo *info, love::image::ImageData *i, void * /*gd*/)
488{
489 if (info == nullptr)
490 return;
491
492 auto *channel = (love::thread::Channel *) info->data;
493
494 if (channel != nullptr)
495 {
496 if (i != nullptr)
497 channel->push(Variant(&love::image::ImageData::type, i));
498
499 channel->release();
500 }
501}
502
503int w_captureScreenshot(lua_State *L)
504{
505 Graphics::ScreenshotInfo info;
506
507 if (lua_isfunction(L, 1))
508 {
509 lua_pushvalue(L, 1);
510 info.data = luax_refif(L, LUA_TFUNCTION);
511 lua_pop(L, 1);
512 info.callback = screenshotFunctionCallback;
513 }
514 else if (lua_isstring(L, 1))
515 {
516 std::string filename = luax_checkstring(L, 1);
517 std::string ext;
518
519 size_t dotpos = filename.rfind('.');
520
521 if (dotpos != std::string::npos)
522 ext = filename.substr(dotpos + 1);
523
524 std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
525
526 image::FormatHandler::EncodedFormat format;
527 if (!image::ImageData::getConstant(ext.c_str(), format))
528 return luax_enumerror(L, "encoded image format", image::ImageData::getConstants(format), ext.c_str());
529
530 ScreenshotFileInfo *fileinfo = new ScreenshotFileInfo;
531 fileinfo->filename = filename;
532 fileinfo->format = format;
533
534 info.data = fileinfo;
535 info.callback = screenshotFileCallback;
536 }
537 else if (luax_istype(L, 1, love::thread::Channel::type))
538 {
539 auto *channel = love::thread::luax_checkchannel(L, 1);
540 channel->retain();
541 info.data = channel;
542 info.callback = screenshotChannelCallback;
543 }
544 else
545 return luax_typerror(L, 1, "function, string, or Channel");
546
547 luax_catchexcept(L,
548 [&]() { instance()->captureScreenshot(info); },
549 [&](bool except) { if (except) info.callback(&info, nullptr, nullptr); }
550 );
551
552 return 0;
553}
554
555int w_setScissor(lua_State *L)
556{
557 int nargs = lua_gettop(L);
558
559 if (nargs == 0 || (nargs == 4 && lua_isnil(L, 1) && lua_isnil(L, 2)
560 && lua_isnil(L, 3) && lua_isnil(L, 4)))
561 {
562 instance()->setScissor();
563 return 0;
564 }
565
566 Rect rect;
567 rect.x = (int) luaL_checkinteger(L, 1);
568 rect.y = (int) luaL_checkinteger(L, 2);
569 rect.w = (int) luaL_checkinteger(L, 3);
570 rect.h = (int) luaL_checkinteger(L, 4);
571
572 if (rect.w < 0 || rect.h < 0)
573 return luaL_error(L, "Can't set scissor with negative width and/or height.");
574
575 instance()->setScissor(rect);
576 return 0;
577}
578
579int w_intersectScissor(lua_State *L)
580{
581 Rect rect;
582 rect.x = (int) luaL_checkinteger(L, 1);
583 rect.y = (int) luaL_checkinteger(L, 2);
584 rect.w = (int) luaL_checkinteger(L, 3);
585 rect.h = (int) luaL_checkinteger(L, 4);
586
587 if (rect.w < 0 || rect.h < 0)
588 return luaL_error(L, "Can't set scissor with negative width and/or height.");
589
590 instance()->intersectScissor(rect);
591 return 0;
592}
593
594int w_getScissor(lua_State *L)
595{
596 Rect rect;
597 if (!instance()->getScissor(rect))
598 return 0;
599
600 lua_pushinteger(L, rect.x);
601 lua_pushinteger(L, rect.y);
602 lua_pushinteger(L, rect.w);
603 lua_pushinteger(L, rect.h);
604
605 return 4;
606}
607
608int w_stencil(lua_State *L)
609{
610 luaL_checktype(L, 1, LUA_TFUNCTION);
611
612 StencilAction action = STENCIL_REPLACE;
613
614 if (!lua_isnoneornil(L, 2))
615 {
616 const char *actionstr = luaL_checkstring(L, 2);
617 if (!getConstant(actionstr, action))
618 return luax_enumerror(L, "stencil draw action", getConstants(action), actionstr);
619 }
620
621 int stencilvalue = (int) luaL_optinteger(L, 3, 1);
622
623 // Fourth argument: whether to keep the contents of the stencil buffer.
624 OptionalInt stencilclear;
625 int argtype = lua_type(L, 4);
626 if (argtype == LUA_TNONE || argtype == LUA_TNIL || (argtype == LUA_TBOOLEAN && luax_toboolean(L, 4) == false))
627 stencilclear.set(0);
628 else if (argtype == LUA_TNUMBER)
629 stencilclear.set((int) luaL_checkinteger(L, 4));
630 else if (argtype != LUA_TBOOLEAN)
631 luaL_checktype(L, 4, LUA_TBOOLEAN);
632
633 if (stencilclear.hasValue)
634 instance()->clear(OptionalColorf(), stencilclear, OptionalDouble());
635
636 luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); });
637
638 // Call stencilfunc()
639 lua_pushvalue(L, 1);
640 lua_call(L, 0, 0);
641
642 luax_catchexcept(L, [&](){ instance()->stopDrawToStencilBuffer(); });
643 return 0;
644}
645
646int w_setStencilTest(lua_State *L)
647{
648 // COMPARE_ALWAYS effectively disables stencil testing.
649 CompareMode compare = COMPARE_ALWAYS;
650 int comparevalue = 0;
651
652 if (!lua_isnoneornil(L, 1))
653 {
654 const char *comparestr = luaL_checkstring(L, 1);
655 if (!getConstant(comparestr, compare))
656 return luax_enumerror(L, "compare mode", getConstants(compare), comparestr);
657
658 comparevalue = (int) luaL_checkinteger(L, 2);
659 }
660
661 luax_catchexcept(L, [&](){ instance()->setStencilTest(compare, comparevalue); });
662 return 0;
663}
664
665int w_getStencilTest(lua_State *L)
666{
667 CompareMode compare = COMPARE_ALWAYS;
668 int comparevalue = 1;
669
670 instance()->getStencilTest(compare, comparevalue);
671
672 const char *comparestr;
673 if (!getConstant(compare, comparestr))
674 return luaL_error(L, "Unknown compare mode.");
675
676 lua_pushstring(L, comparestr);
677 lua_pushnumber(L, comparevalue);
678 return 2;
679}
680
681static void parseDPIScale(Data *d, float *dpiscale)
682{
683 auto fd = dynamic_cast<love::filesystem::FileData *>(d);
684 if (fd == nullptr)
685 return;
686
687 // Parse a density scale of 2.0 from "image@2x.png".
688 const std::string &fname = fd->getName();
689
690 size_t namelen = fname.length();
691 size_t atpos = fname.rfind('@');
692
693 if (atpos != std::string::npos && atpos + 2 < namelen
694 && (fname[namelen - 1] == 'x' || fname[namelen - 1] == 'X'))
695 {
696 char *end = nullptr;
697 long density = strtol(fname.c_str() + atpos + 1, &end, 10);
698 if (end != nullptr && density > 0 && dpiscale != nullptr)
699 *dpiscale = (float) density;
700 }
701}
702
703static Image::Settings w__optImageSettings(lua_State *L, int idx, bool &setdpiscale)
704{
705 Image::Settings s;
706
707 setdpiscale = false;
708 if (!lua_isnoneornil(L, idx))
709 {
710 luax_checktablefields<Image::SettingType>(L, idx, "image setting name", Image::getConstant);
711
712 s.mipmaps = luax_boolflag(L, idx, Image::getConstant(Image::SETTING_MIPMAPS), s.mipmaps);
713 s.linear = luax_boolflag(L, idx, Image::getConstant(Image::SETTING_LINEAR), s.linear);
714
715 lua_getfield(L, idx, Image::getConstant(Image::SETTING_DPI_SCALE));
716 if (lua_isnumber(L, -1))
717 {
718 s.dpiScale = (float) lua_tonumber(L, -1);
719 setdpiscale = true;
720 }
721 lua_pop(L, 1);
722 }
723
724 return s;
725}
726
727static std::pair<StrongRef<image::ImageData>, StrongRef<image::CompressedImageData>>
728getImageData(lua_State *L, int idx, bool allowcompressed, float *dpiscale)
729{
730 StrongRef<image::ImageData> idata;
731 StrongRef<image::CompressedImageData> cdata;
732
733 if (luax_istype(L, idx, image::ImageData::type))
734 idata.set(image::luax_checkimagedata(L, idx));
735 else if (luax_istype(L, idx, image::CompressedImageData::type))
736 cdata.set(image::luax_checkcompressedimagedata(L, idx));
737 else if (filesystem::luax_cangetdata(L, idx))
738 {
739 // Convert to ImageData / CompressedImageData.
740 auto imagemodule = Module::getInstance<image::Image>(Module::M_IMAGE);
741 if (imagemodule == nullptr)
742 luaL_error(L, "Cannot load images without the love.image module.");
743
744 StrongRef<Data> fdata(filesystem::luax_getdata(L, idx), Acquire::NORETAIN);
745
746 if (dpiscale != nullptr)
747 parseDPIScale(fdata, dpiscale);
748
749 if (allowcompressed && imagemodule->isCompressed(fdata))
750 luax_catchexcept(L, [&]() { cdata.set(imagemodule->newCompressedData(fdata), Acquire::NORETAIN); });
751 else
752 luax_catchexcept(L, [&]() { idata.set(imagemodule->newImageData(fdata), Acquire::NORETAIN); });
753 }
754 else
755 idata.set(image::luax_checkimagedata(L, idx));
756
757 return std::make_pair(idata, cdata);
758}
759
760static int w__pushNewImage(lua_State *L, Image::Slices &slices, const Image::Settings &settings)
761{
762 StrongRef<Image> i;
763 luax_catchexcept(L,
764 [&]() { i.set(instance()->newImage(slices, settings), Acquire::NORETAIN); },
765 [&](bool) { slices.clear(); }
766 );
767
768 luax_pushtype(L, i);
769 return 1;
770}
771
772int w_newCubeImage(lua_State *L)
773{
774 luax_checkgraphicscreated(L);
775
776 Image::Slices slices(TEXTURE_CUBE);
777
778 bool dpiscaleset = false;
779 Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset);
780 float *autodpiscale = dpiscaleset ? nullptr : &settings.dpiScale;
781
782 auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
783
784 if (!lua_istable(L, 1))
785 {
786 auto data = getImageData(L, 1, true, autodpiscale);
787
788 std::vector<StrongRef<love::image::ImageData>> faces;
789
790 if (data.first.get())
791 {
792 luax_catchexcept(L, [&](){ faces = imagemodule->newCubeFaces(data.first); });
793
794 for (int i = 0; i < (int) faces.size(); i++)
795 slices.set(i, 0, faces[i]);
796 }
797 else
798 slices.add(data.second, 0, 0, true, settings.mipmaps);
799 }
800 else
801 {
802 int tlen = (int) luax_objlen(L, 1);
803
804 if (luax_isarrayoftables(L, 1))
805 {
806 if (tlen != 6)
807 return luaL_error(L, "Cubemap images must have 6 faces.");
808
809 for (int face = 0; face < tlen; face++)
810 {
811 lua_rawgeti(L, 1, face + 1);
812 luaL_checktype(L, -1, LUA_TTABLE);
813
814 int miplen = std::max(1, (int) luax_objlen(L, -1));
815
816 for (int mip = 0; mip < miplen; mip++)
817 {
818 lua_rawgeti(L, -1, mip + 1);
819
820 auto data = getImageData(L, -1, true, face == 0 && mip == 0 ? autodpiscale : nullptr);
821 if (data.first.get())
822 slices.set(face, mip, data.first);
823 else
824 slices.set(face, mip, data.second->getSlice(0, 0));
825
826 lua_pop(L, 1);
827 }
828 }
829 }
830 else
831 {
832 bool usemipmaps = false;
833
834 for (int i = 0; i < tlen; i++)
835 {
836 lua_rawgeti(L, 1, i + 1);
837
838 auto data = getImageData(L, -1, true, i == 0 ? autodpiscale : nullptr);
839
840 if (data.first.get())
841 {
842 if (usemipmaps || data.first->getWidth() != data.first->getHeight())
843 {
844 usemipmaps = true;
845
846 std::vector<StrongRef<love::image::ImageData>> faces;
847 luax_catchexcept(L, [&](){ faces = imagemodule->newCubeFaces(data.first); });
848
849 for (int face = 0; face < (int) faces.size(); face++)
850 slices.set(face, i, faces[i]);
851 }
852 else
853 slices.set(i, 0, data.first);
854 }
855 else
856 slices.add(data.second, i, 0, false, settings.mipmaps);
857 }
858 }
859
860 lua_pop(L, tlen);
861 }
862
863 return w__pushNewImage(L, slices, settings);
864}
865
866int w_newArrayImage(lua_State *L)
867{
868 luax_checkgraphicscreated(L);
869
870 Image::Slices slices(TEXTURE_2D_ARRAY);
871
872 bool dpiscaleset = false;
873 Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset);
874 float *autodpiscale = dpiscaleset ? nullptr : &settings.dpiScale;
875
876 if (lua_istable(L, 1))
877 {
878 int tlen = std::max(1, (int) luax_objlen(L, 1));
879
880 if (luax_isarrayoftables(L, 1))
881 {
882 for (int slice = 0; slice < tlen; slice++)
883 {
884 lua_rawgeti(L, 1, slice + 1);
885 luaL_checktype(L, -1, LUA_TTABLE);
886
887 int miplen = std::max(1, (int) luax_objlen(L, -1));
888
889 for (int mip = 0; mip < miplen; mip++)
890 {
891 lua_rawgeti(L, -1, mip + 1);
892
893 auto data = getImageData(L, -1, true, slice == 0 && mip == 0 ? autodpiscale : nullptr);
894 if (data.first.get())
895 slices.set(slice, mip, data.first);
896 else
897 slices.set(slice, mip, data.second->getSlice(0, 0));
898
899 lua_pop(L, 1);
900 }
901 }
902 }
903 else
904 {
905 for (int slice = 0; slice < tlen; slice++)
906 {
907 lua_rawgeti(L, 1, slice + 1);
908 auto data = getImageData(L, -1, true, slice == 0 ? autodpiscale : nullptr);
909 if (data.first.get())
910 slices.set(slice, 0, data.first);
911 else
912 slices.add(data.second, slice, 0, false, settings.mipmaps);
913 }
914 }
915
916 lua_pop(L, tlen);
917 }
918 else
919 {
920 auto data = getImageData(L, 1, true, autodpiscale);
921 if (data.first.get())
922 slices.set(0, 0, data.first);
923 else
924 slices.add(data.second, 0, 0, true, settings.mipmaps);
925 }
926
927 return w__pushNewImage(L, slices, settings);
928}
929
930int w_newVolumeImage(lua_State *L)
931{
932 luax_checkgraphicscreated(L);
933
934 auto imagemodule = Module::getInstance<love::image::Image>(Module::M_IMAGE);
935
936 Image::Slices slices(TEXTURE_VOLUME);
937
938 bool dpiscaleset = false;
939 Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset);
940 float *autodpiscale = dpiscaleset ? nullptr : &settings.dpiScale;
941
942 if (lua_istable(L, 1))
943 {
944 int tlen = std::max(1, (int) luax_objlen(L, 1));
945
946 if (luax_isarrayoftables(L, 1))
947 {
948 for (int mip = 0; mip < tlen; mip++)
949 {
950 lua_rawgeti(L, 1, mip + 1);
951 luaL_checktype(L, -1, LUA_TTABLE);
952
953 int slicelen = std::max(1, (int) luax_objlen(L, -1));
954
955 for (int slice = 0; slice < slicelen; slice++)
956 {
957 lua_rawgeti(L, -1, slice + 1);
958
959 auto data = getImageData(L, -1, true, slice == 0 && mip == 0 ? autodpiscale : nullptr);
960 if (data.first.get())
961 slices.set(slice, mip, data.first);
962 else
963 slices.set(slice, mip, data.second->getSlice(0, 0));
964
965 lua_pop(L, 1);
966 }
967 }
968 }
969 else
970 {
971 for (int layer = 0; layer < tlen; layer++)
972 {
973 lua_rawgeti(L, 1, layer + 1);
974 auto data = getImageData(L, -1, true, layer == 0 ? autodpiscale : nullptr);
975 if (data.first.get())
976 slices.set(layer, 0, data.first);
977 else
978 slices.add(data.second, layer, 0, false, settings.mipmaps);
979 }
980 }
981
982 lua_pop(L, tlen);
983 }
984 else
985 {
986 auto data = getImageData(L, 1, true, autodpiscale);
987
988 if (data.first.get())
989 {
990 std::vector<StrongRef<love::image::ImageData>> layers;
991 luax_catchexcept(L, [&](){ layers = imagemodule->newVolumeLayers(data.first); });
992
993 for (int i = 0; i < (int) layers.size(); i++)
994 slices.set(i, 0, layers[i]);
995 }
996 else
997 slices.add(data.second, 0, 0, true, settings.mipmaps);
998 }
999
1000 return w__pushNewImage(L, slices, settings);
1001}
1002
1003int w_newImage(lua_State *L)
1004{
1005 luax_checkgraphicscreated(L);
1006
1007 Image::Slices slices(TEXTURE_2D);
1008
1009 bool dpiscaleset = false;
1010 Image::Settings settings = w__optImageSettings(L, 2, dpiscaleset);
1011 float *autodpiscale = dpiscaleset ? nullptr : &settings.dpiScale;
1012
1013 if (lua_istable(L, 1))
1014 {
1015 int n = std::max(1, (int) luax_objlen(L, 1));
1016 for (int i = 0; i < n; i++)
1017 {
1018 lua_rawgeti(L, 1, i + 1);
1019 auto data = getImageData(L, -1, true, i == 0 ? autodpiscale : nullptr);
1020 if (data.first.get())
1021 slices.set(0, i, data.first);
1022 else
1023 slices.set(0, i, data.second->getSlice(0, 0));
1024 }
1025 lua_pop(L, n);
1026 }
1027 else
1028 {
1029 auto data = getImageData(L, 1, true, autodpiscale);
1030 if (data.first.get())
1031 slices.set(0, 0, data.first);
1032 else
1033 slices.add(data.second, 0, 0, false, settings.mipmaps);
1034 }
1035
1036 return w__pushNewImage(L, slices, settings);
1037}
1038
1039int w_newQuad(lua_State *L)
1040{
1041 luax_checkgraphicscreated(L);
1042
1043 Quad::Viewport v;
1044 v.x = luaL_checknumber(L, 1);
1045 v.y = luaL_checknumber(L, 2);
1046 v.w = luaL_checknumber(L, 3);
1047 v.h = luaL_checknumber(L, 4);
1048
1049 double sw = 0.0f;
1050 double sh = 0.0f;
1051 int layer = 0;
1052
1053 if (luax_istype(L, 5, Texture::type))
1054 {
1055 Texture *texture = luax_checktexture(L, 5);
1056 sw = texture->getWidth();
1057 sh = texture->getHeight();
1058 }
1059 else if (luax_istype(L, 6, Texture::type))
1060 {
1061 layer = (int) luaL_checkinteger(L, 5) - 1;
1062 Texture *texture = luax_checktexture(L, 6);
1063 sw = texture->getWidth();
1064 sh = texture->getHeight();
1065 }
1066 else if (!lua_isnoneornil(L, 7))
1067 {
1068 layer = (int) luaL_checkinteger(L, 5) - 1;
1069 sw = luaL_checknumber(L, 6);
1070 sh = luaL_checknumber(L, 7);
1071 }
1072 else
1073 {
1074 sw = luaL_checknumber(L, 5);
1075 sh = luaL_checknumber(L, 6);
1076 }
1077
1078 Quad *quad = instance()->newQuad(v, sw, sh);
1079 quad->setLayer(layer);
1080
1081 luax_pushtype(L, quad);
1082 quad->release();
1083 return 1;
1084}
1085
1086int w_newFont(lua_State *L)
1087{
1088 luax_checkgraphicscreated(L);
1089
1090 graphics::Font *font = nullptr;
1091
1092 // Convert to Rasterizer, if necessary.
1093 if (!luax_istype(L, 1, love::font::Rasterizer::type))
1094 {
1095 std::vector<int> idxs;
1096 for (int i = 0; i < lua_gettop(L); i++)
1097 idxs.push_back(i + 1);
1098
1099 luax_convobj(L, idxs, "font", "newRasterizer");
1100 }
1101
1102 love::font::Rasterizer *rasterizer = luax_checktype<love::font::Rasterizer>(L, 1);
1103
1104 luax_catchexcept(L, [&]() {
1105 font = instance()->newFont(rasterizer, instance()->getDefaultFilter()); }
1106 );
1107
1108 // Push the type.
1109 luax_pushtype(L, font);
1110 font->release();
1111 return 1;
1112}
1113
1114int w_newImageFont(lua_State *L)
1115{
1116 luax_checkgraphicscreated(L);
1117
1118 // filter for glyphs
1119 Texture::Filter filter = instance()->getDefaultFilter();
1120
1121 // Convert to Rasterizer if necessary.
1122 if (!luax_istype(L, 1, love::font::Rasterizer::type))
1123 {
1124 luaL_checktype(L, 2, LUA_TSTRING);
1125
1126 std::vector<int> idxs;
1127 for (int i = 0; i < lua_gettop(L); i++)
1128 idxs.push_back(i + 1);
1129
1130 luax_convobj(L, idxs, "font", "newImageRasterizer");
1131 }
1132
1133 love::font::Rasterizer *rasterizer = luax_checktype<love::font::Rasterizer>(L, 1);
1134
1135 // Create the font.
1136 Font *font = instance()->newFont(rasterizer, filter);
1137
1138 // Push the type.
1139 luax_pushtype(L, font);
1140 font->release();
1141 return 1;
1142}
1143
1144int w_newSpriteBatch(lua_State *L)
1145{
1146 luax_checkgraphicscreated(L);
1147
1148 Texture *texture = luax_checktexture(L, 1);
1149 int size = (int) luaL_optinteger(L, 2, 1000);
1150 vertex::Usage usage = vertex::USAGE_DYNAMIC;
1151 if (lua_gettop(L) > 2)
1152 {
1153 const char *usagestr = luaL_checkstring(L, 3);
1154 if (!vertex::getConstant(usagestr, usage))
1155 return luax_enumerror(L, "usage hint", vertex::getConstants(usage), usagestr);
1156 }
1157
1158 SpriteBatch *t = nullptr;
1159 luax_catchexcept(L,
1160 [&](){ t = instance()->newSpriteBatch(texture, size, usage); }
1161 );
1162
1163 luax_pushtype(L, t);
1164 t->release();
1165 return 1;
1166}
1167
1168int w_newParticleSystem(lua_State *L)
1169{
1170 luax_checkgraphicscreated(L);
1171
1172 Texture *texture = luax_checktexture(L, 1);
1173 lua_Number size = luaL_optnumber(L, 2, 1000);
1174 ParticleSystem *t = nullptr;
1175 if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
1176 return luaL_error(L, "Invalid ParticleSystem size");
1177
1178 luax_catchexcept(L,
1179 [&](){ t = instance()->newParticleSystem(texture, int(size)); }
1180 );
1181
1182 luax_pushtype(L, t);
1183 t->release();
1184 return 1;
1185}
1186
1187int w_newCanvas(lua_State *L)
1188{
1189 luax_checkgraphicscreated(L);
1190
1191 Canvas::Settings settings;
1192
1193 // check if width and height are given. else default to screen dimensions.
1194 settings.width = (int) luaL_optinteger(L, 1, instance()->getWidth());
1195 settings.height = (int) luaL_optinteger(L, 2, instance()->getHeight());
1196
1197 // Default to the screen's current pixel density scale.
1198 settings.dpiScale = instance()->getScreenDPIScale();
1199
1200 int startidx = 3;
1201
1202 if (lua_isnumber(L, 3))
1203 {
1204 settings.layers = (int) luaL_checkinteger(L, 3);
1205 settings.type = TEXTURE_2D_ARRAY;
1206 startidx = 4;
1207 }
1208
1209 if (!lua_isnoneornil(L, startidx))
1210 {
1211 luax_checktablefields<Canvas::SettingType>(L, startidx, "canvas setting name", Canvas::getConstant);
1212
1213 settings.dpiScale = (float) luax_numberflag(L, startidx, Canvas::getConstant(Canvas::SETTING_DPI_SCALE), settings.dpiScale);
1214 settings.msaa = luax_intflag(L, startidx, Canvas::getConstant(Canvas::SETTING_MSAA), settings.msaa);
1215
1216 lua_getfield(L, startidx, Canvas::getConstant(Canvas::SETTING_FORMAT));
1217 if (!lua_isnoneornil(L, -1))
1218 {
1219 const char *str = luaL_checkstring(L, -1);
1220 if (!getConstant(str, settings.format))
1221 return luax_enumerror(L, "pixel format", str);
1222 }
1223 lua_pop(L, 1);
1224
1225 lua_getfield(L, startidx, Canvas::getConstant(Canvas::SETTING_TYPE));
1226 if (!lua_isnoneornil(L, -1))
1227 {
1228 const char *str = luaL_checkstring(L, -1);
1229 if (!Texture::getConstant(str, settings.type))
1230 return luax_enumerror(L, "texture type", Texture::getConstants(settings.type), str);
1231 }
1232 lua_pop(L, 1);
1233
1234 lua_getfield(L, startidx, Canvas::getConstant(Canvas::SETTING_READABLE));
1235 if (!lua_isnoneornil(L, -1))
1236 {
1237 settings.readable.hasValue = true;
1238 settings.readable.value = luax_checkboolean(L, -1);
1239 }
1240 lua_pop(L, 1);
1241
1242 lua_getfield(L, startidx, Canvas::getConstant(Canvas::SETTING_MIPMAPS));
1243 if (!lua_isnoneornil(L, -1))
1244 {
1245 const char *str = luaL_checkstring(L, -1);
1246 if (!Canvas::getConstant(str, settings.mipmaps))
1247 return luax_enumerror(L, "Canvas mipmap mode", Canvas::getConstants(settings.mipmaps), str);
1248 }
1249 lua_pop(L, 1);
1250 }
1251
1252 Canvas *canvas = nullptr;
1253 luax_catchexcept(L, [&](){ canvas = instance()->newCanvas(settings); });
1254
1255 luax_pushtype(L, canvas);
1256 canvas->release();
1257 return 1;
1258}
1259
1260static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string &vertexsource, std::string &pixelsource)
1261{
1262 using namespace love::filesystem;
1263
1264 luax_checkgraphicscreated(L);
1265
1266 auto fs = Module::getInstance<Filesystem>(Module::M_FILESYSTEM);
1267
1268 // read any filepath arguments
1269 for (int i = startidx; i < startidx + 2; i++)
1270 {
1271 if (!lua_isstring(L, i))
1272 {
1273 if (luax_cangetfiledata(L, i))
1274 {
1275 FileData *fd = luax_getfiledata(L, i);
1276
1277 lua_pushlstring(L, (const char *) fd->getData(), fd->getSize());
1278 fd->release();
1279
1280 lua_replace(L, i);
1281 }
1282
1283 continue;
1284 }
1285
1286 size_t slen = 0;
1287 const char *str = lua_tolstring(L, i, &slen);
1288
1289 Filesystem::Info info = {};
1290 if (fs != nullptr && fs->getInfo(str, info))
1291 {
1292 FileData *fd = nullptr;
1293 luax_catchexcept(L, [&](){ fd = fs->read(str); });
1294
1295 lua_pushlstring(L, (const char *) fd->getData(), fd->getSize());
1296 fd->release();
1297
1298 lua_replace(L, i);
1299 }
1300 else
1301 {
1302 // Check if the argument looks like a filepath - we want a nicer
1303 // error for misspelled filepath arguments.
1304 if (slen > 0 && slen < 64 && !strchr(str, '\n'))
1305 {
1306 const char *ext = strchr(str, '.');
1307 if (ext != nullptr && !strchr(ext, ';') && !strchr(ext, ' '))
1308 return luaL_error(L, "Could not open file %s. Does not exist.", str);
1309 }
1310 }
1311 }
1312
1313 bool has_arg1 = lua_isstring(L, startidx + 0) != 0;
1314 bool has_arg2 = lua_isstring(L, startidx + 1) != 0;
1315
1316 // require at least one string argument
1317 if (!(has_arg1 || has_arg2))
1318 luaL_checkstring(L, startidx);
1319
1320 luax_getfunction(L, "graphics", "_shaderCodeToGLSL");
1321
1322 // push vertexcode and pixelcode strings to the top of the stack
1323 lua_pushboolean(L, gles);
1324
1325 if (has_arg1)
1326 lua_pushvalue(L, startidx + 0);
1327 else
1328 lua_pushnil(L);
1329
1330 if (has_arg2)
1331 lua_pushvalue(L, startidx + 1);
1332 else
1333 lua_pushnil(L);
1334
1335 // call effectCodeToGLSL, returned values will be at the top of the stack
1336 if (lua_pcall(L, 3, 2, 0) != 0)
1337 return luaL_error(L, "%s", lua_tostring(L, -1));
1338
1339 // vertex shader code
1340 if (lua_isstring(L, -2))
1341 vertexsource = luax_checkstring(L, -2);
1342 else if (has_arg1 && has_arg2)
1343 return luaL_error(L, "Could not parse vertex shader code (missing 'position' function?)");
1344
1345 // pixel shader code
1346 if (lua_isstring(L, -1))
1347 pixelsource = luax_checkstring(L, -1);
1348 else if (has_arg1 && has_arg2)
1349 return luaL_error(L, "Could not parse pixel shader code (missing 'effect' function?)");
1350
1351 if (vertexsource.empty() && pixelsource.empty())
1352 {
1353 // Original args had source code, but effectCodeToGLSL couldn't translate it
1354 for (int i = startidx; i < startidx + 2; i++)
1355 {
1356 if (lua_isstring(L, i))
1357 return luaL_argerror(L, i, "missing 'position' or 'effect' function?");
1358 }
1359 }
1360
1361 return 0;
1362}
1363
1364int w_newShader(lua_State *L)
1365{
1366 bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
1367
1368 std::string vertexsource, pixelsource;
1369 w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
1370
1371 bool should_error = false;
1372 try
1373 {
1374 Shader *shader = instance()->newShader(vertexsource, pixelsource);
1375 luax_pushtype(L, shader);
1376 shader->release();
1377 }
1378 catch (love::Exception &e)
1379 {
1380 luax_getfunction(L, "graphics", "_transformGLSLErrorMessages");
1381 lua_pushstring(L, e.what());
1382
1383 // Function pushes the new error string onto the stack.
1384 lua_pcall(L, 1, 1, 0);
1385 should_error = true;
1386 }
1387
1388 if (should_error)
1389 return lua_error(L);
1390
1391 return 1;
1392}
1393
1394int w_validateShader(lua_State *L)
1395{
1396 bool gles = luax_checkboolean(L, 1);
1397
1398 std::string vertexsource, pixelsource;
1399 w_getShaderSource(L, 2, gles, vertexsource, pixelsource);
1400
1401 bool success = true;
1402 std::string err;
1403 try
1404 {
1405 success = instance()->validateShader(gles, vertexsource, pixelsource, err);
1406 }
1407 catch (love::Exception &e)
1408 {
1409 success = false;
1410 err = e.what();
1411 }
1412
1413 luax_pushboolean(L, success);
1414
1415 if (!success)
1416 {
1417 luax_pushstring(L, err);
1418 return 2;
1419 }
1420
1421 return 1;
1422}
1423
1424static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def)
1425{
1426 const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
1427
1428 if (usagestr && !vertex::getConstant(usagestr, def))
1429 luax_enumerror(L, "usage hint", vertex::getConstants(def), usagestr);
1430
1431 return def;
1432}
1433
1434static PrimitiveType luax_optmeshdrawmode(lua_State *L, int idx, PrimitiveType def)
1435{
1436 const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
1437
1438 if (modestr && !vertex::getConstant(modestr, def))
1439 luax_enumerror(L, "mesh draw mode", vertex::getConstants(def), modestr);
1440
1441 return def;
1442}
1443
1444static Mesh *newStandardMesh(lua_State *L)
1445{
1446 Mesh *t = nullptr;
1447
1448 PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN);
1449 vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC);
1450
1451 // First argument is a table of standard vertices, or the number of
1452 // standard vertices.
1453 if (lua_istable(L, 1))
1454 {
1455 size_t vertexcount = luax_objlen(L, 1);
1456 std::vector<Vertex> vertices;
1457 vertices.reserve(vertexcount);
1458
1459 // Get the vertices from the table.
1460 for (size_t i = 1; i <= vertexcount; i++)
1461 {
1462 lua_rawgeti(L, 1, (int) i);
1463
1464 if (lua_type(L, -1) != LUA_TTABLE)
1465 {
1466 luax_typerror(L, 1, "table of tables");
1467 return nullptr;
1468 }
1469
1470 for (int j = 1; j <= 8; j++)
1471 lua_rawgeti(L, -j, j);
1472
1473 Vertex v;
1474
1475 v.x = (float) luaL_checknumber(L, -8);
1476 v.y = (float) luaL_checknumber(L, -7);
1477 v.s = (float) luaL_optnumber(L, -6, 0.0);
1478 v.t = (float) luaL_optnumber(L, -5, 0.0);
1479
1480 v.color.r = (unsigned char) (luax_optnumberclamped01(L, -4, 1.0) * 255.0);
1481 v.color.g = (unsigned char) (luax_optnumberclamped01(L, -3, 1.0) * 255.0);
1482 v.color.b = (unsigned char) (luax_optnumberclamped01(L, -2, 1.0) * 255.0);
1483 v.color.a = (unsigned char) (luax_optnumberclamped01(L, -1, 1.0) * 255.0);
1484
1485 lua_pop(L, 9);
1486 vertices.push_back(v);
1487 }
1488
1489 luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); });
1490 }
1491 else
1492 {
1493 int count = (int) luaL_checkinteger(L, 1);
1494 luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); });
1495 }
1496
1497 return t;
1498}
1499
1500static Mesh *newCustomMesh(lua_State *L)
1501{
1502 Mesh *t = nullptr;
1503
1504 // First argument is the vertex format, second is a table of vertices or
1505 // the number of vertices.
1506 std::vector<Mesh::AttribFormat> vertexformat;
1507
1508 PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
1509 vertex::Usage usage = luax_optmeshusage(L, 4, vertex::USAGE_DYNAMIC);
1510
1511 lua_rawgeti(L, 1, 1);
1512 if (!lua_istable(L, -1))
1513 {
1514 luaL_argerror(L, 1, "table of tables expected");
1515 return nullptr;
1516 }
1517 lua_pop(L, 1);
1518
1519 // Per-vertex attribute formats.
1520 for (int i = 1; i <= (int) luax_objlen(L, 1); i++)
1521 {
1522 lua_rawgeti(L, 1, i);
1523
1524 // {name, datatype, components}
1525 for (int j = 1; j <= 3; j++)
1526 lua_rawgeti(L, -j, j);
1527
1528 Mesh::AttribFormat format;
1529 format.name = luaL_checkstring(L, -3);
1530
1531 const char *tname = luaL_checkstring(L, -2);
1532 if (!vertex::getConstant(tname, format.type))
1533 {
1534 luax_enumerror(L, "Mesh vertex data type name", vertex::getConstants(format.type), tname);
1535 return nullptr;
1536 }
1537
1538 format.components = (int) luaL_checkinteger(L, -1);
1539 if (format.components <= 0 || format.components > 4)
1540 {
1541 luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components);
1542 return nullptr;
1543 }
1544
1545 lua_pop(L, 4);
1546 vertexformat.push_back(format);
1547 }
1548
1549 if (lua_isnumber(L, 2))
1550 {
1551 int vertexcount = (int) luaL_checkinteger(L, 2);
1552 luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, vertexcount, drawmode, usage); });
1553 }
1554 else if (luax_istype(L, 2, Data::type))
1555 {
1556 // Vertex data comes directly from a Data object.
1557 Data *data = luax_checktype<Data>(L, 2);
1558 luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, data->getData(), data->getSize(), drawmode, usage); });
1559 }
1560 else
1561 {
1562 // Table of vertices.
1563 lua_rawgeti(L, 2, 1);
1564 if (!lua_istable(L, -1))
1565 {
1566 luaL_argerror(L, 2, "expected table of tables");
1567 return nullptr;
1568 }
1569 lua_pop(L, 1);
1570
1571 int vertexcomponents = 0;
1572 for (const Mesh::AttribFormat &format : vertexformat)
1573 vertexcomponents += format.components;
1574
1575 size_t numvertices = luax_objlen(L, 2);
1576
1577 luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
1578
1579 // Maximum possible data size for a single vertex attribute.
1580 char data[sizeof(float) * 4];
1581
1582 for (size_t vertindex = 0; vertindex < numvertices; vertindex++)
1583 {
1584 // get vertices[vertindex]
1585 lua_rawgeti(L, 2, vertindex + 1);
1586 luaL_checktype(L, -1, LUA_TTABLE);
1587
1588 int n = 0;
1589 for (size_t i = 0; i < vertexformat.size(); i++)
1590 {
1591 int components = vertexformat[i].components;
1592
1593 // get vertices[vertindex][n]
1594 for (int c = 0; c < components; c++)
1595 {
1596 n++;
1597 lua_rawgeti(L, -(c + 1), n);
1598 }
1599
1600 // Fetch the values from Lua and store them in data buffer.
1601 luax_writeAttributeData(L, -components, vertexformat[i].type, components, data);
1602
1603 lua_pop(L, components);
1604
1605 luax_catchexcept(L,
1606 [&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); },
1607 [&](bool diderror){ if (diderror) t->release(); }
1608 );
1609 }
1610
1611 lua_pop(L, 1); // pop vertices[vertindex]
1612 }
1613
1614 t->flush();
1615 }
1616
1617 return t;
1618}
1619
1620int w_newMesh(lua_State *L)
1621{
1622 luax_checkgraphicscreated(L);
1623
1624 // Check first argument: table or number of vertices.
1625 int arg1type = lua_type(L, 1);
1626 if (arg1type != LUA_TTABLE && arg1type != LUA_TNUMBER)
1627 luaL_argerror(L, 1, "table or number expected");
1628
1629 Mesh *t = nullptr;
1630
1631 int arg2type = lua_type(L, 2);
1632 if (arg1type == LUA_TTABLE && (arg2type == LUA_TTABLE || arg2type == LUA_TNUMBER || arg2type == LUA_TUSERDATA))
1633 t = newCustomMesh(L);
1634 else
1635 t = newStandardMesh(L);
1636
1637 luax_pushtype(L, t);
1638 t->release();
1639 return 1;
1640}
1641
1642int w_newText(lua_State *L)
1643{
1644 luax_checkgraphicscreated(L);
1645
1646 graphics::Font *font = luax_checkfont(L, 1);
1647 Text *t = nullptr;
1648
1649 if (lua_isnoneornil(L, 2))
1650 luax_catchexcept(L, [&](){ t = instance()->newText(font); });
1651 else
1652 {
1653 std::vector<Font::ColoredString> text;
1654 luax_checkcoloredstring(L, 2, text);
1655
1656 luax_catchexcept(L, [&](){ t = instance()->newText(font, text); });
1657 }
1658
1659 luax_pushtype(L, t);
1660 t->release();
1661 return 1;
1662}
1663
1664int w_newVideo(lua_State *L)
1665{
1666 luax_checkgraphicscreated(L);
1667
1668 if (!luax_istype(L, 1, love::video::VideoStream::type))
1669 luax_convobj(L, 1, "video", "newVideoStream");
1670
1671 auto stream = luax_checktype<love::video::VideoStream>(L, 1);
1672 float dpiscale = (float) luaL_optnumber(L, 2, 1.0);
1673 Video *video = nullptr;
1674
1675 luax_catchexcept(L, [&]() { video = instance()->newVideo(stream, dpiscale); });
1676
1677 luax_pushtype(L, video);
1678 video->release();
1679 return 1;
1680}
1681
1682int w_setColor(lua_State *L)
1683{
1684 Colorf c;
1685 if (lua_istable(L, 1))
1686 {
1687 for (int i = 1; i <= 4; i++)
1688 lua_rawgeti(L, 1, i);
1689
1690 c.r = (float) luaL_checknumber(L, -4);
1691 c.g = (float) luaL_checknumber(L, -3);
1692 c.b = (float) luaL_checknumber(L, -2);
1693 c.a = (float) luaL_optnumber(L, -1, 1.0);
1694
1695 lua_pop(L, 4);
1696 }
1697 else
1698 {
1699 c.r = (float) luaL_checknumber(L, 1);
1700 c.g = (float) luaL_checknumber(L, 2);
1701 c.b = (float) luaL_checknumber(L, 3);
1702 c.a = (float) luaL_optnumber(L, 4, 1.0);
1703 }
1704 instance()->setColor(c);
1705 return 0;
1706}
1707
1708int w_getColor(lua_State *L)
1709{
1710 Colorf c = instance()->getColor();
1711 lua_pushnumber(L, c.r);
1712 lua_pushnumber(L, c.g);
1713 lua_pushnumber(L, c.b);
1714 lua_pushnumber(L, c.a);
1715 return 4;
1716}
1717
1718int w_setBackgroundColor(lua_State *L)
1719{
1720 Colorf c;
1721 if (lua_istable(L, 1))
1722 {
1723 for (int i = 1; i <= 4; i++)
1724 lua_rawgeti(L, 1, i);
1725
1726 c.r = (float) luaL_checknumber(L, -4);
1727 c.g = (float) luaL_checknumber(L, -3);
1728 c.b = (float) luaL_checknumber(L, -2);
1729 c.a = (float) luaL_optnumber(L, -1, 1.0);
1730
1731 lua_pop(L, 4);
1732 }
1733 else
1734 {
1735 c.r = (float) luaL_checknumber(L, 1);
1736 c.g = (float) luaL_checknumber(L, 2);
1737 c.b = (float) luaL_checknumber(L, 3);
1738 c.a = (float) luaL_optnumber(L, 4, 1.0);
1739 }
1740 instance()->setBackgroundColor(c);
1741 return 0;
1742}
1743
1744int w_getBackgroundColor(lua_State *L)
1745{
1746 Colorf c = instance()->getBackgroundColor();
1747 lua_pushnumber(L, c.r);
1748 lua_pushnumber(L, c.g);
1749 lua_pushnumber(L, c.b);
1750 lua_pushnumber(L, c.a);
1751 return 4;
1752}
1753
1754int w_setNewFont(lua_State *L)
1755{
1756 int ret = w_newFont(L);
1757 Font *font = luax_checktype<Font>(L, -1);
1758 instance()->setFont(font);
1759 return ret;
1760}
1761
1762int w_setFont(lua_State *L)
1763{
1764 Font *font = luax_checktype<Font>(L, 1);
1765 instance()->setFont(font);
1766 return 0;
1767}
1768
1769int w_getFont(lua_State *L)
1770{
1771 Font *f = nullptr;
1772 luax_catchexcept(L, [&](){ f = instance()->getFont(); });
1773
1774 luax_pushtype(L, f);
1775 return 1;
1776}
1777
1778int w_setColorMask(lua_State *L)
1779{
1780 Graphics::ColorMask mask;
1781
1782 if (lua_gettop(L) <= 1 && lua_isnoneornil(L, 1))
1783 {
1784 // Enable all color components if no argument is given.
1785 mask.r = mask.g = mask.b = mask.a = true;
1786 }
1787 else
1788 {
1789 mask.r = luax_checkboolean(L, 1);
1790 mask.g = luax_checkboolean(L, 2);
1791 mask.b = luax_checkboolean(L, 3);
1792 mask.a = luax_checkboolean(L, 4);
1793 }
1794
1795 instance()->setColorMask(mask);
1796
1797 return 0;
1798}
1799
1800int w_getColorMask(lua_State *L)
1801{
1802 Graphics::ColorMask mask = instance()->getColorMask();
1803
1804 luax_pushboolean(L, mask.r);
1805 luax_pushboolean(L, mask.g);
1806 luax_pushboolean(L, mask.b);
1807 luax_pushboolean(L, mask.a);
1808
1809 return 4;
1810}
1811
1812int w_setBlendMode(lua_State *L)
1813{
1814 Graphics::BlendMode mode;
1815 const char *str = luaL_checkstring(L, 1);
1816 if (!Graphics::getConstant(str, mode))
1817 return luax_enumerror(L, "blend mode", Graphics::getConstants(mode), str);
1818
1819 Graphics::BlendAlpha alphamode = Graphics::BLENDALPHA_MULTIPLY;
1820 if (!lua_isnoneornil(L, 2))
1821 {
1822 const char *alphastr = luaL_checkstring(L, 2);
1823 if (!Graphics::getConstant(alphastr, alphamode))
1824 return luax_enumerror(L, "blend alpha mode", Graphics::getConstants(alphamode), alphastr);
1825 }
1826
1827 luax_catchexcept(L, [&](){ instance()->setBlendMode(mode, alphamode); });
1828 return 0;
1829}
1830
1831int w_getBlendMode(lua_State *L)
1832{
1833 const char *str;
1834 const char *alphastr;
1835
1836 Graphics::BlendAlpha alphamode;
1837 Graphics::BlendMode mode = instance()->getBlendMode(alphamode);
1838
1839 if (!Graphics::getConstant(mode, str))
1840 return luaL_error(L, "Unknown blend mode");
1841
1842 if (!Graphics::getConstant(alphamode, alphastr))
1843 return luaL_error(L, "Unknown blend alpha mode");
1844
1845 lua_pushstring(L, str);
1846 lua_pushstring(L, alphastr);
1847 return 2;
1848}
1849
1850int w_setDefaultFilter(lua_State *L)
1851{
1852 Texture::Filter f;
1853
1854 const char *minstr = luaL_checkstring(L, 1);
1855 const char *magstr = luaL_optstring(L, 2, minstr);
1856
1857 if (!Texture::getConstant(minstr, f.min))
1858 return luax_enumerror(L, "filter mode", Texture::getConstants(f.min), minstr);
1859 if (!Texture::getConstant(magstr, f.mag))
1860 return luax_enumerror(L, "filter mode", Texture::getConstants(f.mag), magstr);
1861
1862 f.anisotropy = (float) luaL_optnumber(L, 3, 1.0);
1863
1864 instance()->setDefaultFilter(f);
1865
1866 return 0;
1867}
1868
1869int w_getDefaultFilter(lua_State *L)
1870{
1871 const Texture::Filter &f = instance()->getDefaultFilter();
1872 const char *minstr;
1873 const char *magstr;
1874 if (!Texture::getConstant(f.min, minstr))
1875 return luaL_error(L, "Unknown minification filter mode");
1876 if (!Texture::getConstant(f.mag, magstr))
1877 return luaL_error(L, "Unknown magnification filter mode");
1878 lua_pushstring(L, minstr);
1879 lua_pushstring(L, magstr);
1880 lua_pushnumber(L, f.anisotropy);
1881 return 3;
1882}
1883
1884int w_setDefaultMipmapFilter(lua_State *L)
1885{
1886 Texture::FilterMode filter = Texture::FILTER_NONE;
1887 if (!lua_isnoneornil(L, 1))
1888 {
1889 const char *str = luaL_checkstring(L, 1);
1890 if (!Texture::getConstant(str, filter))
1891 return luax_enumerror(L, "filter mode", Texture::getConstants(filter), str);
1892 }
1893
1894 float sharpness = (float) luaL_optnumber(L, 2, 0);
1895
1896 instance()->setDefaultMipmapFilter(filter, sharpness);
1897
1898 return 0;
1899}
1900
1901int w_getDefaultMipmapFilter(lua_State *L)
1902{
1903 Texture::FilterMode filter;
1904 float sharpness;
1905
1906 instance()->getDefaultMipmapFilter(&filter, &sharpness);
1907
1908 const char *str;
1909 if (Texture::getConstant(filter, str))
1910 lua_pushstring(L, str);
1911 else
1912 lua_pushnil(L);
1913
1914 lua_pushnumber(L, sharpness);
1915
1916 return 2;
1917}
1918
1919int w_setLineWidth(lua_State *L)
1920{
1921 float width = (float)luaL_checknumber(L, 1);
1922 instance()->setLineWidth(width);
1923 return 0;
1924}
1925
1926int w_setLineStyle(lua_State *L)
1927{
1928 Graphics::LineStyle style;
1929 const char *str = luaL_checkstring(L, 1);
1930 if (!Graphics::getConstant(str, style))
1931 return luax_enumerror(L, "line style", Graphics::getConstants(style), str);
1932
1933 instance()->setLineStyle(style);
1934 return 0;
1935}
1936
1937int w_setLineJoin(lua_State *L)
1938{
1939 Graphics::LineJoin join;
1940 const char *str = luaL_checkstring(L, 1);
1941 if (!Graphics::getConstant(str, join))
1942 return luax_enumerror(L, "line join", Graphics::getConstants(join), str);
1943
1944 instance()->setLineJoin(join);
1945 return 0;
1946}
1947
1948int w_getLineWidth(lua_State *L)
1949{
1950 lua_pushnumber(L, instance()->getLineWidth());
1951 return 1;
1952}
1953
1954int w_getLineStyle(lua_State *L)
1955{
1956 Graphics::LineStyle style = instance()->getLineStyle();
1957 const char *str;
1958 if (!Graphics::getConstant(style, str))
1959 return luaL_error(L, "Unknown line style");
1960 lua_pushstring(L, str);
1961 return 1;
1962}
1963
1964int w_getLineJoin(lua_State *L)
1965{
1966 Graphics::LineJoin join = instance()->getLineJoin();
1967 const char *str;
1968 if (!Graphics::getConstant(join, str))
1969 return luaL_error(L, "Unknown line join");
1970 lua_pushstring(L, str);
1971 return 1;
1972}
1973
1974int w_setPointSize(lua_State *L)
1975{
1976 float size = (float)luaL_checknumber(L, 1);
1977 instance()->setPointSize(size);
1978 return 0;
1979}
1980
1981int w_getPointSize(lua_State *L)
1982{
1983 lua_pushnumber(L, instance()->getPointSize());
1984 return 1;
1985}
1986
1987int w_setDepthMode(lua_State *L)
1988{
1989 if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2))
1990 luax_catchexcept(L, [&]() { instance()->setDepthMode(); });
1991 else
1992 {
1993 CompareMode compare = COMPARE_ALWAYS;
1994 const char *str = luaL_checkstring(L, 1);
1995 bool write = luax_checkboolean(L, 2);
1996
1997 if (!getConstant(str, compare))
1998 return luax_enumerror(L, "compare mode", getConstants(compare), str);
1999
2000 luax_catchexcept(L, [&]() { instance()->setDepthMode(compare, write); });
2001 }
2002
2003 return 0;
2004}
2005
2006int w_getDepthMode(lua_State *L)
2007{
2008 CompareMode compare = COMPARE_ALWAYS;
2009 bool write = false;
2010 instance()->getDepthMode(compare, write);
2011
2012 const char *str;
2013 if (!getConstant(compare, str))
2014 return luaL_error(L, "Unknown compare mode");
2015
2016 lua_pushstring(L, str);
2017 luax_pushboolean(L, write);
2018 return 2;
2019}
2020
2021int w_setMeshCullMode(lua_State *L)
2022{
2023 const char *str = luaL_checkstring(L, 1);
2024 CullMode mode;
2025
2026 if (!vertex::getConstant(str, mode))
2027 return luax_enumerror(L, "cull mode", vertex::getConstants(mode), str);
2028
2029 luax_catchexcept(L, [&]() { instance()->setMeshCullMode(mode); });
2030 return 0;
2031}
2032
2033int w_getMeshCullMode(lua_State *L)
2034{
2035 CullMode mode = instance()->getMeshCullMode();
2036 const char *str;
2037 if (!vertex::getConstant(mode, str))
2038 return luaL_error(L, "Unknown cull mode");
2039 lua_pushstring(L, str);
2040 return 1;
2041}
2042
2043int w_setFrontFaceWinding(lua_State *L)
2044{
2045 const char *str = luaL_checkstring(L, 1);
2046 vertex::Winding winding;
2047
2048 if (!vertex::getConstant(str, winding))
2049 return luax_enumerror(L, "vertex winding", vertex::getConstants(winding), str);
2050
2051 luax_catchexcept(L, [&]() { instance()->setFrontFaceWinding(winding); });
2052 return 0;
2053}
2054
2055int w_getFrontFaceWinding(lua_State *L)
2056{
2057 vertex::Winding winding = instance()->getFrontFaceWinding();
2058 const char *str;
2059 if (!vertex::getConstant(winding, str))
2060 return luaL_error(L, "Unknown vertex winding");
2061 lua_pushstring(L, str);
2062 return 1;
2063}
2064
2065int w_setWireframe(lua_State *L)
2066{
2067 instance()->setWireframe(luax_checkboolean(L, 1));
2068 return 0;
2069}
2070
2071int w_isWireframe(lua_State *L)
2072{
2073 luax_pushboolean(L, instance()->isWireframe());
2074 return 1;
2075}
2076
2077int w_setShader(lua_State *L)
2078{
2079 if (lua_isnoneornil(L,1))
2080 {
2081 instance()->setShader();
2082 return 0;
2083 }
2084
2085 Shader *shader = luax_checkshader(L, 1);
2086 instance()->setShader(shader);
2087 return 0;
2088}
2089
2090int w_getShader(lua_State *L)
2091{
2092 Shader *shader = instance()->getShader();
2093 if (shader)
2094 luax_pushtype(L, shader);
2095 else
2096 lua_pushnil(L);
2097
2098 return 1;
2099}
2100
2101int w_setDefaultShaderCode(lua_State *L)
2102{
2103 for (int i = 0; i < 2; i++)
2104 {
2105 luaL_checktype(L, i + 1, LUA_TTABLE);
2106
2107 for (int lang = 0; lang < Shader::LANGUAGE_MAX_ENUM; lang++)
2108 {
2109 const char *langname;
2110 if (!Shader::getConstant((Shader::Language) lang, langname))
2111 continue;
2112
2113 lua_getfield(L, i + 1, langname);
2114
2115 lua_getfield(L, -1, "vertex");
2116 lua_getfield(L, -2, "pixel");
2117 lua_getfield(L, -3, "videopixel");
2118 lua_getfield(L, -4, "arraypixel");
2119
2120 std::string vertex = luax_checkstring(L, -4);
2121 std::string pixel = luax_checkstring(L, -3);
2122 std::string videopixel = luax_checkstring(L, -2);
2123 std::string arraypixel = luax_checkstring(L, -1);
2124
2125 lua_pop(L, 5);
2126
2127 Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
2128 Graphics::defaultShaderCode[Shader::STANDARD_DEFAULT][lang][i].source[ShaderStage::STAGE_PIXEL] = pixel;
2129
2130 Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
2131 Graphics::defaultShaderCode[Shader::STANDARD_VIDEO][lang][i].source[ShaderStage::STAGE_PIXEL] = videopixel;
2132
2133 Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_VERTEX] = vertex;
2134 Graphics::defaultShaderCode[Shader::STANDARD_ARRAY][lang][i].source[ShaderStage::STAGE_PIXEL] = arraypixel;
2135 }
2136 }
2137
2138 return 0;
2139}
2140
2141int w_getSupported(lua_State *L)
2142{
2143 const Graphics::Capabilities &caps = instance()->getCapabilities();
2144
2145 if (lua_istable(L, 1))
2146 lua_pushvalue(L, 1);
2147 else
2148 lua_createtable(L, 0, (int) Graphics::FEATURE_MAX_ENUM);
2149
2150 for (int i = 0; i < (int) Graphics::FEATURE_MAX_ENUM; i++)
2151 {
2152 auto feature = (Graphics::Feature) i;
2153 const char *name = nullptr;
2154
2155 if (!Graphics::getConstant(feature, name))
2156 continue;
2157
2158 luax_pushboolean(L, caps.features[i]);
2159 lua_setfield(L, -2, name);
2160 }
2161
2162 return 1;
2163}
2164
2165static int w__getFormats(lua_State *L, int idx, bool (*isFormatSupported)(PixelFormat), bool (*ignore)(PixelFormat))
2166{
2167 if (lua_istable(L, idx))
2168 lua_pushvalue(L, idx);
2169 else
2170 lua_createtable(L, 0, (int) PIXELFORMAT_MAX_ENUM);
2171
2172 for (int i = 0; i < (int) PIXELFORMAT_MAX_ENUM; i++)
2173 {
2174 PixelFormat format = (PixelFormat) i;
2175 const char *name = nullptr;
2176
2177 if (format == PIXELFORMAT_UNKNOWN || !love::getConstant(format, name) || ignore(format))
2178 continue;
2179
2180 luax_pushboolean(L, isFormatSupported(format));
2181 lua_setfield(L, -2, name);
2182 }
2183
2184 return 1;
2185}
2186
2187int w_getCanvasFormats(lua_State *L)
2188{
2189 bool (*supported)(PixelFormat);
2190
2191 int idx = 1;
2192 if (lua_type(L, 1) == LUA_TBOOLEAN)
2193 {
2194 idx = 2;
2195 if (luax_checkboolean(L, 1))
2196 {
2197 supported = [](PixelFormat format) -> bool
2198 {
2199 return instance()->isCanvasFormatSupported(format, true);
2200 };
2201 }
2202 else
2203 {
2204 supported = [](PixelFormat format) -> bool
2205 {
2206 return instance()->isCanvasFormatSupported(format, false);
2207 };
2208 }
2209 }
2210 else
2211 {
2212 supported = [](PixelFormat format) -> bool
2213 {
2214 return instance()->isCanvasFormatSupported(format);
2215 };
2216 }
2217
2218 return w__getFormats(L, idx, supported, isPixelFormatCompressed);
2219}
2220
2221int w_getImageFormats(lua_State *L)
2222{
2223 const auto supported = [](PixelFormat format) -> bool
2224 {
2225 return instance()->isImageFormatSupported(format);
2226 };
2227
2228 const auto ignore = [](PixelFormat format) -> bool
2229 {
2230 return !(image::ImageData::validPixelFormat(format) || isPixelFormatCompressed(format));
2231 };
2232
2233 return w__getFormats(L, 1, supported, ignore);
2234}
2235
2236int w_getTextureTypes(lua_State *L)
2237{
2238 const Graphics::Capabilities &caps = instance()->getCapabilities();
2239
2240 if (lua_istable(L, 1))
2241 lua_pushvalue(L, 1);
2242 else
2243 lua_createtable(L, 0, (int) TEXTURE_MAX_ENUM);
2244
2245 for (int i = 0; i < (int) TEXTURE_MAX_ENUM; i++)
2246 {
2247 TextureType textype = (TextureType) i;
2248 const char *name = nullptr;
2249
2250 if (!Texture::getConstant(textype, name))
2251 continue;
2252
2253 luax_pushboolean(L, caps.textureTypes[i]);
2254 lua_setfield(L, -2, name);
2255 }
2256
2257 return 1;
2258}
2259
2260int w_getRendererInfo(lua_State *L)
2261{
2262 Graphics::RendererInfo info;
2263 luax_catchexcept(L, [&](){ info = instance()->getRendererInfo(); });
2264
2265 luax_pushstring(L, info.name);
2266 luax_pushstring(L, info.version);
2267 luax_pushstring(L, info.vendor);
2268 luax_pushstring(L, info.device);
2269 return 4;
2270}
2271
2272int w_getSystemLimits(lua_State *L)
2273{
2274 const Graphics::Capabilities &caps = instance()->getCapabilities();
2275
2276 if (lua_istable(L, 1))
2277 lua_pushvalue(L, 1);
2278 else
2279 lua_createtable(L, 0, (int) Graphics::LIMIT_MAX_ENUM);
2280
2281 for (int i = 0; i < (int) Graphics::LIMIT_MAX_ENUM; i++)
2282 {
2283 Graphics::SystemLimit limittype = (Graphics::SystemLimit) i;
2284 const char *name = nullptr;
2285
2286 if (!Graphics::getConstant(limittype, name))
2287 continue;
2288
2289 lua_pushnumber(L, caps.limits[i]);
2290 lua_setfield(L, -2, name);
2291 }
2292
2293 return 1;
2294}
2295
2296int w_getStats(lua_State *L)
2297{
2298 Graphics::Stats stats = instance()->getStats();
2299
2300 if (lua_istable(L, 1))
2301 lua_pushvalue(L, 1);
2302 else
2303 lua_createtable(L, 0, 7);
2304
2305 lua_pushinteger(L, stats.drawCalls);
2306 lua_setfield(L, -2, "drawcalls");
2307
2308 lua_pushinteger(L, stats.drawCallsBatched);
2309 lua_setfield(L, -2, "drawcallsbatched");
2310
2311 lua_pushinteger(L, stats.canvasSwitches);
2312 lua_setfield(L, -2, "canvasswitches");
2313
2314 lua_pushinteger(L, stats.shaderSwitches);
2315 lua_setfield(L, -2, "shaderswitches");
2316
2317 lua_pushinteger(L, stats.canvases);
2318 lua_setfield(L, -2, "canvases");
2319
2320 lua_pushinteger(L, stats.images);
2321 lua_setfield(L, -2, "images");
2322
2323 lua_pushinteger(L, stats.fonts);
2324 lua_setfield(L, -2, "fonts");
2325
2326 lua_pushinteger(L, stats.textureMemory);
2327 lua_setfield(L, -2, "texturememory");
2328
2329 return 1;
2330}
2331
2332int w_draw(lua_State *L)
2333{
2334 Drawable *drawable = nullptr;
2335 Texture *texture = nullptr;
2336 Quad *quad = nullptr;
2337 int startidx = 2;
2338
2339 if (luax_istype(L, 2, Quad::type))
2340 {
2341 texture = luax_checktexture(L, 1);
2342 quad = luax_totype<Quad>(L, 2);
2343 startidx = 3;
2344 }
2345 else if (lua_isnil(L, 2) && !lua_isnoneornil(L, 3))
2346 {
2347 return luax_typerror(L, 2, "Quad");
2348 }
2349 else
2350 {
2351 drawable = luax_checktype<Drawable>(L, 1);
2352 startidx = 2;
2353 }
2354
2355 luax_checkstandardtransform(L, startidx, [&](const Matrix4 &m)
2356 {
2357 luax_catchexcept(L, [&]()
2358 {
2359 if (texture && quad)
2360 instance()->draw(texture, quad, m);
2361 else
2362 instance()->draw(drawable, m);
2363 });
2364 });
2365
2366 return 0;
2367}
2368
2369int w_drawLayer(lua_State *L)
2370{
2371 Texture *texture = luax_checktexture(L, 1);
2372 Quad *quad = nullptr;
2373 int layer = (int) luaL_checkinteger(L, 2) - 1;
2374 int startidx = 3;
2375
2376 if (luax_istype(L, startidx, Quad::type))
2377 {
2378 texture = luax_checktexture(L, 1);
2379 quad = luax_totype<Quad>(L, startidx);
2380 startidx++;
2381 }
2382 else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1))
2383 {
2384 return luax_typerror(L, startidx, "Quad");
2385 }
2386
2387 luax_checkstandardtransform(L, startidx, [&](const Matrix4 &m)
2388 {
2389 luax_catchexcept(L, [&]()
2390 {
2391 if (quad)
2392 instance()->drawLayer(texture, layer, quad, m);
2393 else
2394 instance()->drawLayer(texture, layer, m);
2395 });
2396 });
2397
2398 return 0;
2399}
2400
2401int w_drawInstanced(lua_State *L)
2402{
2403 Mesh *t = luax_checkmesh(L, 1);
2404 int instancecount = (int) luaL_checkinteger(L, 2);
2405
2406 luax_checkstandardtransform(L, 3, [&](const Matrix4 &m)
2407 {
2408 luax_catchexcept(L, [&]() { instance()->drawInstanced(t, m, instancecount); });
2409 });
2410
2411 return 0;
2412}
2413
2414int w_print(lua_State *L)
2415{
2416 std::vector<Font::ColoredString> str;
2417 luax_checkcoloredstring(L, 1, str);
2418
2419 if (luax_istype(L, 2, Font::type))
2420 {
2421 Font *font = luax_checkfont(L, 2);
2422
2423 luax_checkstandardtransform(L, 3, [&](const Matrix4 &m)
2424 {
2425 luax_catchexcept(L, [&](){ instance()->print(str, font, m); });
2426 });
2427 }
2428 else
2429 {
2430 luax_checkstandardtransform(L, 2, [&](const Matrix4 &m)
2431 {
2432 luax_catchexcept(L, [&](){ instance()->print(str, m); });
2433 });
2434 }
2435
2436 return 0;
2437}
2438
2439int w_printf(lua_State *L)
2440{
2441 std::vector<Font::ColoredString> str;
2442 luax_checkcoloredstring(L, 1, str);
2443
2444 Font *font = nullptr;
2445 int startidx = 2;
2446
2447 if (luax_istype(L, startidx, Font::type))
2448 {
2449 font = luax_checkfont(L, startidx);
2450 startidx++;
2451 }
2452
2453 Font::AlignMode align = Font::ALIGN_LEFT;
2454 Matrix4 m;
2455
2456 int formatidx = startidx + 2;
2457
2458 if (luax_istype(L, startidx, math::Transform::type))
2459 {
2460 math::Transform *tf = luax_totype<math::Transform>(L, startidx);
2461 m = tf->getMatrix();
2462 formatidx = startidx + 1;
2463 }
2464 else
2465 {
2466 float x = (float)luaL_checknumber(L, startidx + 0);
2467 float y = (float)luaL_checknumber(L, startidx + 1);
2468
2469 float angle = (float) luaL_optnumber(L, startidx + 4, 0.0f);
2470 float sx = (float) luaL_optnumber(L, startidx + 5, 1.0f);
2471 float sy = (float) luaL_optnumber(L, startidx + 6, sx);
2472 float ox = (float) luaL_optnumber(L, startidx + 7, 0.0f);
2473 float oy = (float) luaL_optnumber(L, startidx + 8, 0.0f);
2474 float kx = (float) luaL_optnumber(L, startidx + 9, 0.0f);
2475 float ky = (float) luaL_optnumber(L, startidx + 10, 0.0f);
2476
2477 m = Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
2478 }
2479
2480 float wrap = (float)luaL_checknumber(L, formatidx);
2481
2482 const char *astr = lua_isnoneornil(L, formatidx + 1) ? nullptr : luaL_checkstring(L, formatidx + 1);
2483 if (astr != nullptr && !Font::getConstant(astr, align))
2484 return luax_enumerror(L, "alignment", Font::getConstants(align), astr);
2485
2486 if (font != nullptr)
2487 luax_catchexcept(L, [&](){ instance()->printf(str, font, wrap, align, m); });
2488 else
2489 luax_catchexcept(L, [&](){ instance()->printf(str, wrap, align, m); });
2490
2491 return 0;
2492}
2493
2494int w_points(lua_State *L)
2495{
2496 // love.graphics.points has 3 variants:
2497 // - points(x1, y1, x2, y2, ...)
2498 // - points({x1, y1, x2, y2, ...})
2499 // - points({{x1, y1 [, r, g, b, a]}, {x2, y2 [, r, g, b, a]}, ...})
2500
2501 int args = lua_gettop(L);
2502 bool is_table = false;
2503 bool is_table_of_tables = false;
2504 if (args == 1 && lua_istable(L, 1))
2505 {
2506 is_table = true;
2507 args = (int) luax_objlen(L, 1);
2508
2509 lua_rawgeti(L, 1, 1);
2510 is_table_of_tables = lua_istable(L, -1);
2511 lua_pop(L, 1);
2512 }
2513
2514 if (args % 2 != 0 && !is_table_of_tables)
2515 return luaL_error(L, "Number of vertex components must be a multiple of two");
2516
2517 int numpositions = args / 2;
2518 if (is_table_of_tables)
2519 numpositions = args;
2520
2521 Vector2 *positions = nullptr;
2522 Colorf *colors = nullptr;
2523
2524 if (is_table_of_tables)
2525 {
2526 size_t datasize = (sizeof(Vector2) + sizeof(Colorf)) * numpositions;
2527 uint8 *data = instance()->getScratchBuffer<uint8>(datasize);
2528
2529 positions = (Vector2 *) data;
2530 colors = (Colorf *) (data + sizeof(Vector2) * numpositions);
2531 }
2532 else
2533 positions = instance()->getScratchBuffer<Vector2>(numpositions);
2534
2535 if (is_table)
2536 {
2537 if (is_table_of_tables)
2538 {
2539 // points({{x1, y1 [, r, g, b, a]}, {x2, y2 [, r, g, b, a]}, ...})
2540 for (int i = 0; i < args; i++)
2541 {
2542 lua_rawgeti(L, 1, i + 1);
2543 for (int j = 1; j <= 6; j++)
2544 lua_rawgeti(L, -j, j);
2545
2546 positions[i].x = luax_checkfloat(L, -6);
2547 positions[i].y = luax_checkfloat(L, -5);
2548
2549 colors[i].r = (float) luax_optnumberclamped01(L, -4, 1.0);
2550 colors[i].g = (float) luax_optnumberclamped01(L, -3, 1.0);
2551 colors[i].b = (float) luax_optnumberclamped01(L, -2, 1.0);
2552 colors[i].a = (float) luax_optnumberclamped01(L, -1, 1.0);
2553
2554 lua_pop(L, 7);
2555 }
2556 }
2557 else
2558 {
2559 // points({x1, y1, x2, y2, ...})
2560 for (int i = 0; i < numpositions; i++)
2561 {
2562 lua_rawgeti(L, 1, i * 2 + 1);
2563 lua_rawgeti(L, 1, i * 2 + 2);
2564 positions[i].x = luax_checkfloat(L, -2);
2565 positions[i].y = luax_checkfloat(L, -1);
2566 lua_pop(L, 2);
2567 }
2568 }
2569 }
2570 else
2571 {
2572 for (int i = 0; i < numpositions; i++)
2573 {
2574 positions[i].x = luax_checkfloat(L, i * 2 + 1);
2575 positions[i].y = luax_checkfloat(L, i * 2 + 2);
2576 }
2577 }
2578
2579 luax_catchexcept(L, [&](){ instance()->points(positions, colors, numpositions); });
2580 return 0;
2581}
2582
2583int w_line(lua_State *L)
2584{
2585 int args = lua_gettop(L);
2586 int arg1type = lua_type(L, 1);
2587 bool is_table = false;
2588
2589 if (args == 1 && arg1type == LUA_TTABLE)
2590 {
2591 args = (int) luax_objlen(L, 1);
2592 is_table = true;
2593 }
2594
2595 if (arg1type != LUA_TTABLE && arg1type != LUA_TNUMBER)
2596 return luax_typerror(L, 1, "table or number");
2597 else if (args % 2 != 0)
2598 return luaL_error(L, "Number of vertex components must be a multiple of two.");
2599 else if (args < 4)
2600 return luaL_error(L, "Need at least two vertices to draw a line.");
2601
2602 int numvertices = args / 2;
2603
2604 Vector2 *coords = instance()->getScratchBuffer<Vector2>(numvertices);
2605 if (is_table)
2606 {
2607 for (int i = 0; i < numvertices; ++i)
2608 {
2609 lua_rawgeti(L, 1, (i * 2) + 1);
2610 lua_rawgeti(L, 1, (i * 2) + 2);
2611 coords[i].x = luax_checkfloat(L, -2);
2612 coords[i].y = luax_checkfloat(L, -1);
2613 lua_pop(L, 2);
2614 }
2615 }
2616 else
2617 {
2618 for (int i = 0; i < numvertices; ++i)
2619 {
2620 coords[i].x = luax_checkfloat(L, (i * 2) + 1);
2621 coords[i].y = luax_checkfloat(L, (i * 2) + 2);
2622 }
2623 }
2624
2625 luax_catchexcept(L,
2626 [&](){ instance()->polyline(coords, numvertices); }
2627 );
2628
2629 return 0;
2630}
2631
2632int w_rectangle(lua_State *L)
2633{
2634 Graphics::DrawMode mode;
2635 const char *str = luaL_checkstring(L, 1);
2636 if (!Graphics::getConstant(str, mode))
2637 return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
2638
2639 float x = (float)luaL_checknumber(L, 2);
2640 float y = (float)luaL_checknumber(L, 3);
2641 float w = (float)luaL_checknumber(L, 4);
2642 float h = (float)luaL_checknumber(L, 5);
2643
2644 if (lua_isnoneornil(L, 6))
2645 {
2646 instance()->rectangle(mode, x, y, w, h);
2647 return 0;
2648 }
2649
2650 float rx = (float)luaL_optnumber(L, 6, 0.0);
2651 float ry = (float)luaL_optnumber(L, 7, rx);
2652
2653 if (lua_isnoneornil(L, 8))
2654 luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry); });
2655 else
2656 {
2657 int points = (int) luaL_checkinteger(L, 8);
2658 luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry, points); });
2659 }
2660
2661 return 0;
2662}
2663
2664int w_circle(lua_State *L)
2665{
2666 Graphics::DrawMode mode;
2667 const char *str = luaL_checkstring(L, 1);
2668 if (!Graphics::getConstant(str, mode))
2669 return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
2670
2671 float x = (float)luaL_checknumber(L, 2);
2672 float y = (float)luaL_checknumber(L, 3);
2673 float radius = (float)luaL_checknumber(L, 4);
2674
2675 if (lua_isnoneornil(L, 5))
2676 luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius); });
2677 else
2678 {
2679 int points = (int) luaL_checkinteger(L, 5);
2680 luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius, points); });
2681 }
2682
2683 return 0;
2684}
2685
2686int w_ellipse(lua_State *L)
2687{
2688 Graphics::DrawMode mode;
2689 const char *str = luaL_checkstring(L, 1);
2690 if (!Graphics::getConstant(str, mode))
2691 return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
2692
2693 float x = (float)luaL_checknumber(L, 2);
2694 float y = (float)luaL_checknumber(L, 3);
2695 float a = (float)luaL_checknumber(L, 4);
2696 float b = (float)luaL_optnumber(L, 5, a);
2697
2698 if (lua_isnoneornil(L, 6))
2699 luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b); });
2700 else
2701 {
2702 int points = (int) luaL_checkinteger(L, 6);
2703 luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b, points); });
2704 }
2705
2706 return 0;
2707}
2708
2709int w_arc(lua_State *L)
2710{
2711 Graphics::DrawMode drawmode;
2712 const char *drawstr = luaL_checkstring(L, 1);
2713 if (!Graphics::getConstant(drawstr, drawmode))
2714 return luax_enumerror(L, "draw mode", Graphics::getConstants(drawmode), drawstr);
2715
2716 int startidx = 2;
2717
2718 Graphics::ArcMode arcmode = Graphics::ARC_PIE;
2719
2720 if (lua_type(L, 2) == LUA_TSTRING)
2721 {
2722 const char *arcstr = luaL_checkstring(L, 2);
2723 if (!Graphics::getConstant(arcstr, arcmode))
2724 return luax_enumerror(L, "arc mode", Graphics::getConstants(arcmode), arcstr);
2725
2726 startidx = 3;
2727 }
2728
2729 float x = (float) luaL_checknumber(L, startidx + 0);
2730 float y = (float) luaL_checknumber(L, startidx + 1);
2731 float radius = (float) luaL_checknumber(L, startidx + 2);
2732 float angle1 = (float) luaL_checknumber(L, startidx + 3);
2733 float angle2 = (float) luaL_checknumber(L, startidx + 4);
2734
2735 if (lua_isnoneornil(L, startidx + 5))
2736 luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); });
2737 else
2738 {
2739 int points = (int) luaL_checkinteger(L, startidx + 5);
2740 luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); });
2741 }
2742
2743 return 0;
2744}
2745
2746int w_polygon(lua_State *L)
2747{
2748 int args = lua_gettop(L) - 1;
2749
2750 Graphics::DrawMode mode;
2751 const char *str = luaL_checkstring(L, 1);
2752 if (!Graphics::getConstant(str, mode))
2753 return luax_enumerror(L, "draw mode", Graphics::getConstants(mode), str);
2754
2755 bool is_table = false;
2756 if (args == 1 && lua_istable(L, 2))
2757 {
2758 args = (int) luax_objlen(L, 2);
2759 is_table = true;
2760 }
2761
2762 if (args % 2 != 0)
2763 return luaL_error(L, "Number of vertex components must be a multiple of two");
2764 else if (args < 6)
2765 return luaL_error(L, "Need at least three vertices to draw a polygon");
2766
2767 int numvertices = args / 2;
2768
2769 // fetch coords
2770 Vector2 *coords = instance()->getScratchBuffer<Vector2>(numvertices + 1);
2771 if (is_table)
2772 {
2773 for (int i = 0; i < numvertices; ++i)
2774 {
2775 lua_rawgeti(L, 2, (i * 2) + 1);
2776 lua_rawgeti(L, 2, (i * 2) + 2);
2777 coords[i].x = luax_checkfloat(L, -2);
2778 coords[i].y = luax_checkfloat(L, -1);
2779 lua_pop(L, 2);
2780 }
2781 }
2782 else
2783 {
2784 for (int i = 0; i < numvertices; ++i)
2785 {
2786 coords[i].x = luax_checkfloat(L, (i * 2) + 2);
2787 coords[i].y = luax_checkfloat(L, (i * 2) + 3);
2788 }
2789 }
2790
2791 // make a closed loop
2792 coords[numvertices] = coords[0];
2793
2794 luax_catchexcept(L, [&](){ instance()->polygon(mode, coords, numvertices+1); });
2795 return 0;
2796}
2797
2798int w_flushBatch(lua_State *)
2799{
2800 instance()->flushStreamDraws();
2801 return 0;
2802}
2803
2804int w_getStackDepth(lua_State *L)
2805{
2806 lua_pushnumber(L, instance()->getStackDepth());
2807 return 1;
2808}
2809
2810int w_push(lua_State *L)
2811{
2812 Graphics::StackType stype = Graphics::STACK_TRANSFORM;
2813 const char *sname = lua_isnoneornil(L, 1) ? nullptr : luaL_checkstring(L, 1);
2814 if (sname && !Graphics::getConstant(sname, stype))
2815 return luax_enumerror(L, "graphics stack type", Graphics::getConstants(stype), sname);
2816
2817 luax_catchexcept(L, [&](){ instance()->push(stype); });
2818
2819 if (luax_istype(L, 2, math::Transform::type))
2820 {
2821 math::Transform *t = luax_totype<math::Transform>(L, 2);
2822 luax_catchexcept(L, [&]() { instance()->applyTransform(t); });
2823 }
2824
2825 return 0;
2826}
2827
2828int w_pop(lua_State *L)
2829{
2830 luax_catchexcept(L, [&](){ instance()->pop(); });
2831 return 0;
2832}
2833
2834int w_rotate(lua_State *L)
2835{
2836 float angle = (float)luaL_checknumber(L, 1);
2837 instance()->rotate(angle);
2838 return 0;
2839}
2840
2841int w_scale(lua_State *L)
2842{
2843 float sx = (float)luaL_optnumber(L, 1, 1.0f);
2844 float sy = (float)luaL_optnumber(L, 2, sx);
2845 instance()->scale(sx, sy);
2846 return 0;
2847}
2848
2849int w_translate(lua_State *L)
2850{
2851 float x = (float)luaL_checknumber(L, 1);
2852 float y = (float)luaL_checknumber(L, 2);
2853 instance()->translate(x, y);
2854 return 0;
2855}
2856
2857int w_shear(lua_State *L)
2858{
2859 float kx = (float)luaL_checknumber(L, 1);
2860 float ky = (float)luaL_checknumber(L, 2);
2861 instance()->shear(kx, ky);
2862 return 0;
2863}
2864
2865int w_origin(lua_State * /*L*/)
2866{
2867 instance()->origin();
2868 return 0;
2869}
2870
2871int w_applyTransform(lua_State *L)
2872{
2873 math::Transform *t = math::luax_checktransform(L, 1);
2874 luax_catchexcept(L, [&]() { instance()->applyTransform(t); });
2875 return 0;
2876}
2877
2878int w_replaceTransform(lua_State *L)
2879{
2880 math::Transform *t = math::luax_checktransform(L, 1);
2881 luax_catchexcept(L, [&]() { instance()->replaceTransform(t); });
2882 return 0;
2883}
2884
2885int w_transformPoint(lua_State *L)
2886{
2887 Vector2 p;
2888 p.x = (float) luaL_checknumber(L, 1);
2889 p.y = (float) luaL_checknumber(L, 2);
2890 p = instance()->transformPoint(p);
2891 lua_pushnumber(L, p.x);
2892 lua_pushnumber(L, p.y);
2893 return 2;
2894}
2895
2896int w_inverseTransformPoint(lua_State *L)
2897{
2898 Vector2 p;
2899 p.x = (float) luaL_checknumber(L, 1);
2900 p.y = (float) luaL_checknumber(L, 2);
2901 p = instance()->inverseTransformPoint(p);
2902 lua_pushnumber(L, p.x);
2903 lua_pushnumber(L, p.y);
2904 return 2;
2905}
2906
2907
2908// List of functions to wrap.
2909static const luaL_Reg functions[] =
2910{
2911 { "reset", w_reset },
2912 { "clear", w_clear },
2913 { "discard", w_discard },
2914 { "present", w_present },
2915
2916 { "newImage", w_newImage },
2917 { "newArrayImage", w_newArrayImage },
2918 { "newVolumeImage", w_newVolumeImage },
2919 { "newCubeImage", w_newCubeImage },
2920 { "newQuad", w_newQuad },
2921 { "newFont", w_newFont },
2922 { "newImageFont", w_newImageFont },
2923 { "newSpriteBatch", w_newSpriteBatch },
2924 { "newParticleSystem", w_newParticleSystem },
2925 { "newCanvas", w_newCanvas },
2926 { "newShader", w_newShader },
2927 { "newMesh", w_newMesh },
2928 { "newText", w_newText },
2929 { "_newVideo", w_newVideo },
2930
2931 { "validateShader", w_validateShader },
2932
2933 { "setCanvas", w_setCanvas },
2934 { "getCanvas", w_getCanvas },
2935
2936 { "setColor", w_setColor },
2937 { "getColor", w_getColor },
2938 { "setBackgroundColor", w_setBackgroundColor },
2939 { "getBackgroundColor", w_getBackgroundColor },
2940
2941 { "setNewFont", w_setNewFont },
2942 { "setFont", w_setFont },
2943 { "getFont", w_getFont },
2944
2945 { "setColorMask", w_setColorMask },
2946 { "getColorMask", w_getColorMask },
2947 { "setBlendMode", w_setBlendMode },
2948 { "getBlendMode", w_getBlendMode },
2949 { "setDefaultFilter", w_setDefaultFilter },
2950 { "getDefaultFilter", w_getDefaultFilter },
2951 { "setDefaultMipmapFilter", w_setDefaultMipmapFilter },
2952 { "getDefaultMipmapFilter", w_getDefaultMipmapFilter },
2953 { "setLineWidth", w_setLineWidth },
2954 { "setLineStyle", w_setLineStyle },
2955 { "setLineJoin", w_setLineJoin },
2956 { "getLineWidth", w_getLineWidth },
2957 { "getLineStyle", w_getLineStyle },
2958 { "getLineJoin", w_getLineJoin },
2959 { "setPointSize", w_setPointSize },
2960 { "getPointSize", w_getPointSize },
2961 { "setDepthMode", w_setDepthMode },
2962 { "getDepthMode", w_getDepthMode },
2963 { "setMeshCullMode", w_setMeshCullMode },
2964 { "getMeshCullMode", w_getMeshCullMode },
2965 { "setFrontFaceWinding", w_setFrontFaceWinding },
2966 { "getFrontFaceWinding", w_getFrontFaceWinding },
2967 { "setWireframe", w_setWireframe },
2968 { "isWireframe", w_isWireframe },
2969
2970 { "setShader", w_setShader },
2971 { "getShader", w_getShader },
2972 { "_setDefaultShaderCode", w_setDefaultShaderCode },
2973
2974 { "getSupported", w_getSupported },
2975 { "getCanvasFormats", w_getCanvasFormats },
2976 { "getImageFormats", w_getImageFormats },
2977 { "getRendererInfo", w_getRendererInfo },
2978 { "getSystemLimits", w_getSystemLimits },
2979 { "getTextureTypes", w_getTextureTypes },
2980 { "getStats", w_getStats },
2981
2982 { "captureScreenshot", w_captureScreenshot },
2983
2984 { "draw", w_draw },
2985 { "drawLayer", w_drawLayer },
2986 { "drawInstanced", w_drawInstanced },
2987
2988 { "print", w_print },
2989 { "printf", w_printf },
2990
2991 { "isCreated", w_isCreated },
2992 { "isActive", w_isActive },
2993 { "isGammaCorrect", w_isGammaCorrect },
2994 { "getWidth", w_getWidth },
2995 { "getHeight", w_getHeight },
2996 { "getDimensions", w_getDimensions },
2997 { "getPixelWidth", w_getPixelWidth },
2998 { "getPixelHeight", w_getPixelHeight },
2999 { "getPixelDimensions", w_getPixelDimensions },
3000 { "getDPIScale", w_getDPIScale },
3001
3002 { "setScissor", w_setScissor },
3003 { "intersectScissor", w_intersectScissor },
3004 { "getScissor", w_getScissor },
3005
3006 { "stencil", w_stencil },
3007 { "setStencilTest", w_setStencilTest },
3008 { "getStencilTest", w_getStencilTest },
3009
3010 { "points", w_points },
3011 { "line", w_line },
3012 { "rectangle", w_rectangle },
3013 { "circle", w_circle },
3014 { "ellipse", w_ellipse },
3015 { "arc", w_arc },
3016 { "polygon", w_polygon },
3017
3018 { "flushBatch", w_flushBatch },
3019
3020 { "getStackDepth", w_getStackDepth },
3021 { "push", w_push },
3022 { "pop", w_pop },
3023 { "rotate", w_rotate },
3024 { "scale", w_scale },
3025 { "translate", w_translate },
3026 { "shear", w_shear },
3027 { "origin", w_origin },
3028 { "applyTransform", w_applyTransform },
3029 { "replaceTransform", w_replaceTransform },
3030 { "transformPoint", w_transformPoint },
3031 { "inverseTransformPoint", w_inverseTransformPoint },
3032
3033 { 0, 0 }
3034};
3035
3036static int luaopen_drawable(lua_State *L)
3037{
3038 return luax_register_type(L, &Drawable::type, nullptr);
3039}
3040
3041// Types for this module.
3042static const lua_CFunction types[] =
3043{
3044 luaopen_drawable,
3045 luaopen_texture,
3046 luaopen_font,
3047 luaopen_image,
3048 luaopen_quad,
3049 luaopen_spritebatch,
3050 luaopen_particlesystem,
3051 luaopen_canvas,
3052 luaopen_shader,
3053 luaopen_mesh,
3054 luaopen_text,
3055 luaopen_video,
3056 0
3057};
3058
3059extern "C" int luaopen_love_graphics(lua_State *L)
3060{
3061 Graphics *instance = instance();
3062 if (instance == nullptr)
3063 {
3064 luax_catchexcept(L, [&](){ instance = new love::graphics::opengl::Graphics(); });
3065 }
3066 else
3067 instance->retain();
3068
3069 WrappedModule w;
3070 w.module = instance;
3071 w.name = "graphics";
3072 w.type = &Graphics::type;
3073 w.functions = functions;
3074 w.types = types;
3075
3076 int n = luax_register_module(L, w);
3077
3078 if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "=[love \"wrap_Graphics.lua\"]") == 0)
3079 lua_call(L, 0, 0);
3080 else
3081 lua_error(L);
3082
3083 if (luaL_loadbuffer(L, (const char *)graphics_shader_lua, sizeof(graphics_shader_lua), "=[love \"wrap_GraphicsShader.lua\"]") == 0)
3084 lua_call(L, 0, 0);
3085 else
3086 lua_error(L);
3087
3088 return n;
3089}
3090
3091} // graphics
3092} // love
3093