mirror of
https://github.com/nicbarker/clay.git
synced 2025-09-18 20:46:17 +00:00
implemented image scaling and variabel corner radius rendering
This commit is contained in:
parent
b99cf3603a
commit
fb092e59f2
|
@ -116,7 +116,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
|
||||||
CLAY({
|
CLAY({
|
||||||
.id = CLAY_ID("Sidebar"),
|
.id = CLAY_ID("Sidebar"),
|
||||||
.border = contentBorders,
|
.border = contentBorders,
|
||||||
.cornerRadius = 4,
|
.cornerRadius = CLAY_CORNER_RADIUS(4),
|
||||||
.layout = {
|
.layout = {
|
||||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
.padding = CLAY_PADDING_ALL(8),
|
.padding = CLAY_PADDING_ALL(8),
|
||||||
|
@ -138,7 +138,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
|
||||||
CLAY({
|
CLAY({
|
||||||
.layout = sidebarButtonLayout,
|
.layout = sidebarButtonLayout,
|
||||||
.backgroundColor = COLOR_BLACK,
|
.backgroundColor = COLOR_BLACK,
|
||||||
.cornerRadius = CLAY_CORNER_RADIUS(4)
|
.cornerRadius = { .topLeft = 10, .topRight = 60, .bottomLeft = 30, .bottomRight = 60 }
|
||||||
}) {
|
}) {
|
||||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||||
.fontId = FONT_ID_BODY,
|
.fontId = FONT_ID_BODY,
|
||||||
|
@ -149,7 +149,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(int selectedDocumentIndex) {
|
||||||
CLAY({
|
CLAY({
|
||||||
.layout = sidebarButtonLayout,
|
.layout = sidebarButtonLayout,
|
||||||
.backgroundColor = (Clay_Color) { 0, 0, 0, Clay_Hovered() ? 120 : 0 },
|
.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
|
.border = contentBorders
|
||||||
}) {
|
}) {
|
||||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
#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"
|
||||||
|
|
||||||
|
@ -17,7 +15,7 @@ struct TextUserData {
|
||||||
PlaydateAPI *pd;
|
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) {
|
static Clay_Dimensions PlayDate_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
|
||||||
struct TextUserData *textUserData = userData;
|
struct TextUserData *textUserData = userData;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "pd_api.h"
|
#include "pd_api.h"
|
||||||
|
|
||||||
#include "../../clay.h"
|
|
||||||
|
|
||||||
struct Clay_Playdate_Rect {
|
struct Clay_Playdate_Rect {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
|
@ -43,6 +41,18 @@ static LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) {
|
||||||
return kDrawModeCopy;
|
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) {
|
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);
|
||||||
|
@ -50,37 +60,93 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
|
||||||
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 Clay_Playdate_Rect rect = (struct Clay_Playdate_Rect){
|
|
||||||
.x = boundingBox.x,
|
float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);
|
||||||
.y = boundingBox.y,
|
float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);
|
||||||
.w = boundingBox.width,
|
float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);
|
||||||
.h = boundingBox.height,
|
float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);
|
||||||
};
|
|
||||||
// TODO: support different radius for each corner like clay API allows
|
pd->graphics->fillEllipse(
|
||||||
if (config->cornerRadius.topLeft > 0) {
|
boundingBox.x, boundingBox.y,
|
||||||
pd->graphics->fillRoundRect(
|
radiusTl * 2, radiusTl * 2,
|
||||||
rect.x, rect.y, rect.w, rect.h,
|
-90.0f, 0.0f,
|
||||||
config->cornerRadius.topLeft,
|
clayColorToLCDColor(config->backgroundColor)
|
||||||
clayColorToLCDColor(config->backgroundColor)
|
);
|
||||||
);
|
|
||||||
} else {
|
pd->graphics->fillEllipse(
|
||||||
pd->graphics->fillRect(
|
boundingBox.x + boundingBox.width - radiusTr * 2, boundingBox.y,
|
||||||
rect.x, rect.y, rect.w, rect.h,
|
radiusTr * 2, radiusTr * 2,
|
||||||
clayColorToLCDColor(config->backgroundColor)
|
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;
|
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];
|
||||||
pd->graphics->setFont(font);
|
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->setDrawMode(clayColorToDrawMode(config->textColor));
|
||||||
pd->graphics->drawText(
|
pd->graphics->drawText(
|
||||||
renderCommand->renderData.text.stringContents.chars,
|
renderCommand->renderData.text.stringContents.chars,
|
||||||
|
@ -89,22 +155,16 @@ static void Clay_Playdate_Render(PlaydateAPI *pd, Clay_RenderCommandArray render
|
||||||
renderCommand->renderData.text.stringContents.length
|
renderCommand->renderData.text.stringContents.length
|
||||||
),
|
),
|
||||||
kUTF8Encoding,
|
kUTF8Encoding,
|
||||||
destination.x,
|
boundingBox.x,
|
||||||
destination.y
|
boundingBox.y
|
||||||
);
|
);
|
||||||
pd->graphics->setDrawMode(kDrawModeCopy);
|
pd->graphics->setDrawMode(kDrawModeCopy);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
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(
|
pd->graphics->setClipRect(
|
||||||
currentClippingRectangle.x, currentClippingRectangle.y,
|
boundingBox.x,boundingBox.y,
|
||||||
currentClippingRectangle.w, currentClippingRectangle.h
|
boundingBox.width, boundingBox.height
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -115,36 +175,101 @@ 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 Clay_Playdate_Rect destination = (struct Clay_Playdate_Rect){
|
int texWidth;
|
||||||
.x = boundingBox.x,
|
int texHeight;
|
||||||
.y = boundingBox.y,
|
pd->graphics->getBitmapData(texture, &texWidth, &texHeight, NULL, NULL, NULL);
|
||||||
.w = boundingBox.width,
|
if (texWidth != boundingBox.width || texHeight != boundingBox.height) {
|
||||||
.h = boundingBox.height,
|
pd->graphics->drawScaledBitmap(
|
||||||
};
|
texture,
|
||||||
pd->graphics->drawBitmap(texture, destination.x, destination.y, kBitmapUnflipped);
|
boundingBox.x, boundingBox.y,
|
||||||
|
boundingBox.width / texWidth,
|
||||||
|
boundingBox.height / texHeight
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
pd->graphics->drawBitmap(texture, boundingBox.x, boundingBox.y, kBitmapUnflipped);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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 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
|
float radiusTl = clampCornerRadius(boundingBox.height, config->cornerRadius.topLeft);
|
||||||
// instead of just using topLeft corner /top thickness as a global setting
|
float radiusTr = clampCornerRadius(boundingBox.height, config->cornerRadius.topRight);
|
||||||
if (config->cornerRadius.topLeft > 0) {
|
float radiusBl = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomLeft);
|
||||||
pd->graphics->drawRoundRect(
|
float radiusBr = clampCornerRadius(boundingBox.height, config->cornerRadius.bottomRight);
|
||||||
rect.x, rect.y, rect.w, rect.h,
|
|
||||||
config->cornerRadius.topLeft,
|
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,
|
config->width.top,
|
||||||
clayColorToLCDColor(config->color)
|
clayColorToLCDColor(config->color)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
pd->graphics->drawRect(
|
pd->graphics->drawEllipse(
|
||||||
rect.x, rect.y, rect.w, rect.h,
|
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)
|
clayColorToLCDColor(config->color)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue