340 lines
13 KiB
C
340 lines
13 KiB
C
// gfx_playdate.c - Cell integration for Playdate Graphics API
|
|
// Wraps pd_api_gfx.h functions for JavaScript access
|
|
|
|
#include "cell.h"
|
|
#include "common.h"
|
|
#include <string.h>
|
|
|
|
// --- Helper: Convert JS value to LCDBitmap* ---
|
|
static LCDBitmap* js2bitmap(JSContext *js, JSValueConst val) {
|
|
int64_t ptr;
|
|
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
|
|
return (LCDBitmap*)(intptr_t)ptr;
|
|
}
|
|
|
|
static LCDBitmapTable* js2bitmaptable(JSContext *js, JSValueConst val) {
|
|
int64_t ptr;
|
|
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
|
|
return (LCDBitmapTable*)(intptr_t)ptr;
|
|
}
|
|
|
|
static LCDFont* js2font(JSContext *js, JSValueConst val) {
|
|
int64_t ptr;
|
|
if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
|
|
return (LCDFont*)(intptr_t)ptr;
|
|
}
|
|
|
|
// --- Drawing Functions ---
|
|
|
|
JSC_CCALL(gfx_clear,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDColor color = (LCDColor)js2number(js, argv[0]);
|
|
pd_gfx->clear(color);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setBackgroundColor,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDSolidColor color = (LCDSolidColor)(int)js2number(js, argv[0]);
|
|
pd_gfx->setBackgroundColor(color);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setDrawMode,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmapDrawMode mode = (LCDBitmapDrawMode)(int)js2number(js, argv[0]);
|
|
LCDBitmapDrawMode prev = pd_gfx->setDrawMode(mode);
|
|
return JS_NewInt32(js, prev);
|
|
)
|
|
|
|
JSC_CCALL(gfx_setDrawOffset,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setDrawOffset((int)js2number(js, argv[0]), (int)js2number(js, argv[1]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setClipRect,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setClipRect((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_clearClipRect,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->clearClipRect();
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setLineCapStyle,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setLineCapStyle((LCDLineCapStyle)(int)js2number(js, argv[0]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setFont,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setFont(js2font(js, argv[0]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setTextTracking,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setTextTracking((int)js2number(js, argv[0]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_getTextTracking,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
return JS_NewInt32(js, pd_gfx->getTextTracking());
|
|
)
|
|
|
|
JSC_CCALL(gfx_pushContext,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->pushContext(argc > 0 ? js2bitmap(js, argv[0]) : NULL);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_popContext,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->popContext();
|
|
return JS_NULL;
|
|
)
|
|
|
|
// --- Drawing Primitives ---
|
|
|
|
JSC_CCALL(gfx_drawBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
pd_gfx->drawBitmap(bmp, (int)js2number(js, argv[1]), (int)js2number(js, argv[2]),
|
|
argc > 3 ? (LCDBitmapFlip)(int)js2number(js, argv[3]) : kBitmapUnflipped);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_drawLine,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->drawLine((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(int)js2number(js, argv[4]), (LCDColor)js2number(js, argv[5]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_drawRect,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->drawRect((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(LCDColor)js2number(js, argv[4]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_fillRect,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->fillRect((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(LCDColor)js2number(js, argv[4]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_fillTriangle,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->fillTriangle((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(int)js2number(js, argv[4]), (int)js2number(js, argv[5]),
|
|
(LCDColor)js2number(js, argv[6]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_drawEllipse,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->drawEllipse((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(int)js2number(js, argv[4]), (float)js2number(js, argv[5]),
|
|
(float)js2number(js, argv[6]), (LCDColor)js2number(js, argv[7]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_fillEllipse,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->fillEllipse((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(int)js2number(js, argv[2]), (int)js2number(js, argv[3]),
|
|
(float)js2number(js, argv[4]), (float)js2number(js, argv[5]),
|
|
(LCDColor)js2number(js, argv[6]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_SCALL(gfx_drawText,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
int w = pd_gfx->drawText(str, strlen(str), kUTF8Encoding,
|
|
(int)js2number(js, argv[1]), (int)js2number(js, argv[2]));
|
|
ret = JS_NewInt32(js, w);
|
|
)
|
|
|
|
JSC_CCALL(gfx_drawScaledBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
pd_gfx->drawScaledBitmap(bmp, (int)js2number(js, argv[1]), (int)js2number(js, argv[2]),
|
|
(float)js2number(js, argv[3]), (float)js2number(js, argv[4]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_drawRotatedBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
pd_gfx->drawRotatedBitmap(bmp, (int)js2number(js, argv[1]), (int)js2number(js, argv[2]),
|
|
(float)js2number(js, argv[3]), (float)js2number(js, argv[4]),
|
|
(float)js2number(js, argv[5]), (float)js2number(js, argv[6]),
|
|
(float)js2number(js, argv[7]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_setPixel,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->setPixel((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
(LCDColor)js2number(js, argv[2]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
// --- Bitmap Functions ---
|
|
|
|
JSC_CCALL(gfx_newBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = pd_gfx->newBitmap((int)js2number(js, argv[0]), (int)js2number(js, argv[1]),
|
|
argc > 2 ? (LCDColor)js2number(js, argv[2]) : kColorWhite);
|
|
if (!bmp) return JS_ThrowInternalError(js, "failed to create bitmap");
|
|
return JS_NewInt64(js, (int64_t)(intptr_t)bmp);
|
|
)
|
|
|
|
JSC_CCALL(gfx_freeBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (bmp) pd_gfx->freeBitmap(bmp);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_SCALL(gfx_loadBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
const char *err = NULL;
|
|
LCDBitmap *bmp = pd_gfx->loadBitmap(str, &err);
|
|
if (!bmp) ret = JS_ThrowInternalError(js, "loadBitmap: %s", err ? err : "unknown");
|
|
else ret = JS_NewInt64(js, (int64_t)(intptr_t)bmp);
|
|
)
|
|
|
|
JSC_CCALL(gfx_copyBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
LCDBitmap *copy = pd_gfx->copyBitmap(bmp);
|
|
return copy ? JS_NewInt64(js, (int64_t)(intptr_t)copy) : JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_clearBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
pd_gfx->clearBitmap(bmp, argc > 1 ? (LCDColor)js2number(js, argv[1]) : kColorWhite);
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_getBitmapData,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = js2bitmap(js, argv[0]);
|
|
if (!bmp) return JS_ThrowTypeError(js, "invalid bitmap");
|
|
int w, h, rb; uint8_t *mask, *data;
|
|
pd_gfx->getBitmapData(bmp, &w, &h, &rb, &mask, &data);
|
|
JSValue obj = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, w));
|
|
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, h));
|
|
JS_SetPropertyStr(js, obj, "rowbytes", JS_NewInt32(js, rb));
|
|
return obj;
|
|
)
|
|
|
|
// --- Font Functions ---
|
|
|
|
JSC_SCALL(gfx_loadFont,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
const char *err = NULL;
|
|
LCDFont *font = pd_gfx->loadFont(str, &err);
|
|
if (!font) ret = JS_ThrowInternalError(js, "loadFont: %s", err ? err : "unknown");
|
|
else ret = JS_NewInt64(js, (int64_t)(intptr_t)font);
|
|
)
|
|
|
|
JSC_CCALL(gfx_getFontHeight,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDFont *font = js2font(js, argv[0]);
|
|
return JS_NewInt32(js, pd_gfx->getFontHeight(font));
|
|
)
|
|
|
|
JSC_SCALL(gfx_getTextWidth,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDFont *font = argc > 1 ? js2font(js, argv[1]) : NULL;
|
|
int tracking = argc > 2 ? (int)js2number(js, argv[2]) : 0;
|
|
ret = JS_NewInt32(js, pd_gfx->getTextWidth(font, str, strlen(str), kUTF8Encoding, tracking));
|
|
)
|
|
|
|
// --- Framebuffer ---
|
|
|
|
JSC_CCALL(gfx_display,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->display();
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_markUpdatedRows,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
pd_gfx->markUpdatedRows((int)js2number(js, argv[0]), (int)js2number(js, argv[1]));
|
|
return JS_NULL;
|
|
)
|
|
|
|
JSC_CCALL(gfx_copyFrameBufferBitmap,
|
|
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
|
|
LCDBitmap *bmp = pd_gfx->copyFrameBufferBitmap();
|
|
return bmp ? JS_NewInt64(js, (int64_t)(intptr_t)bmp) : JS_NULL;
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_gfx_funcs[] = {
|
|
MIST_FUNC_DEF(gfx, clear, 1),
|
|
MIST_FUNC_DEF(gfx, setBackgroundColor, 1),
|
|
MIST_FUNC_DEF(gfx, setDrawMode, 1),
|
|
MIST_FUNC_DEF(gfx, setDrawOffset, 2),
|
|
MIST_FUNC_DEF(gfx, setClipRect, 4),
|
|
MIST_FUNC_DEF(gfx, clearClipRect, 0),
|
|
MIST_FUNC_DEF(gfx, setLineCapStyle, 1),
|
|
MIST_FUNC_DEF(gfx, setFont, 1),
|
|
MIST_FUNC_DEF(gfx, setTextTracking, 1),
|
|
MIST_FUNC_DEF(gfx, getTextTracking, 0),
|
|
MIST_FUNC_DEF(gfx, pushContext, 1),
|
|
MIST_FUNC_DEF(gfx, popContext, 0),
|
|
MIST_FUNC_DEF(gfx, drawBitmap, 4),
|
|
MIST_FUNC_DEF(gfx, drawLine, 6),
|
|
MIST_FUNC_DEF(gfx, drawRect, 5),
|
|
MIST_FUNC_DEF(gfx, fillRect, 5),
|
|
MIST_FUNC_DEF(gfx, fillTriangle, 7),
|
|
MIST_FUNC_DEF(gfx, drawEllipse, 8),
|
|
MIST_FUNC_DEF(gfx, fillEllipse, 7),
|
|
MIST_FUNC_DEF(gfx, drawText, 3),
|
|
MIST_FUNC_DEF(gfx, drawScaledBitmap, 5),
|
|
MIST_FUNC_DEF(gfx, drawRotatedBitmap, 8),
|
|
MIST_FUNC_DEF(gfx, setPixel, 3),
|
|
MIST_FUNC_DEF(gfx, newBitmap, 3),
|
|
MIST_FUNC_DEF(gfx, freeBitmap, 1),
|
|
MIST_FUNC_DEF(gfx, loadBitmap, 1),
|
|
MIST_FUNC_DEF(gfx, copyBitmap, 1),
|
|
MIST_FUNC_DEF(gfx, clearBitmap, 2),
|
|
MIST_FUNC_DEF(gfx, getBitmapData, 1),
|
|
MIST_FUNC_DEF(gfx, loadFont, 1),
|
|
MIST_FUNC_DEF(gfx, getFontHeight, 1),
|
|
MIST_FUNC_DEF(gfx, getTextWidth, 3),
|
|
MIST_FUNC_DEF(gfx, display, 0),
|
|
MIST_FUNC_DEF(gfx, markUpdatedRows, 2),
|
|
MIST_FUNC_DEF(gfx, copyFrameBufferBitmap, 0),
|
|
};
|
|
|
|
JSValue js_gfx_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, mod, js_gfx_funcs, countof(js_gfx_funcs));
|
|
return mod;
|
|
}
|