Files
cell/playdate/gfx_playdate.c
2025-12-10 15:01:20 -06:00

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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
JSC_CCALL(gfx_clearClipRect,
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
pd_gfx->clearClipRect();
return JS_UNDEFINED;
)
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_UNDEFINED;
)
JSC_CCALL(gfx_setFont,
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
pd_gfx->setFont(js2font(js, argv[0]));
return JS_UNDEFINED;
)
JSC_CCALL(gfx_setTextTracking,
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
pd_gfx->setTextTracking((int)js2number(js, argv[0]));
return JS_UNDEFINED;
)
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_UNDEFINED;
)
JSC_CCALL(gfx_popContext,
if (!pd_gfx) return JS_ThrowInternalError(js, "graphics not initialized");
pd_gfx->popContext();
return JS_UNDEFINED;
)
// --- 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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
// --- 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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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;
}