// 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 // --- 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; }