Scripting: Export current image API

This commit is contained in:
Vicki Pfau 2023-04-02 02:59:48 -07:00
parent ed69b9f741
commit 2fca2f4395
5 changed files with 193 additions and 5 deletions

View File

@ -11,8 +11,12 @@
CXX_GUARD_START
#include <mgba/script/macros.h>
#include <mgba-util/image.h>
mSCRIPT_DECLARE_STRUCT(mImage)
struct mScriptContext;
void mScriptContextAttachImage(struct mScriptContext* context);
void mScriptContextAttachStdlib(struct mScriptContext* context);
void mScriptContextAttachSocket(struct mScriptContext* context);

View File

@ -2,6 +2,7 @@ include(ExportDirectory)
set(SOURCE_FILES
context.c
input.c
image.c
socket.c
stdlib.c
types.c)
@ -18,6 +19,7 @@ if(USE_LUA)
list(APPEND SOURCE_FILES engines/lua.c)
list(APPEND TEST_FILES
test/context.c
test/image.c
test/input.c
test/lua.c
test/stdlib.c)

View File

@ -7,10 +7,7 @@
#include <mgba/core/scripting.h>
#include <mgba/core/version.h>
#include <mgba/internal/script/types.h>
#include <mgba/script/base.h>
#include <mgba/script/context.h>
#include <mgba/script/input.h>
#include <mgba/script/storage.h>
#include <mgba/script.h>
#include <mgba-util/string.h>
struct mScriptContext context;
@ -473,9 +470,10 @@ int main(int argc, char* argv[]) {
mScriptContextInit(&context);
mScriptContextAttachStdlib(&context);
mScriptContextAttachImage(&context);
mScriptContextAttachInput(&context);
mScriptContextAttachSocket(&context);
mScriptContextAttachStorage(&context);
mScriptContextAttachInput(&context);
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
initTypes();

73
src/script/image.c Normal file
View File

@ -0,0 +1,73 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/script.h>
static struct mScriptValue* _mImageNew(unsigned width, unsigned height) {
// For various reasons, it's probably a good idea to limit the maximum image size scripts can make
if (width >= 10000 || height >= 10000) {
return NULL;
}
struct mImage* image = mImageCreate(width, height, mCOLOR_ABGR8);
if (!image) {
return NULL;
}
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
result->value.opaque = image;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
static struct mScriptValue* _mImageLoad(const char* path) {
struct mImage* image = mImageLoad(path);
if (!image) {
return NULL;
}
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
result->value.opaque = image;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
mSCRIPT_DECLARE_STRUCT_C_METHOD(mImage, U32, getPixel, mImageGetPixel, 2, U32, x, U32, y);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mImage, setPixel, mImageSetPixel, 3, U32, x, U32, y, U32, color);
mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mImage, BOOL, save, mImageSave, 2, CHARP, path, CHARP, format);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mImage, _deinit, mImageDestroy, 0);
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mImage, save)
mSCRIPT_NO_DEFAULT,
mSCRIPT_CHARP("PNG")
mSCRIPT_DEFINE_DEFAULTS_END;
mSCRIPT_DEFINE_STRUCT(mImage)
mSCRIPT_DEFINE_CLASS_DOCSTRING(
"A single, static image."
)
mSCRIPT_DEFINE_STRUCT_DEINIT(mImage)
mSCRIPT_DEFINE_DOCSTRING("Save the image to a file. Currently, only `PNG` format is supported")
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, save)
mSCRIPT_DEFINE_DOCSTRING("Get the ARGB value of the pixel at a given coordinate")
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, getPixel)
mSCRIPT_DEFINE_DOCSTRING("Set the ARGB value of the pixel at a given coordinate")
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, setPixel)
mSCRIPT_DEFINE_DOCSTRING("The width of the image, in pixels")
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(mImage, U32, width)
mSCRIPT_DEFINE_DOCSTRING("The height of the image, in pixels")
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(mImage, U32, height)
mSCRIPT_DEFINE_END;
mSCRIPT_BIND_FUNCTION(mImageNew_Binding, W(mImage), _mImageNew, 2, U32, width, U32, height);
mSCRIPT_BIND_FUNCTION(mImageLoad_Binding, W(mImage), _mImageLoad, 1, CHARP, path);
void mScriptContextAttachImage(struct mScriptContext* context) {
mScriptContextExportNamespace(context, "image", (struct mScriptKVPair[]) {
mSCRIPT_KV_PAIR(new, &mImageNew_Binding),
mSCRIPT_KV_PAIR(load, &mImageLoad_Binding),
mSCRIPT_KV_SENTINEL
});
mScriptContextSetDocstring(context, "image", "Methods for creating struct::mImage instances");
mScriptContextSetDocstring(context, "image.new", "Create a new image with the given dimensions");
mScriptContextSetDocstring(context, "image.load", "Load an image from a path. Currently, only `PNG` format is supported");
}

111
src/script/test/image.c Normal file
View File

@ -0,0 +1,111 @@
/* Copyright (c) 2013-2023 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba/internal/script/lua.h>
#include <mgba/script.h>
#include "script/test.h"
#define SETUP_LUA \
struct mScriptContext context; \
mScriptContextInit(&context); \
struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \
mScriptContextAttachStdlib(&context); \
mScriptContextAttachImage(&context)
M_TEST_SUITE_SETUP(mScriptImage) {
if (mSCRIPT_ENGINE_LUA->init) {
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
}
return 0;
}
M_TEST_SUITE_TEARDOWN(mScriptImage) {
if (mSCRIPT_ENGINE_LUA->deinit) {
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
}
return 0;
}
M_TEST_DEFINE(members) {
SETUP_LUA;
TEST_PROGRAM("assert(image)");
TEST_PROGRAM("assert(image.new)");
TEST_PROGRAM("assert(image.load)");
TEST_PROGRAM("im = image.new(1, 1)");
TEST_PROGRAM("assert(im)");
TEST_PROGRAM("assert(im.width == 1)");
TEST_PROGRAM("assert(im.height == 1)");
TEST_PROGRAM("assert(im.save)");
TEST_PROGRAM("assert(im.save)");
TEST_PROGRAM("assert(im.getPixel)");
TEST_PROGRAM("assert(im.setPixel)");
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(zeroDim) {
SETUP_LUA;
TEST_PROGRAM("im = image.new(0, 0)");
TEST_PROGRAM("assert(not im)");
TEST_PROGRAM("im = image.new(1, 0)");
TEST_PROGRAM("assert(not im)");
TEST_PROGRAM("im = image.new(0, 1)");
TEST_PROGRAM("assert(not im)");
TEST_PROGRAM("im = image.new(1, 1)");
TEST_PROGRAM("assert(im)");
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(pixelColorDefault) {
SETUP_LUA;
TEST_PROGRAM("im = image.new(1, 1)");
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0)");
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(pixelColorRoundTrip) {
SETUP_LUA;
TEST_PROGRAM("im = image.new(1, 1)");
TEST_PROGRAM("im:setPixel(0, 0, 0xFF123456)");
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0xFF123456)");
mScriptContextDeinit(&context);
}
#ifdef USE_PNG
M_TEST_DEFINE(saveLoadRoundTrip) {
SETUP_LUA;
unlink("tmp.png");
TEST_PROGRAM("im = image.new(1, 1)");
TEST_PROGRAM("im:setPixel(0, 0, 0xFF123456)");
TEST_PROGRAM("assert(im:save('tmp.png'))");
TEST_PROGRAM("im = image.load('tmp.png')");
TEST_PROGRAM("assert(im)");
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0xFF123456)");
unlink("tmp.png");
mScriptContextDeinit(&context);
}
#endif
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptImage,
cmocka_unit_test(members),
cmocka_unit_test(zeroDim),
cmocka_unit_test(pixelColorDefault),
cmocka_unit_test(pixelColorRoundTrip),
#ifdef USE_PNG
cmocka_unit_test(saveLoadRoundTrip),
#endif
)