mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-03 21:08:04 +00:00
241 lines
8.1 KiB
C
241 lines
8.1 KiB
C
#include <Windows.h>
|
|
#include "../../clay.h"
|
|
|
|
HDC renderer_hdcMem = {0};
|
|
HBITMAP renderer_hbmMem = {0};
|
|
HANDLE renderer_hOld = {0};
|
|
|
|
void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands)
|
|
{
|
|
bool is_clipping = false;
|
|
HRGN clipping_region = {0};
|
|
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
RECT rc; // Top left of our window
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
|
|
int win_width = rc.right - rc.left,
|
|
win_height = rc.bottom - rc.top;
|
|
|
|
// Create an off-screen DC for double-buffering
|
|
renderer_hdcMem = CreateCompatibleDC(hdc);
|
|
renderer_hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);
|
|
|
|
renderer_hOld = SelectObject(renderer_hdcMem, renderer_hbmMem);
|
|
|
|
// draw
|
|
|
|
for (int j = 0; j < renderCommands.length; j++)
|
|
{
|
|
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);
|
|
Clay_BoundingBox boundingBox = renderCommand->boundingBox;
|
|
|
|
switch (renderCommand->commandType)
|
|
{
|
|
case CLAY_RENDER_COMMAND_TYPE_TEXT:
|
|
{
|
|
Clay_Color c = renderCommand->renderData.text.textColor;
|
|
SetTextColor(renderer_hdcMem, RGB(c.r, c.g, c.b));
|
|
SetBkMode(renderer_hdcMem, TRANSPARENT);
|
|
|
|
RECT r = rc;
|
|
r.left = boundingBox.x;
|
|
r.top = boundingBox.y;
|
|
r.right = boundingBox.x + boundingBox.width + r.right;
|
|
r.bottom = boundingBox.y + boundingBox.height + r.bottom;
|
|
|
|
DrawTextA(renderer_hdcMem, renderCommand->renderData.text.stringContents.chars,
|
|
renderCommand->renderData.text.stringContents.length,
|
|
&r, DT_TOP | DT_LEFT);
|
|
|
|
break;
|
|
}
|
|
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:
|
|
{
|
|
Clay_RectangleRenderData rrd = renderCommand->renderData.rectangle;
|
|
RECT r = rc;
|
|
|
|
r.left = boundingBox.x;
|
|
r.top = boundingBox.y;
|
|
r.right = boundingBox.x + boundingBox.width;
|
|
r.bottom = boundingBox.y + boundingBox.height;
|
|
|
|
HBRUSH recColor = CreateSolidBrush(RGB(rrd.backgroundColor.r, rrd.backgroundColor.g, rrd.backgroundColor.b));
|
|
|
|
if (rrd.cornerRadius.topLeft > 0)
|
|
{
|
|
HRGN roundedRectRgn = CreateRoundRectRgn(
|
|
r.left, r.top, r.right + 1, r.bottom + 1,
|
|
rrd.cornerRadius.topLeft * 2, rrd.cornerRadius.topLeft * 2);
|
|
|
|
FillRgn(renderer_hdcMem, roundedRectRgn, recColor);
|
|
DeleteObject(roundedRectRgn);
|
|
}
|
|
else
|
|
{
|
|
FillRect(renderer_hdcMem, &r, recColor);
|
|
}
|
|
|
|
DeleteObject(recColor);
|
|
break;
|
|
}
|
|
|
|
// The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
|
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:
|
|
{
|
|
is_clipping = true;
|
|
|
|
clipping_region = CreateRectRgn(boundingBox.x,
|
|
boundingBox.y,
|
|
boundingBox.x + boundingBox.width,
|
|
boundingBox.y + boundingBox.height);
|
|
|
|
SelectClipRgn(renderer_hdcMem, clipping_region);
|
|
break;
|
|
}
|
|
|
|
// The renderer should finish any previously active clipping, and begin rendering elements in full again.
|
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:
|
|
{
|
|
SelectClipRgn(renderer_hdcMem, NULL);
|
|
|
|
if (clipping_region)
|
|
{
|
|
DeleteObject(clipping_region);
|
|
}
|
|
|
|
is_clipping = false;
|
|
clipping_region = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
// The renderer should draw a colored border inset into the bounding box.
|
|
case CLAY_RENDER_COMMAND_TYPE_BORDER:
|
|
{
|
|
Clay_BorderRenderData brd = renderCommand->renderData.border;
|
|
RECT r = rc;
|
|
|
|
r.left = boundingBox.x;
|
|
r.top = boundingBox.y;
|
|
r.right = boundingBox.x + boundingBox.width;
|
|
r.bottom = boundingBox.y + boundingBox.height;
|
|
|
|
HPEN topPen = CreatePen(PS_SOLID, brd.width.top, RGB(brd.color.r, brd.color.g, brd.color.b));
|
|
HPEN leftPen = CreatePen(PS_SOLID, brd.width.left, RGB(brd.color.r, brd.color.g, brd.color.b));
|
|
HPEN bottomPen = CreatePen(PS_SOLID, brd.width.bottom, RGB(brd.color.r, brd.color.g, brd.color.b));
|
|
HPEN rightPen = CreatePen(PS_SOLID, brd.width.right, RGB(brd.color.r, brd.color.g, brd.color.b));
|
|
|
|
HPEN oldPen = SelectObject(renderer_hdcMem, topPen);
|
|
|
|
if (brd.cornerRadius.topLeft == 0)
|
|
{
|
|
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.top);
|
|
|
|
SelectObject(renderer_hdcMem, leftPen);
|
|
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.left, r.bottom);
|
|
|
|
SelectObject(renderer_hdcMem, bottomPen);
|
|
MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.bottom);
|
|
|
|
SelectObject(renderer_hdcMem, rightPen);
|
|
MoveToEx(renderer_hdcMem, r.right, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.bottom);
|
|
}
|
|
else
|
|
{
|
|
// todo: i should be rounded
|
|
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.top);
|
|
|
|
SelectObject(renderer_hdcMem, leftPen);
|
|
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.left, r.bottom);
|
|
|
|
SelectObject(renderer_hdcMem, bottomPen);
|
|
MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.bottom);
|
|
|
|
SelectObject(renderer_hdcMem, rightPen);
|
|
MoveToEx(renderer_hdcMem, r.right, r.top, NULL);
|
|
LineTo(renderer_hdcMem, r.right, r.bottom);
|
|
|
|
}
|
|
|
|
SelectObject(renderer_hdcMem, oldPen);
|
|
DeleteObject(topPen);
|
|
DeleteObject(leftPen);
|
|
DeleteObject(bottomPen);
|
|
DeleteObject(rightPen);
|
|
|
|
break;
|
|
}
|
|
|
|
// case CLAY_RENDER_COMMAND_TYPE_IMAGE:
|
|
// {
|
|
// // TODO: i couldnt get the win 32 api to load a bitmap.... So im punting on this one :(
|
|
// break;
|
|
// }
|
|
|
|
default:
|
|
printf("Unhandled render command %d\r\n", renderCommand->commandType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BitBlt(hdc, 0, 0, win_width, win_height, renderer_hdcMem, 0, 0, SRCCOPY);
|
|
|
|
// Free-up the off-screen DC
|
|
SelectObject(renderer_hdcMem, renderer_hOld);
|
|
DeleteObject(renderer_hbmMem);
|
|
DeleteDC(renderer_hdcMem);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
|
|
/*
|
|
Hacks due to the windows api not making sence to use.... may measure too large, but never too small
|
|
*/
|
|
|
|
#ifndef WIN32_FONT_HEIGHT
|
|
#define WIN32_FONT_HEIGHT (16)
|
|
#endif
|
|
|
|
#ifndef WIN32_FONT_WIDTH
|
|
#define WIN32_FONT_WIDTH (8)
|
|
#endif
|
|
|
|
static inline Clay_Dimensions Clay_Win32_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)
|
|
{
|
|
Clay_Dimensions textSize = {0};
|
|
|
|
float maxTextWidth = 0.0f;
|
|
float lineTextWidth = 0;
|
|
float textHeight = WIN32_FONT_HEIGHT;
|
|
|
|
for (int i = 0; i < text.length; ++i)
|
|
{
|
|
if (text.chars[i] == '\n')
|
|
{
|
|
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
|
|
lineTextWidth = 0;
|
|
continue;
|
|
}
|
|
|
|
lineTextWidth += WIN32_FONT_WIDTH;
|
|
}
|
|
|
|
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
|
|
|
|
textSize.width = maxTextWidth;
|
|
textSize.height = textHeight;
|
|
|
|
return textSize;
|
|
} |