mirror of
				https://github.com/nicbarker/clay.git
				synced 2025-11-04 08:36:17 +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;
 | 
						|
} |