Forráskód Böngészése

Added MRT support to canvases via love.graphics.setCanvases(c1, c2, ...), and an 'effects' callback function in shaders.
- Simultanious rendering to multiple canvases requires all canvases to be the same size (limitation of pre-GL3.0 framebuffers)
- love.graphics.isSupported("mrtcanvas") was added
- love_Canvases[n] (0-based array of active canvases) can be written to in pixel shaders in the 'effects' callback function
- everything drawn to multiple canvases at once will be duplicated to all canvases if no shader is active or the standard 'effect' shader function is used

--HG--
branch : MRTs

Alex Szpakowski 12 éve
szülő
commit
2ea065c68b

+ 1 - 0
src/modules/graphics/Graphics.cpp

@@ -158,6 +158,7 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
 {
 	{ "canvas", Graphics::SUPPORT_CANVAS },
 	{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
+	{ "mrtcanvas", Graphics::SUPPORT_MRT_CANVAS },
 	{ "shader", Graphics::SUPPORT_SHADER },
 	{ "npot", Graphics::SUPPORT_NPOT },
 	{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },

+ 1 - 0
src/modules/graphics/Graphics.h

@@ -87,6 +87,7 @@ public:
 	{
 		SUPPORT_CANVAS = 1,
 		SUPPORT_HDR_CANVAS,
+		SUPPORT_MRT_CANVAS,
 		SUPPORT_SHADER,
 		SUPPORT_NPOT,
 		SUPPORT_SUBTRACTIVE,

+ 130 - 2
src/modules/graphics/opengl/Canvas.cpp

@@ -59,6 +59,12 @@ struct FramebufferStrategy
 	 */
 	virtual void deleteFBO(GLuint, GLuint, GLuint) {}
 	virtual void bindFBO(GLuint) {}
+
+	/// attach additional canvases to this framebuffer for rendering
+	/**
+	 * @param[in] canvases List of canvases to attach
+	 **/
+	virtual void setAttachments(const std::vector<Canvas *> &canvases) {}
 };
 
 struct FramebufferStrategyGL3 : public FramebufferStrategy
@@ -127,6 +133,35 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
 	{
 		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 	}
+
+	virtual void setAttachments(const std::vector<Canvas *> &canvases)
+	{
+		if (canvases.size() == 0)
+		{
+			// set a single render target
+			glDrawBuffer(GL_COLOR_ATTACHMENT0);
+			return;
+		}
+
+		std::vector<GLenum> drawbuffers;
+		drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
+
+		// attach the canvas framebuffer textures to the currently bound framebuffer
+		for (int i = 0; i < canvases.size(); i++)
+		{
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
+				GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
+			drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
+		}
+
+		// set up multiple render targets
+		if (GLEE_VERSION_2_0)
+			glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
+		else if (GLEE_ARB_draw_buffers)
+			glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
+		else if (GLEE_ATI_draw_buffers)
+			glDrawBuffersATI(drawbuffers.size(), &drawbuffers[0]);
+	}
 };
 
 struct FramebufferStrategyPackedEXT : public FramebufferStrategy
@@ -196,6 +231,35 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 	{
 		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
 	}
+
+	virtual void setAttachments(const std::vector<Canvas *> &canvases)
+	{
+		if (canvases.size() == 0)
+		{
+			// set a single render target
+			glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+			return;
+		}
+
+		std::vector<GLenum> drawbuffers;
+		drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
+
+		// attach the canvas framebuffer textures to the currently bound framebuffer
+		for (int i = 0; i < canvases.size(); i++)
+		{
+			glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
+								   GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
+			drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
+		}
+
+		// set up multiple render targets
+		if (GLEE_VERSION_2_0)
+			glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
+		else if (GLEE_ARB_draw_buffers)
+			glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
+		else if (GLEE_ATI_draw_buffers)
+			glDrawBuffersATI(drawbuffers.size(), &drawbuffers[0]);
+	}
 };
 
 struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
@@ -288,6 +352,9 @@ static void getStrategy()
 	}
 }
 
+static int maxFBOColorAttachments = 0;
+static int maxDrawBuffers = 0;
+
 Canvas::Canvas(int width, int height, TextureType texture_type)
 	: width(width)
 	, height(height)
@@ -338,11 +405,26 @@ bool Canvas::isSupported()
 	return (strategy != &strategyNone);
 }
 
-bool Canvas::isHdrSupported()
+bool Canvas::isHDRSupported()
 {
 	return GLEE_VERSION_3_0 || GLEE_ARB_texture_float;
 }
 
+bool Canvas::isMRTSupported()
+{
+	if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers || GLEE_ATI_draw_buffers)))
+		return false;
+
+	if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
+	{
+		glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
+		glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+	}
+
+	// system must support at least 4 simultanious render targets
+	return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
+}
+
 void Canvas::bindDefaultCanvas()
 {
 	if (current != NULL)
@@ -379,6 +461,42 @@ void Canvas::startGrab()
 	current = this;
 }
 
+void Canvas::startGrab(const std::vector<Canvas *> &canvases)
+{
+	if (canvases.size() > 0)
+	{
+		if (!isMRTSupported())
+			throw love::Exception("Multiple render targets are not supported on this system.");
+
+		if (canvases.size() + 1 > maxDrawBuffers || canvases.size() + 1 > maxFBOColorAttachments)
+			throw love::Exception("This system can't support %d simultanious render targets.", canvases.size() + 1);
+	}
+
+	for (size_t i = 0; i < canvases.size(); i++)
+	{
+		if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
+			throw love::Exception("All canvas arguments must have the same dimensions.");
+	}
+
+	startGrab();
+
+	// don't attach anything if there's nothing to attach/detach
+	if (canvases.size() == 0 && attachedCanvases.size() == 0)
+		return;
+
+	strategy->setAttachments(canvases);
+
+	// retain newly attached canvases
+	for (size_t i = 0; i < canvases.size(); i++)
+		canvases[i]->retain();
+
+	// release previously attached canvases
+	for (size_t i = 0; i < attachedCanvases.size(); i++)
+		attachedCanvases[i]->release();
+
+	attachedCanvases = canvases;
+}
+
 void Canvas::stopGrab()
 {
 	// i am not grabbing. leave me alone
@@ -393,7 +511,6 @@ void Canvas::stopGrab()
 	current = NULL;
 }
 
-
 void Canvas::clear(const Color &c)
 {
 	GLuint previous = 0;
@@ -472,6 +589,11 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
 		strategy->bindFBO( 0 );
 }
 
+const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
+{
+	return attachedCanvases;
+}
+
 void Canvas::setFilter(const Image::Filter &f)
 {
 	bindTexture(img);
@@ -515,6 +637,12 @@ void Canvas::unloadVolatile()
 	settings.filter = getFilter();
 	settings.wrap   = getWrap();
 	strategy->deleteFBO(fbo, depth_stencil, img);
+
+	// release attached canvases
+	for (size_t i = 0; i < attachedCanvases.size(); i++)
+		attachedCanvases[i]->release();
+
+	attachedCanvases.clear();
 }
 
 int Canvas::getWidth()

+ 19 - 4
src/modules/graphics/opengl/Canvas.h

@@ -21,6 +21,7 @@
 #ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
 #define LOVE_GRAPHICS_OPENGL_CANVAS_H
 
+// LOVE
 #include "graphics/DrawQable.h"
 #include "graphics/Volatile.h"
 #include "graphics/Image.h"
@@ -31,6 +32,9 @@
 #include "common/Matrix.h"
 #include "OpenGL.h"
 
+// STL
+#include <vector>
+
 namespace love
 {
 namespace graphics
@@ -50,7 +54,11 @@ public:
 	Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
 	virtual ~Canvas();
 
-	void startGrab();
+	/**
+	 * @param canvases A list of other canvases to temporarily attach to this one,
+	 * to allow drawing to multiple canvases at once.
+	 **/
+	void startGrab(const std::vector<Canvas *> &canvases);
 	void stopGrab();
 
 	void clear(const Color &c);
@@ -66,6 +74,8 @@ public:
 
 	void getPixel(unsigned char* pixel_rgba, int x, int y);
 
+	const std::vector<Canvas *> &getAttachedCanvases() const;
+
 	void setFilter(const Image::Filter &f);
 	Image::Filter getFilter() const;
 
@@ -89,20 +99,22 @@ public:
 	void unloadVolatile();
 
 	static bool isSupported();
-	static bool isHdrSupported();
+	static bool isHDRSupported();
+	static bool isMRTSupported();
 	static bool getConstant(const char *in, TextureType &out);
 	static bool getConstant(TextureType in, const char *&out);
 
 	static Canvas *current;
 	static void bindDefaultCanvas();
 
-private:
-	friend class Shader;
 	GLuint getTextureName() const
 	{
 		return img;
 	}
 
+private:
+	friend class Shader;
+
 	GLsizei width;
 	GLsizei height;
 	GLuint fbo;
@@ -121,6 +133,9 @@ private:
 		Image::Wrap   wrap;
 	} settings;
 
+	std::vector<Canvas *> attachedCanvases;
+
+	void startGrab();
 	void drawv(const Matrix &t, const vertex *v) const;
 
 	static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];

+ 1 - 1
src/modules/graphics/opengl/Graphics.cpp

@@ -397,7 +397,7 @@ ParticleSystem *Graphics::newParticleSystem(Image *image, int size)
 
 Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type)
 {
-	if (texture_type == Canvas::TYPE_HDR && !Canvas::isHdrSupported())
+	if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported())
 		throw Exception("HDR Canvases are not supported by your OpenGL implementation");
 
 	while (GL_NO_ERROR != glGetError())

+ 9 - 1
src/modules/graphics/opengl/wrap_Canvas.cpp

@@ -47,7 +47,15 @@ int w_Canvas_renderTo(lua_State *L)
 	if (!lua_isfunction(L, 2))
 		return luaL_error(L, "Need a function to render to canvas.");
 
-	canvas->startGrab();
+	static std::vector<Canvas *> attachments;
+	try
+	{
+		canvas->startGrab(attachments);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	lua_settop(L, 2); // make sure the function is on top of the stack
 	lua_call(L, 0, 0);
 	canvas->stopGrab();

+ 54 - 5
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -942,9 +942,41 @@ int w_setCanvas(lua_State *L)
 		return 0;
 	}
 
-	Canvas *canvas = luax_checkcanvas(L, 1);
-	// this unbinds the previous fbo
-	canvas->startGrab();
+	bool is_table = lua_istable(L, 1);
+	std::vector<Canvas *> attachments;
+
+	Canvas *canvas = 0;
+
+	if (is_table)
+	{
+		lua_pushinteger(L, 1);
+		lua_gettable(L, 1);
+		canvas = luax_checkcanvas(L, -1);
+		lua_pop(L, 1);
+
+		for (int i = 2; i <= lua_objlen(L, 1); i++)
+		{
+			lua_pushinteger(L, i);
+			lua_gettable(L, 1);
+			attachments.push_back(luax_checkcanvas(L, -1));
+			lua_pop(L, 1);
+		}
+	}
+	else
+	{
+		canvas = luax_checkcanvas(L, 1);
+		for (int i = 2; i <= lua_gettop(L); i++)
+			attachments.push_back(luax_checkcanvas(L, i));
+	}
+
+	try
+	{
+		canvas->startGrab(attachments);
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 
 	return 0;
 }
@@ -952,14 +984,25 @@ int w_setCanvas(lua_State *L)
 int w_getCanvas(lua_State *L)
 {
 	Canvas *canvas = Canvas::current;
+	int n = 1;
+
 	if (canvas)
 	{
 		canvas->retain();
 		luax_newtype(L, "Canvas", GRAPHICS_CANVAS_T, (void *) canvas);
+
+		const std::vector<Canvas *> &attachments = canvas->getAttachedCanvases();
+		for (size_t i = 0; i < attachments.size(); i++)
+		{
+			attachments[i]->retain();
+			luax_newtype(L, "Canvas", GRAPHICS_CANVAS_T, (void *) attachments[i]);
+			n++;
+		}
 	}
 	else
 		lua_pushnil(L);
-	return 1;
+
+	return n;
 }
 
 int w_setShader(lua_State *L)
@@ -1009,7 +1052,11 @@ int w_isSupported(lua_State *L)
 				supported = false;
 			break;
 		case Graphics::SUPPORT_HDR_CANVAS:
-			if (!Canvas::isHdrSupported())
+			if (!Canvas::isHDRSupported())
+				supported = false;
+			break;
+		case Graphics::SUPPORT_MRT_CANVAS:
+			if (!Canvas::isMRTSupported())
 				supported = false;
 			break;
 		case Graphics::SUPPORT_SHADER:
@@ -1427,7 +1474,9 @@ static const luaL_Reg functions[] =
 	{ "getMaxPointSize", w_getMaxPointSize },
 	{ "newScreenshot", w_newScreenshot },
 	{ "setCanvas", w_setCanvas },
+	{ "setCanvases", w_setCanvas },
 	{ "getCanvas", w_getCanvas },
+	{ "getCanvases", w_getCanvas },
 
 	{ "setShader", w_setShader },
 	{ "getShader", w_getShader },

+ 54 - 20
src/scripts/graphics.lua

@@ -1295,7 +1295,8 @@ do
 #define number float
 #define Image sampler2D
 #define extern uniform
-#define Texel texture2D]]
+#define Texel texture2D
+#define love_Canvases gl_FragData]]
 
 	local GLSL_UNIFORMS = [[
 #define ModelViewMatrix gl_ModelViewMatrix
@@ -1332,10 +1333,17 @@ void main() {
 
 		FOOTER = [[
 void main() {
-	// fix weird crashing issue in OSX when _tex0_ is unused within effect()
+	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	float dummy = texture2D(_tex0_, vec2(.5)).r;
 	gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, gl_FragCoord.xy);
 }]],
+
+		FOOTER_MRT = [[
+void main() {
+	// fix crashing issue in OSX when _tex0_ is unused within effect()
+	float dummy = texture2D(_tex0_, vec2(.5)).r;
+	effects(VaryingColor, _tex0_, VaryingTexCoord.st, gl_FragCoord.xy);
+}]],
 	}
 
 	local function createVertexCode(vertexcode)
@@ -1349,36 +1357,62 @@ void main() {
 		return table_concat(vertexcodes, "\n")
 	end
 
-	local function createPixelCode(pixelcode)
+	local function createPixelCode(pixelcode, is_mrt)
 		local pixelcodes = {
 			GLSL_VERSION,
 			GLSL_SYNTAX, GLSL_PIXEL.HEADER, GLSL_UNIFORMS,
 			"#line 0",
 			pixelcode,
-			GLSL_PIXEL.FOOTER
+			is_mrt and GLSL_PIXEL.FOOTER_MRT or GLSL_PIXEL.FOOTER,
 		}
 		return table_concat(pixelcodes, "\n")
 	end
 
-	function love.graphics._shaderCodeToGLSL(vertexcode, pixelcode)
-		if vertexcode then
-			local s = vertexcode:gsub("\r\n\t", " ")
-			s = s:gsub("(%w+)(%s+)%(", "%1(")
-			if s:match("vec4%s*effect%(") then
-				pixelcode = vertexcode -- first argument contains pixel shader code
+	local function isVertexCode(code)
+		return code:match("vec4%s*position%(") ~= nil
+	end
+
+	local function isPixelCode(code)
+		if code:match("vec4%s*effect%(") then
+			return true
+		elseif code:match("void%s*effects%(") then -- multiple render targets (MRT)
+			return true, true
+		else
+			return false
+		end
+	end
+
+	function love.graphics._shaderCodeToGLSL(arg1, arg2)
+		local vertexcode, pixelcode
+		local is_mrt = false -- whether pixel code has "effects" function instead of "effect"
+		
+		if arg1 then
+			local s = arg1:gsub("\r\n\t", " ") -- convert whitespace to spaces for parsing
+			s = s:gsub("(%w+)(%s+)%(", "%1(") -- convert "func ()" to "func()"
+
+			if isVertexCode(s) then
+				vertexcode = arg1 -- first arg contains vertex shader code
 			end
-			if not s:match("vec4%s*position%(") then
-				vertexcode = nil -- first argument doesn't contain vertex shader code
+
+			local ispixel, isMRT = isPixelCode(s)
+			if ispixel then
+				pixelcode = arg1 -- first arg contains pixel shader code
+				is_mrt = isMRT
 			end
 		end
-		if pixelcode then
-			local s = pixelcode:gsub("\r\n\t", " ")
-			s = s:gsub("(%w+)(%s+)%(", "%1(")
-			if s:match("vec4%s*position%(") then
-				vertexcode = pixelcode -- second argument contains vertex shader code
+		
+		if arg2 then
+			local s = arg2:gsub("\r\n\t", " ") -- convert whitespace to spaces for parsing
+			s = s:gsub("(%w+)(%s+)%(", "%1(") -- convert "func ()" to "func()"
+
+			if isVertexCode(s) then
+				vertexcode = arg2 -- second arg contains vertex shader code
 			end
-			if not s:match("vec4%s*effect%(") then
-				pixelcode = nil -- second argument doesn't contain pixel shader code
+
+			local ispixel, isMRT = isPixelCode(s)
+			if ispixel then
+				pixelcode = arg2 -- second arg contains pixel shader code
+				is_mrt = isMRT
 			end
 		end
 
@@ -1386,7 +1420,7 @@ void main() {
 			vertexcode = createVertexCode(vertexcode)
 		end
 		if pixelcode then
-			pixelcode = createPixelCode(pixelcode)
+			pixelcode = createPixelCode(pixelcode, is_mrt)
 		end
 
 		return vertexcode, pixelcode

+ 108 - 54
src/scripts/graphics.lua.h

@@ -6276,7 +6276,9 @@ const unsigned char graphics_lua[] =
 	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x69, 
 	0x66, 0x6f, 0x72, 0x6d, 0x0a,
 	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x54, 0x65, 0x78, 0x65, 0x6c, 0x20, 0x74, 0x65, 0x78, 0x74, 
-	0x75, 0x72, 0x65, 0x32, 0x44, 0x5d, 0x5d, 0x0a,
+	0x75, 0x72, 0x65, 0x32, 0x44, 0x0a,
+	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x43, 0x61, 0x6e, 0x76, 0x61, 
+	0x73, 0x65, 0x73, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x5d, 0x5d, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 
 	0x4d, 0x53, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
 	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x4d, 
@@ -6333,11 +6335,10 @@ const unsigned char graphics_lua[] =
 	0x6f, 0x72, 0x20, 0x67, 0x6c, 0x5f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
 	0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
-	0x09, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x77, 0x65, 0x69, 0x72, 0x64, 0x20, 0x63, 0x72, 0x61, 0x73, 
-	0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x4f, 0x53, 0x58, 0x20, 
-	0x77, 0x68, 0x65, 0x6e, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x20, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x75, 
-	0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 
-	0x29, 0x0a,
+	0x09, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x63, 0x72, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 
+	0x73, 0x73, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x4f, 0x53, 0x58, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x5f, 
+	0x74, 0x65, 0x78, 0x30, 0x5f, 0x20, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 
+	0x74, 0x68, 0x69, 0x6e, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x29, 0x0a,
 	0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 
 	0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76, 0x65, 0x63, 
 	0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
@@ -6347,6 +6348,20 @@ const unsigned char graphics_lua[] =
 	0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 
 	0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
 	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
+	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x52, 0x54, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
+	0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
+	0x09, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x63, 0x72, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 
+	0x73, 0x73, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x4f, 0x53, 0x58, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x5f, 
+	0x74, 0x65, 0x78, 0x30, 0x5f, 0x20, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 
+	0x74, 0x68, 0x69, 0x6e, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x29, 0x0a,
+	0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 
+	0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76, 0x65, 0x63, 
+	0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
+	0x09, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x28, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 
+	0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 
+	0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 
+	0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
+	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x7d, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 
 	0x65, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x76, 0x65, 0x72, 
@@ -6368,7 +6383,7 @@ const unsigned char graphics_lua[] =
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 
 	0x65, 0x61, 0x74, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x70, 0x69, 0x78, 0x65, 
-	0x6c, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
+	0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x29, 0x0a,
 	0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x73, 
 	0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x09, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x2c, 0x0a,
@@ -6377,65 +6392,104 @@ const unsigned char graphics_lua[] =
 	0x4c, 0x53, 0x4c, 0x5f, 0x55, 0x4e, 0x49, 0x46, 0x4f, 0x52, 0x4d, 0x53, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x22, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x30, 0x22, 0x2c, 0x0a,
 	0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x0a,
-	0x09, 0x09, 0x09, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x46, 0x4f, 0x4f, 0x54, 
-	0x45, 0x52, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x4c, 0x53, 0x4c, 
+	0x5f, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x52, 0x54, 0x20, 
+	0x6f, 0x72, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x2e, 0x46, 0x4f, 0x4f, 0x54, 
+	0x45, 0x52, 0x2c, 0x0a,
 	0x09, 0x09, 0x7d, 0x0a,
 	0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 
 	0x63, 0x61, 0x74, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x2c, 0x20, 0x22, 0x5c, 
 	0x6e, 0x22, 0x29, 0x0a,
 	0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 
+	0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
+	0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x6d, 0x61, 0x74, 0x63, 
+	0x68, 0x28, 0x22, 0x76, 0x65, 0x63, 0x34, 0x25, 0x73, 0x2a, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 
+	0x25, 0x28, 0x22, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a,
+	0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 
+	0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
+	0x09, 0x09, 0x69, 0x66, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 0x22, 0x76, 
+	0x65, 0x63, 0x34, 0x25, 0x73, 0x2a, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x25, 0x28, 0x22, 0x29, 0x20, 0x74, 
+	0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a,
+	0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x6d, 0x61, 0x74, 0x63, 
+	0x68, 0x28, 0x22, 0x76, 0x6f, 0x69, 0x64, 0x25, 0x73, 0x2a, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x25, 
+	0x28, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x2d, 0x2d, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 
+	0x6c, 0x65, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x20, 
+	0x28, 0x4d, 0x52, 0x54, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x74, 0x72, 
+	0x75, 0x65, 0x0a,
+	0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
+	0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 
 	0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x54, 
-	0x6f, 0x47, 0x4c, 0x53, 0x4c, 0x28, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 
-	0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
-	0x09, 0x09, 0x69, 0x66, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 
-	0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 
-	0x78, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x5c, 0x74, 
-	0x22, 0x2c, 0x20, 0x22, 0x20, 0x22, 0x29, 0x0a,
+	0x6f, 0x47, 0x4c, 0x53, 0x4c, 0x28, 0x61, 0x72, 0x67, 0x31, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x32, 0x29, 0x0a,
+	0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 
+	0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x0a,
+	0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x66, 
+	0x61, 0x6c, 0x73, 0x65, 0x20, 0x2d, 0x2d, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x70, 0x69, 
+	0x78, 0x65, 0x6c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x22, 0x65, 0x66, 0x66, 0x65, 
+	0x63, 0x74, 0x73, 0x22, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 
+	0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x22, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x22, 0x0a,
+	0x09, 0x09, 0x0a,
+	0x09, 0x09, 0x69, 0x66, 0x20, 0x61, 0x72, 0x67, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x31, 0x3a, 
+	0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x5c, 0x74, 0x22, 0x2c, 0x20, 0x22, 0x20, 0x22, 
+	0x29, 0x20, 0x2d, 0x2d, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 
+	0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20, 0x66, 0x6f, 
+	0x72, 0x20, 0x70, 0x61, 0x72, 0x73, 0x69, 0x6e, 0x67, 0x0a,
 	0x09, 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x28, 0x25, 0x77, 
-	0x2b, 0x29, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x28, 0x22, 0x29, 0x0a,
-	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x73, 0x3a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 0x22, 0x76, 0x65, 0x63, 
-	0x34, 0x25, 0x73, 0x2a, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x25, 0x28, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 
-	0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x76, 0x65, 
-	0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 
-	0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 
-	0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
+	0x2b, 0x29, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x28, 0x22, 0x29, 
+	0x20, 0x2d, 0x2d, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x20, 
+	0x28, 0x29, 0x22, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x22, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x69, 0x73, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 
+	0x28, 0x73, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x61, 
+	0x72, 0x67, 0x31, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x20, 0x63, 
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 
+	0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
 	0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
-	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x3a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 
-	0x22, 0x76, 0x65, 0x63, 0x34, 0x25, 0x73, 0x2a, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x25, 0x28, 
-	0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x6e, 
-	0x69, 0x6c, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 
-	0x6e, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 
-	0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 
-	0x65, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x73, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 
+	0x69, 0x73, 0x4d, 0x52, 0x54, 0x20, 0x3d, 0x20, 0x69, 0x73, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 
+	0x65, 0x28, 0x73, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x69, 0x73, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x61, 0x72, 
+	0x67, 0x31, 0x20, 0x2d, 0x2d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x20, 0x63, 0x6f, 
+	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 
+	0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x73, 0x4d, 0x52, 0x54, 0x0a,
 	0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
-	0x09, 0x09, 0x69, 0x66, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 
-	0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 
-	0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x5c, 0x74, 0x22, 
-	0x2c, 0x20, 0x22, 0x20, 0x22, 0x29, 0x0a,
+	0x09, 0x09, 0x0a,
+	0x09, 0x09, 0x69, 0x66, 0x20, 0x61, 0x72, 0x67, 0x32, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x32, 0x3a, 
+	0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x5c, 0x72, 0x5c, 0x6e, 0x5c, 0x74, 0x22, 0x2c, 0x20, 0x22, 0x20, 0x22, 
+	0x29, 0x20, 0x2d, 0x2d, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 
+	0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20, 0x66, 0x6f, 
+	0x72, 0x20, 0x70, 0x61, 0x72, 0x73, 0x69, 0x6e, 0x67, 0x0a,
 	0x09, 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x3a, 0x67, 0x73, 0x75, 0x62, 0x28, 0x22, 0x28, 0x25, 0x77, 
-	0x2b, 0x29, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x28, 0x22, 0x29, 0x0a,
-	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x73, 0x3a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 0x22, 0x76, 0x65, 0x63, 
-	0x34, 0x25, 0x73, 0x2a, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x25, 0x28, 0x22, 0x29, 0x20, 0x74, 
-	0x68, 0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x70, 
-	0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 
-	0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 
-	0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 
-	0x65, 0x0a,
+	0x2b, 0x29, 0x28, 0x25, 0x73, 0x2b, 0x29, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x28, 0x22, 0x29, 
+	0x20, 0x2d, 0x2d, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x20, 
+	0x28, 0x29, 0x22, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x22, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x69, 0x73, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x43, 0x6f, 0x64, 0x65, 
+	0x28, 0x73, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x61, 
+	0x72, 0x67, 0x32, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x67, 0x20, 
+	0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 
+	0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
 	0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
-	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x3a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 
-	0x22, 0x76, 0x65, 0x63, 0x34, 0x25, 0x73, 0x2a, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x25, 0x28, 0x22, 0x29, 
-	0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
-	0x09, 0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x6e, 0x69, 
-	0x6c, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 
-	0x6e, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 
-	0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
+	0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x73, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2c, 0x20, 
+	0x69, 0x73, 0x4d, 0x52, 0x54, 0x20, 0x3d, 0x20, 0x69, 0x73, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 
+	0x65, 0x28, 0x73, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x69, 0x73, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x61, 0x72, 
+	0x67, 0x32, 0x20, 0x2d, 0x2d, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x67, 0x20, 0x63, 
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x73, 0x68, 0x61, 0x64, 
+	0x65, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x73, 0x4d, 0x52, 0x54, 0x0a,
 	0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x69, 0x66, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 
@@ -6448,7 +6502,7 @@ const unsigned char graphics_lua[] =
 	0x6e, 0x0a,
 	0x09, 0x09, 0x09, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x72, 0x65, 
 	0x61, 0x74, 0x65, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x28, 0x70, 0x69, 0x78, 0x65, 0x6c, 
-	0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
+	0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x73, 0x5f, 0x6d, 0x72, 0x74, 0x29, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x64, 
 	0x65, 0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x0a,