1 /**
2  * Copyright (c) 2006-2019 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.
44 static 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..
49 static const char graphics_shader_lua[] =
50 #include "wrap_GraphicsShader.lua"
51 ;
52 
53 namespace love
54 {
55 namespace graphics
56 {
57 
58 #define instance() (Module::getInstance<Graphics>(Module::M_GRAPHICS))
59 
luax_checkgraphicscreated(lua_State * L)60 static 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 
w_reset(lua_State *)67 int w_reset(lua_State *)
68 {
69 	instance()->reset();
70 	return 0;
71 }
72 
w_clear(lua_State * L)73 int 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 
w_discard(lua_State * L)156 int 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 
w_present(lua_State * L)181 int w_present(lua_State *L)
182 {
183 	luax_catchexcept(L, [&]() { instance()->present(L); });
184 	return 0;
185 }
186 
w_isCreated(lua_State * L)187 int w_isCreated(lua_State *L)
188 {
189 	luax_pushboolean(L, instance()->isCreated());
190 	return 1;
191 }
192 
w_isActive(lua_State * L)193 int w_isActive(lua_State *L)
194 {
195 	luax_pushboolean(L, instance()->isActive());
196 	return 1;
197 }
198 
w_isGammaCorrect(lua_State * L)199 int w_isGammaCorrect(lua_State *L)
200 {
201 	luax_pushboolean(L, graphics::isGammaCorrect());
202 	return 1;
203 }
204 
w_getWidth(lua_State * L)205 int w_getWidth(lua_State *L)
206 {
207 	lua_pushinteger(L, instance()->getWidth());
208 	return 1;
209 }
210 
w_getHeight(lua_State * L)211 int w_getHeight(lua_State *L)
212 {
213 	lua_pushinteger(L, instance()->getHeight());
214 	return 1;
215 }
216 
w_getDimensions(lua_State * L)217 int w_getDimensions(lua_State *L)
218 {
219 	lua_pushinteger(L, instance()->getWidth());
220 	lua_pushinteger(L, instance()->getHeight());
221 	return 2;
222 }
223 
w_getPixelWidth(lua_State * L)224 int w_getPixelWidth(lua_State *L)
225 {
226 	lua_pushinteger(L, instance()->getPixelWidth());
227 	return 1;
228 }
229 
w_getPixelHeight(lua_State * L)230 int w_getPixelHeight(lua_State *L)
231 {
232 	lua_pushinteger(L, instance()->getPixelHeight());
233 	return 1;
234 }
235 
w_getPixelDimensions(lua_State * L)236 int w_getPixelDimensions(lua_State *L)
237 {
238 	lua_pushinteger(L, instance()->getPixelWidth());
239 	lua_pushinteger(L, instance()->getPixelHeight());
240 	return 2;
241 }
242 
w_getDPIScale(lua_State * L)243 int w_getDPIScale(lua_State *L)
244 {
245 	lua_pushnumber(L, instance()->getScreenDPIScale());
246 	return 1;
247 }
248 
checkRenderTarget(lua_State * L,int idx)249 static 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 
w_setCanvas(lua_State * L)266 int 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 
pushRenderTarget(lua_State * L,const Graphics::RenderTarget & rt)360 static 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 
w_getCanvas(lua_State * L)384 int 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 
screenshotFunctionCallback(const Graphics::ScreenshotInfo * info,love::image::ImageData * i,void * gd)436 static 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 
458 struct ScreenshotFileInfo
459 {
460 	std::string filename;
461 	image::FormatHandler::EncodedFormat format;
462 };
463 
screenshotFileCallback(const Graphics::ScreenshotInfo * info,love::image::ImageData * i,void *)464 static 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 
screenshotChannelCallback(const Graphics::ScreenshotInfo * info,love::image::ImageData * i,void *)487 static 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 
w_captureScreenshot(lua_State * L)503 int 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 
w_setScissor(lua_State * L)555 int 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 
w_intersectScissor(lua_State * L)579 int 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 
w_getScissor(lua_State * L)594 int 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 
w_stencil(lua_State * L)608 int 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 
w_setStencilTest(lua_State * L)646 int 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 
w_getStencilTest(lua_State * L)665 int 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 
parseDPIScale(Data * d,float * dpiscale)681 static 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 
w__optImageSettings(lua_State * L,int idx,bool & setdpiscale)703 static 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 
727 static std::pair<StrongRef<image::ImageData>, StrongRef<image::CompressedImageData>>
getImageData(lua_State * L,int idx,bool allowcompressed,float * dpiscale)728 getImageData(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 
w__pushNewImage(lua_State * L,Image::Slices & slices,const Image::Settings & settings)760 static 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 
w_newCubeImage(lua_State * L)772 int 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 
w_newArrayImage(lua_State * L)866 int 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 
w_newVolumeImage(lua_State * L)930 int 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, mip + 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 
w_newImage(lua_State * L)1003 int 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 
w_newQuad(lua_State * L)1039 int 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 
w_newFont(lua_State * L)1086 int 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 
w_newImageFont(lua_State * L)1114 int 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 
w_newSpriteBatch(lua_State * L)1144 int 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 
w_newParticleSystem(lua_State * L)1168 int 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 
w_newCanvas(lua_State * L)1187 int 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 
w_getShaderSource(lua_State * L,int startidx,bool gles,std::string & vertexsource,std::string & pixelsource)1260 static 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 
w_newShader(lua_State * L)1364 int 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 
w_validateShader(lua_State * L)1394 int 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 
luax_optmeshusage(lua_State * L,int idx,vertex::Usage def)1424 static 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 
luax_optmeshdrawmode(lua_State * L,int idx,PrimitiveType def)1434 static 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 
newStandardMesh(lua_State * L)1444 static 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 
newCustomMesh(lua_State * L)1500 static 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 
w_newMesh(lua_State * L)1620 int 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 
w_newText(lua_State * L)1642 int 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 
w_newVideo(lua_State * L)1664 int 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 
w_setColor(lua_State * L)1682 int 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 
w_getColor(lua_State * L)1708 int 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 
w_setBackgroundColor(lua_State * L)1718 int 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 
w_getBackgroundColor(lua_State * L)1744 int 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 
w_setNewFont(lua_State * L)1754 int 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 
w_setFont(lua_State * L)1762 int w_setFont(lua_State *L)
1763 {
1764 	Font *font = luax_checktype<Font>(L, 1);
1765 	instance()->setFont(font);
1766 	return 0;
1767 }
1768 
w_getFont(lua_State * L)1769 int 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 
w_setColorMask(lua_State * L)1778 int 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 
w_getColorMask(lua_State * L)1800 int 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 
w_setBlendMode(lua_State * L)1812 int 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 
w_getBlendMode(lua_State * L)1831 int 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 
w_setDefaultFilter(lua_State * L)1850 int 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 
w_getDefaultFilter(lua_State * L)1869 int 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 
w_setDefaultMipmapFilter(lua_State * L)1884 int 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 
w_getDefaultMipmapFilter(lua_State * L)1901 int 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 
w_setLineWidth(lua_State * L)1919 int w_setLineWidth(lua_State *L)
1920 {
1921 	float width = (float)luaL_checknumber(L, 1);
1922 	instance()->setLineWidth(width);
1923 	return 0;
1924 }
1925 
w_setLineStyle(lua_State * L)1926 int 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 
w_setLineJoin(lua_State * L)1937 int 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 
w_getLineWidth(lua_State * L)1948 int w_getLineWidth(lua_State *L)
1949 {
1950 	lua_pushnumber(L, instance()->getLineWidth());
1951 	return 1;
1952 }
1953 
w_getLineStyle(lua_State * L)1954 int 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 
w_getLineJoin(lua_State * L)1964 int 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 
w_setPointSize(lua_State * L)1974 int w_setPointSize(lua_State *L)
1975 {
1976 	float size = (float)luaL_checknumber(L, 1);
1977 	instance()->setPointSize(size);
1978 	return 0;
1979 }
1980 
w_getPointSize(lua_State * L)1981 int w_getPointSize(lua_State *L)
1982 {
1983 	lua_pushnumber(L, instance()->getPointSize());
1984 	return 1;
1985 }
1986 
w_setDepthMode(lua_State * L)1987 int 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 
w_getDepthMode(lua_State * L)2006 int 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 
w_setMeshCullMode(lua_State * L)2021 int 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 
w_getMeshCullMode(lua_State * L)2033 int 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 
w_setFrontFaceWinding(lua_State * L)2043 int 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 
w_getFrontFaceWinding(lua_State * L)2055 int 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 
w_setWireframe(lua_State * L)2065 int w_setWireframe(lua_State *L)
2066 {
2067 	instance()->setWireframe(luax_checkboolean(L, 1));
2068 	return 0;
2069 }
2070 
w_isWireframe(lua_State * L)2071 int w_isWireframe(lua_State *L)
2072 {
2073 	luax_pushboolean(L, instance()->isWireframe());
2074 	return 1;
2075 }
2076 
w_setShader(lua_State * L)2077 int 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 
w_getShader(lua_State * L)2090 int 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 
w_setDefaultShaderCode(lua_State * L)2101 int 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 
w_getSupported(lua_State * L)2141 int 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 
w__getFormats(lua_State * L,int idx,bool (* isFormatSupported)(PixelFormat),bool (* ignore)(PixelFormat))2165 static 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 
w_getCanvasFormats(lua_State * L)2187 int 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 
w_getImageFormats(lua_State * L)2221 int 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 
w_getTextureTypes(lua_State * L)2236 int 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 
w_getRendererInfo(lua_State * L)2260 int 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 
w_getSystemLimits(lua_State * L)2272 int 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 
w_getStats(lua_State * L)2296 int 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 
w_draw(lua_State * L)2332 int 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 
w_drawLayer(lua_State * L)2369 int 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 
w_drawInstanced(lua_State * L)2401 int 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 
w_print(lua_State * L)2414 int 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 
w_printf(lua_State * L)2439 int 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 
w_points(lua_State * L)2494 int 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 
w_line(lua_State * L)2583 int 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 
w_rectangle(lua_State * L)2632 int 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 
w_circle(lua_State * L)2664 int 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 
w_ellipse(lua_State * L)2686 int 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 
w_arc(lua_State * L)2709 int 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 
w_polygon(lua_State * L)2746 int 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 
w_flushBatch(lua_State *)2798 int w_flushBatch(lua_State *)
2799 {
2800 	instance()->flushStreamDraws();
2801 	return 0;
2802 }
2803 
w_getStackDepth(lua_State * L)2804 int w_getStackDepth(lua_State *L)
2805 {
2806 	lua_pushnumber(L, instance()->getStackDepth());
2807 	return 1;
2808 }
2809 
w_push(lua_State * L)2810 int 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 
w_pop(lua_State * L)2828 int w_pop(lua_State *L)
2829 {
2830 	luax_catchexcept(L, [&](){ instance()->pop(); });
2831 	return 0;
2832 }
2833 
w_rotate(lua_State * L)2834 int w_rotate(lua_State *L)
2835 {
2836 	float angle = (float)luaL_checknumber(L, 1);
2837 	instance()->rotate(angle);
2838 	return 0;
2839 }
2840 
w_scale(lua_State * L)2841 int 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 
w_translate(lua_State * L)2849 int 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 
w_shear(lua_State * L)2857 int 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 
w_origin(lua_State *)2865 int w_origin(lua_State * /*L*/)
2866 {
2867 	instance()->origin();
2868 	return 0;
2869 }
2870 
w_applyTransform(lua_State * L)2871 int 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 
w_replaceTransform(lua_State * L)2878 int 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 
w_transformPoint(lua_State * L)2885 int 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 
w_inverseTransformPoint(lua_State * L)2896 int 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.
2909 static 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 
luaopen_drawable(lua_State * L)3036 static int luaopen_drawable(lua_State *L)
3037 {
3038 	return luax_register_type(L, &Drawable::type, nullptr);
3039 }
3040 
3041 // Types for this module.
3042 static 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 
luaopen_love_graphics(lua_State * L)3059 extern "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), "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), "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