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
This commit is contained in:
Alex Szpakowski
2013-03-24 02:20:15 -03:00
parent 2009124cf9
commit 2ea065c68b
9 changed files with 377 additions and 87 deletions
+1
View File
@@ -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
View File
@@ -87,6 +87,7 @@ public:
{
SUPPORT_CANVAS = 1,
SUPPORT_HDR_CANVAS,
SUPPORT_MRT_CANVAS,
SUPPORT_SHADER,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
+130 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,