implemented image scaling and variabel corner radius rendering

This commit is contained in:
Matthew Jennings 2025-05-10 14:34:04 +03:00
parent b99cf3603a
commit fb092e59f2
3 changed files with 188 additions and 65 deletions

View file

@ -116,7 +116,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
CLAY({
.id = CLAY_ID("Sidebar"),
.border = contentBorders,
.cornerRadius = 4,
.cornerRadius = CLAY_CORNER_RADIUS(4),
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.padding = CLAY_PADDING_ALL(8),
@ -138,7 +138,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
CLAY({
.layout = sidebarButtonLayout,
.backgroundColor = COLOR_BLACK,
.cornerRadius = CLAY_CORNER_RADIUS(4)
.cornerRadius = { .topLeft = 10, .topRight = 60, .bottomLeft = 30, .bottomRight = 60 }
}) {
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY,
@ -149,7 +149,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
CLAY({
.layout = sidebarButtonLayout,
.backgroundColor = (Clay_Color) { 0, 0, 0, Clay_Hovered() ? 120 : 0 },
.cornerRadius = CLAY_CORNER_RADIUS(4),
.cornerRadius = { .topLeft = 10, .topRight = 60, .bottomLeft = 30, .bottomRight = 60 },
.border = contentBorders
}) {
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({

View file

@ -1,6 +1,4 @@
#include "pd_api.h"
#include "pd_api/pd_api_gfx.h"
#define CLAY_IMPLEMENTATION
#include "../../clay.h"
@ -17,7 +15,7 @@ struct TextUserData {
PlaydateAPI *pd;
};
static struct TextUserData textUserData = {.font = {NULL, NULL}, .pd = NULL};
static struct TextUserData textUserData = {.font = {NULL}, .pd = NULL};
static Clay_Dimensions PlayDate_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
struct TextUserData *textUserData = userData;

View file

@ -1,7 +1,5 @@
#include "pd_api.h"
#include "../../clay.h"
struct Clay_Playdate_Rect {
float x;
float y;
@ -43,6 +41,18 @@ static LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) {
return kDrawModeCopy;
}
static float clampCornerRadius(float yAxisSize, float radius) {
if (radius < 1.0f) {
return 0.0f;
}
if (radius > yAxisSize / 2) {
return yAxisSize / 2;
}
// Trying to draw a 2x2 ellipse seems to result in just a dot, so if
// there is a corner radius at minimum it must be 2
return radius + 1;
}
static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray renderCommands, LCDFont **fonts) {
for (uint32_t i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
@ -50,37 +60,93 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
struct Clay_Playdate_Rect rect = (struct Clay_Playdate_Rect){
.x = boundingBox.x,
.y = boundingBox.y,
.w = boundingBox.width,
.h = boundingBox.height,
};
// TODO: support different radius for each corner like clay API allows
if (config->cornerRadius.topLeft > 0) {
pd->graphics->fillRoundRect(
rect.x, rect.y, rect.w, rect.h,
config->cornerRadius.topLeft,
clayColorToLCDColor(config->backgroundColor)
);
} else {
pd->graphics->fillRect(
rect.x, rect.y, rect.w, rect.h,
clayColorToLCDColor(config->backgroundColor)
);
}
float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);
float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);
float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);
float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);
pd->graphics->fillEllipse(
boundingBox.x, boundingBox.y,
radiusTl * 2, radiusTl * 2,
-90.0f, 0.0f,
clayColorToLCDColor(config->backgroundColor)
);
pd->graphics->fillEllipse(
boundingBox.x + boundingBox.width - radiusTr * 2, boundingBox.y,
radiusTr * 2, radiusTr * 2,
0.0f, 90.0f,
clayColorToLCDColor(config->backgroundColor)
);
pd->graphics->fillEllipse(
boundingBox.x + boundingBox.width - radiusBr * 2,
boundingBox.y + boundingBox.height - radiusBr * 2,
radiusBr * 2, radiusBr * 2,
90.0f, 180.0f,
clayColorToLCDColor(config->backgroundColor)
);
pd->graphics->fillEllipse(
boundingBox.x,
boundingBox.y + boundingBox.height - radiusBl * 2,
radiusBl * 2, radiusBl * 2,
180.0f, 270.0f,
clayColorToLCDColor(config->backgroundColor)
);
// Top chunk
pd->graphics->fillRect(
boundingBox.x + radiusTl, boundingBox.y,
boundingBox.width - radiusTl - radiusTr,
CLAY__MAX(radiusTl, radiusTr),
clayColorToLCDColor(config->backgroundColor)
);
// bottom chunk
int bottomChunkHeight = CLAY__MAX(radiusBl, radiusBr);
pd->graphics->fillRect(
boundingBox.x + radiusBl, boundingBox.y + boundingBox.height - bottomChunkHeight,
boundingBox.width - radiusBl - radiusBr,
bottomChunkHeight,
clayColorToLCDColor(config->backgroundColor)
);
// Middle chunk
int middleChunkHeight = boundingBox.height - CLAY__MIN(radiusBr, radiusBl) - CLAY__MIN(radiusTr, radiusTl);
pd->graphics->fillRect(
boundingBox.x + CLAY__MIN(radiusTl, radiusBl), boundingBox.y + CLAY__MIN(radiusTr, radiusTl),
boundingBox.width - radiusBl - radiusBr,
middleChunkHeight,
clayColorToLCDColor(config->backgroundColor)
);
// Left chunk
int leftChunkHeight = boundingBox.height - radiusTl - radiusBl;
int leftChunkWidth = CLAY__MAX(radiusTl, radiusBl);
pd->graphics->fillRect(
boundingBox.x, boundingBox.y + radiusTl,
leftChunkWidth,
leftChunkHeight,
clayColorToLCDColor(config->backgroundColor)
);
// Right chunk
int rightChunkHeight = boundingBox.height - radiusTr - radiusBr;
int rightChunkWidth = CLAY__MAX(radiusTr, radiusBr);
pd->graphics->fillRect(
boundingBox.x + boundingBox.width - rightChunkWidth, boundingBox.y + radiusTr,
rightChunkWidth,
rightChunkHeight,
clayColorToLCDColor(config->backgroundColor)
);
break;
}
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &renderCommand->renderData.text;
LCDFont *font = fonts[config->fontId];
pd->graphics->setFont(font);
struct Clay_Playdate_Rect destination = (struct Clay_Playdate_Rect){
.x = boundingBox.x,
.y = boundingBox.y,
.w = boundingBox.width,
.h = boundingBox.height,
};
pd->graphics->setDrawMode(clayColorToDrawMode(config->textColor));
pd->graphics->drawText(
renderCommand->renderData.text.stringContents.chars,
@ -89,22 +155,16 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
renderCommand->renderData.text.stringContents.length
),
kUTF8Encoding,
destination.x,
destination.y
boundingBox.x,
boundingBox.y
);
pd->graphics->setDrawMode(kDrawModeCopy);
break;
}
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
struct Clay_Playdate_Rect currentClippingRectangle = (struct Clay_Playdate_Rect){
.x = boundingBox.x,
.y = boundingBox.y,
.w = boundingBox.width,
.h = boundingBox.height,
};
pd->graphics->setClipRect(
currentClippingRectangle.x, currentClippingRectangle.y,
currentClippingRectangle.w, currentClippingRectangle.h
boundingBox.x,boundingBox.y,
boundingBox.width, boundingBox.height
);
break;
}
@ -115,36 +175,101 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
Clay_ImageRenderData *config = &renderCommand->renderData.image;
LCDBitmap *texture = config->imageData;
struct Clay_Playdate_Rect destination = (struct Clay_Playdate_Rect){
.x = boundingBox.x,
.y = boundingBox.y,
.w = boundingBox.width,
.h = boundingBox.height,
};
pd->graphics->drawBitmap(texture, destination.x, destination.y, kBitmapUnflipped);
int texWidth;
int texHeight;
pd->graphics->getBitmapData(texture, &texWidth, &texHeight, NULL, NULL, NULL);
if (texWidth != boundingBox.width || texHeight != boundingBox.height) {
pd->graphics->drawScaledBitmap(
texture,
boundingBox.x, boundingBox.y,
boundingBox.width / texWidth,
boundingBox.height / texHeight
);
} else {
pd->graphics->drawBitmap(texture, boundingBox.x, boundingBox.y, kBitmapUnflipped);
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &renderCommand->renderData.border;
struct Clay_Playdate_Rect rect = (struct Clay_Playdate_Rect){
.x = boundingBox.x,
.y = boundingBox.y,
.w = boundingBox.width,
.h = boundingBox.height,
};
// TODO: properly support the different border thickness and radius
// instead of just using topLeft corner /top thickness as a global setting
if (config->cornerRadius.topLeft > 0) {
pd->graphics->drawRoundRect(
rect.x, rect.y, rect.w, rect.h,
config->cornerRadius.topLeft,
float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);
float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);
float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);
float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);
if (config->width.top > 0) {
pd->graphics->drawEllipse(
boundingBox.x, boundingBox.y,
radiusTl * 2, radiusTl * 2,
config->width.top,
-90.0f, 0.0f,
clayColorToLCDColor(config->color)
);
pd->graphics->drawLine(
boundingBox.x + radiusTl, boundingBox.y,
boundingBox.x + boundingBox.width - radiusTr - config->width.right, boundingBox.y,
config->width.top,
clayColorToLCDColor(config->color)
);
} else {
pd->graphics->drawRect(
rect.x, rect.y, rect.w, rect.h,
pd->graphics->drawEllipse(
boundingBox.x + boundingBox.width - radiusTr * 2, boundingBox.y,
radiusTr * 2, radiusTr * 2,
config->width.top,
0.0f, 90.0f,
clayColorToLCDColor(config->color)
);
}
if (config->width.right > 0 && radiusTr + radiusBr <= boundingBox.height) {
pd->graphics->drawLine(
boundingBox.x + boundingBox.width - config->width.right,
boundingBox.y + radiusTr,
boundingBox.x + boundingBox.width - config->width.right,
boundingBox.y + boundingBox.height - radiusBr - config->width.bottom,
config->width.right,
clayColorToLCDColor(config->color)
);
}
if (config->width.bottom > 0) {
pd->graphics->drawEllipse(
boundingBox.x + boundingBox.width - radiusBr * 2,
boundingBox.y + boundingBox.height - radiusBr * 2,
radiusBr * 2, radiusBr * 2,
config->width.bottom,
90.0f, 180.0f,
clayColorToLCDColor(config->color)
);
pd->graphics->drawLine(
boundingBox.x + boundingBox.width - radiusBr - config->width.right,
boundingBox.y + boundingBox.height - config->width.bottom,
boundingBox.x + radiusBl,
boundingBox.y + boundingBox.height - config->width.bottom,
config->width.bottom,
clayColorToLCDColor(config->color)
);
pd->graphics->drawEllipse(
boundingBox.x,
boundingBox.y + boundingBox.height - radiusBl * 2,
radiusBl * 2, radiusBl * 2,
config->width.bottom,
180.0f, 270.0f,
clayColorToLCDColor(config->color)
);
}
if (config->width.left > 0 && radiusBl + radiusTl < boundingBox.height) {
pd->graphics->drawLine(
boundingBox.x, boundingBox.y + boundingBox.height - radiusBl - config->width.bottom,
boundingBox.x, boundingBox.y + radiusTl,
config->width.left,
clayColorToLCDColor(config->color)
);
}