updated to simulate setup with multiple fonts

This commit is contained in:
Matthew Jennings 2025-05-05 20:33:55 +03:00
parent 749d1bb7de
commit b06f5ec400
2 changed files with 33 additions and 37 deletions

View file

@ -1,5 +1,6 @@
#include "pd_api.h" #include "pd_api.h"
#include "pd_api/pd_api_gfx.h"
#define CLAY_IMPLEMENTATION #define CLAY_IMPLEMENTATION
#include "../../clay.h" #include "../../clay.h"
@ -7,28 +8,27 @@
#include "clay-video-demo-playdate.c" #include "clay-video-demo-playdate.c"
static int update(void *userdata); static int update(void *userdata);
const char *fontpath = "/System/Fonts/Asheville-Sans-14-Bold.pft"; const char *fontPath = "/System/Fonts/Asheville-Sans-14-Bold.pft";
LCDFont *font = NULL;
void HandleClayErrors(Clay_ErrorData errorData) {} void HandleClayErrors(Clay_ErrorData errorData) {}
struct TextUserData { struct TextUserData {
LCDFont *font; LCDFont *font[1];
PlaydateAPI *pd; PlaydateAPI *pd;
}; };
static struct TextUserData testUserData = {.font = NULL, .pd = NULL}; static struct TextUserData textUserData = {.font = {NULL, NULL}, .pd = NULL};
static Clay_Dimensions PlayDate_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) { static Clay_Dimensions PlayDate_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
struct TextUserData *textUserData = userData; struct TextUserData *textUserData = userData;
int width = textUserData->pd->graphics->getTextWidth( int width = textUserData->pd->graphics->getTextWidth(
textUserData->font, textUserData->font[config->fontId],
text.chars, text.chars,
utf8_count_codepoints(text.chars, text.length), utf8_count_codepoints(text.chars, text.length),
kUTF8Encoding, kUTF8Encoding,
0 0
); );
int height = textUserData->pd->graphics->getFontHeight(textUserData->font); int height = textUserData->pd->graphics->getFontHeight(textUserData->font[config->fontId]);
return (Clay_Dimensions){ return (Clay_Dimensions){
.width = (float)width, .width = (float)width,
.height = (float)height, .height = (float)height,
@ -42,14 +42,13 @@ int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t eventArg)
{ {
if (event == kEventInit) { if (event == kEventInit) {
const char *err; const char *err;
font = pd->graphics->loadFont(fontpath, &err); textUserData.font[0] = pd->graphics->loadFont(fontPath, &err);
if (font == NULL) { if (textUserData.font[0] == NULL) {
pd->system->error("%s:%i Couldn't load font %s: %s", __FILE__, __LINE__, fontpath, err); pd->system->error("%s:%i Couldn't load font %s: %s", __FILE__, __LINE__, fontPath, err);
} }
testUserData.pd = pd; textUserData.pd = pd;
testUserData.font = font;
pd->system->setUpdateCallback(update, pd); pd->system->setUpdateCallback(update, pd);
uint64_t totalMemorySize = Clay_MinMemorySize(); uint64_t totalMemorySize = Clay_MinMemorySize();
@ -59,7 +58,7 @@ int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t eventArg)
(Clay_Dimensions){(float)pd->display->getWidth(), (float)pd->display->getHeight()}, (Clay_Dimensions){(float)pd->display->getWidth(), (float)pd->display->getHeight()},
(Clay_ErrorHandler){HandleClayErrors} (Clay_ErrorHandler){HandleClayErrors}
); );
Clay_SetMeasureTextFunction(PlayDate_MeasureText, &testUserData); Clay_SetMeasureTextFunction(PlayDate_MeasureText, &textUserData);
ClayVideoDemo_Initialize(); ClayVideoDemo_Initialize();
} }
@ -81,7 +80,6 @@ static int update(void *userdata) {
} }
pd->graphics->clear(kColorWhite); pd->graphics->clear(kColorWhite);
pd->graphics->setFont(font);
// A bit hacky, setting the cursor on to the document view so it can be // A bit hacky, setting the cursor on to the document view so it can be
// scrolled.. // scrolled..
@ -97,7 +95,7 @@ static int update(void *userdata) {
); );
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(selectedDocumentIndex); Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(selectedDocumentIndex);
Clay_Playdate_Render(pd, renderCommands, font); Clay_Playdate_Render(pd, renderCommands, textUserData.font);
return 1; return 1;
} }

View file

@ -1,16 +1,15 @@
#include "../../clay.h"
#include "pd_api.h" #include "pd_api.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #include "../../clay.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct Rect { struct Clay_Playdate_Rect {
float x; float x;
float y; float y;
float w; float w;
float h; float h;
}; };
// Playdate drawText function expects the number of codepoints to draw, not byte length
static size_t utf8_count_codepoints(const char *str, size_t byte_len) { static size_t utf8_count_codepoints(const char *str, size_t byte_len) {
size_t count = 0; size_t count = 0;
size_t i = 0; size_t i = 0;
@ -27,38 +26,37 @@ static size_t utf8_count_codepoints(const char *str, size_t byte_len) {
return count; return count;
} }
// As the playdate can only display black and white, we need to resolve Clay_color to either black or white
// for both color and draw mode.
// TODO: Convert to grayscale and then map the grayscale value to different dithering patterns
static LCDColor clayColorToLCDColor(Clay_Color color) { static LCDColor clayColorToLCDColor(Clay_Color color) {
// Convert the RGB to grayscale using the standard luminance formula if (color.r > 0 || color.g > 0 || color.b > 0) {
unsigned char grayscale_value = (unsigned char)(0.299 * color.r + 0.587 * color.g + 0.114 * color.b);
if (grayscale_value > 128) {
return kColorWhite; return kColorWhite;
} }
return kColorBlack; return kColorBlack;
} }
static LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) { static LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) {
if (color.r > 0 || color.g > 0 || color.b > 0) {
if (color.r == 255 && color.g == 255 && color.b == 255) {
return kDrawModeFillWhite; return kDrawModeFillWhite;
} }
return kDrawModeCopy; return kDrawModeCopy;
} }
static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray renderCommands, LCDFont *fonts) { static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray renderCommands, LCDFont **fonts) {
for (uint32_t i = 0; i < renderCommands.length; i++) { for (uint32_t i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i); Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
Clay_BoundingBox boundingBox = renderCommand->boundingBox; Clay_BoundingBox boundingBox = renderCommand->boundingBox;
switch (renderCommand->commandType) { switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle; Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
struct Rect rect = (struct Rect){ struct Clay_Playdate_Rect rect = (struct Clay_Playdate_Rect){
.x = boundingBox.x, .x = boundingBox.x,
.y = boundingBox.y, .y = boundingBox.y,
.w = boundingBox.width, .w = boundingBox.width,
.h = boundingBox.height, .h = boundingBox.height,
}; };
// TODO: support different radius for each corner like clay API allows
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 0) {
pd->graphics->fillRoundRect( pd->graphics->fillRoundRect(
rect.x, rect.y, rect.w, rect.h, rect.x, rect.y, rect.w, rect.h,
@ -66,18 +64,18 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
clayColorToLCDColor(config->backgroundColor) clayColorToLCDColor(config->backgroundColor)
); );
} else { } else {
pd->graphics->fillRect(rect.x, rect.y, rect.w, rect.h, pd->graphics->fillRect(
clayColorToLCDColor(config->backgroundColor)); rect.x, rect.y, rect.w, rect.h,
clayColorToLCDColor(config->backgroundColor)
);
} }
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_TEXT: { case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &renderCommand->renderData.text; Clay_TextRenderData *config = &renderCommand->renderData.text;
// LCDFont *font = fonts[config->fontId]; LCDFont *font = fonts[config->fontId];
// TODO: support loading more than 1 font and use the fonts that clay pd->graphics->setFont(font);
// layout has.. struct Clay_Playdate_Rect destination = (struct Clay_Playdate_Rect){
LCDFont *font = fonts;
struct Rect destination = (struct Rect){
.x = boundingBox.x, .x = boundingBox.x,
.y = boundingBox.y, .y = boundingBox.y,
.w = boundingBox.width, .w = boundingBox.width,
@ -98,7 +96,7 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: { case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
struct Rect currentClippingRectangle = (struct Rect){ struct Clay_Playdate_Rect currentClippingRectangle = (struct Clay_Playdate_Rect){
.x = boundingBox.x, .x = boundingBox.x,
.y = boundingBox.y, .y = boundingBox.y,
.w = boundingBox.width, .w = boundingBox.width,
@ -117,7 +115,7 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
case CLAY_RENDER_COMMAND_TYPE_IMAGE: { case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
Clay_ImageRenderData *config = &renderCommand->renderData.image; Clay_ImageRenderData *config = &renderCommand->renderData.image;
LCDBitmap *texture = config->imageData; LCDBitmap *texture = config->imageData;
struct Rect destination = (struct Rect){ struct Clay_Playdate_Rect destination = (struct Clay_Playdate_Rect){
.x = boundingBox.x, .x = boundingBox.x,
.y = boundingBox.y, .y = boundingBox.y,
.w = boundingBox.width, .w = boundingBox.width,
@ -128,7 +126,7 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
} }
case CLAY_RENDER_COMMAND_TYPE_BORDER: { case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &renderCommand->renderData.border; Clay_BorderRenderData *config = &renderCommand->renderData.border;
struct Rect rect = (struct Rect){ struct Clay_Playdate_Rect rect = (struct Clay_Playdate_Rect){
.x = boundingBox.x, .x = boundingBox.x,
.y = boundingBox.y, .y = boundingBox.y,
.w = boundingBox.width, .w = boundingBox.width,