mirror of
				https://github.com/nicbarker/clay.git
				synced 2025-11-04 00:26:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			274 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "pd_api.h"
 | 
						|
#include "../../clay.h"
 | 
						|
 | 
						|
// Playdate drawText function expects the number of codepoints to draw, not byte length
 | 
						|
static size_t Clay_Playdate_CountUtf8Codepoints(const char *str, size_t byteLen) {
 | 
						|
    size_t count = 0;
 | 
						|
    size_t i = 0;
 | 
						|
    while (i < byteLen) {
 | 
						|
        uint8_t c = (uint8_t)str[i];
 | 
						|
        if ((c & 0xC0) != 0x80) {
 | 
						|
            count++;
 | 
						|
        }
 | 
						|
        i++;
 | 
						|
    }
 | 
						|
    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.
 | 
						|
static LCDColor clayColorToLCDColor(Clay_Color color) {
 | 
						|
    if (color.r > 0 || color.g > 0 || color.b > 0) {
 | 
						|
        return kColorWhite;
 | 
						|
    }
 | 
						|
    return kColorBlack;
 | 
						|
}
 | 
						|
 | 
						|
static LCDBitmapDrawMode clayColorToDrawMode(Clay_Color color) {
 | 
						|
    if (color.r > 0 || color.g > 0 || color.b > 0) {
 | 
						|
        return kDrawModeFillWhite;
 | 
						|
    }
 | 
						|
    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 CLAY__MAX(2, radius);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
        Clay_BoundingBox boundingBox = renderCommand->boundingBox;
 | 
						|
 | 
						|
        switch (renderCommand->commandType) {
 | 
						|
            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
 | 
						|
                Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
 | 
						|
 | 
						|
                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);
 | 
						|
                pd->graphics->setDrawMode(clayColorToDrawMode(config->textColor));
 | 
						|
                pd->graphics->drawText(
 | 
						|
                    renderCommand->renderData.text.stringContents.chars,
 | 
						|
                    Clay_Playdate_CountUtf8Codepoints(
 | 
						|
                        renderCommand->renderData.text.stringContents.chars,
 | 
						|
                        renderCommand->renderData.text.stringContents.length
 | 
						|
                    ),
 | 
						|
                    kUTF8Encoding,
 | 
						|
                    boundingBox.x,
 | 
						|
                    boundingBox.y
 | 
						|
                );
 | 
						|
                pd->graphics->setDrawMode(kDrawModeCopy);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
 | 
						|
                pd->graphics->setClipRect(
 | 
						|
                    boundingBox.x,boundingBox.y,
 | 
						|
                    boundingBox.width, boundingBox.height
 | 
						|
                );
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
 | 
						|
                pd->graphics->clearClipRect();
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
 | 
						|
                Clay_ImageRenderData *config = &renderCommand->renderData.image;
 | 
						|
                LCDBitmap *texture = config->imageData;
 | 
						|
                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;
 | 
						|
 | 
						|
                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)
 | 
						|
                    );
 | 
						|
 | 
						|
                    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)
 | 
						|
                    );
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            default: {
 | 
						|
                pd->system->logToConsole("Error: unhandled render command: %d\n", renderCommand->commandType);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |