~rycwo/forge

ref: HEAD forge/src/gui_draw.c -rw-r--r-- 3.1 KiB
d7ee94d6Ryan Chan Fix missing cd in .build.yml 8 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// This file is part of Forge, the foundation library for Forge tools.
//
// Copyright (C) 2021 Ryan Chan <rycwo@posteo.net>
// SPDX-License-Identifier: GPL-3.0-only
//
// This Source Code Form is subject to the terms of the GNU General Public
// License v3.0 only. You should have received a copy of the license along with
// this program. If not, see <https://www.gnu.org/licenses/>.

#include "forge/gui_draw.h"

#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "forge/gui_util.h"
#include "forge/pack_float.h"

#define PRIM_CLIP_MASK (0xFFFFFF)

static bool
make_gui_rect(
		struct fg_gui_context* context,
		struct fg_gui_style const* style,
		fg_vec4 const rect,
		struct fg_gui_rect* prim) {
	memset(prim, 0, sizeof(struct fg_gui_rect));
	bool const border = style->flags & FG_GUI_STYLE_BORDER;
	// Filter out completely transparent rects
	if (!border && fabs(style->color[3]) <= 1e-6)
		return false;

	fg_vec4 rect_;
	fg_gui_transform_rect(context, rect_, rect);
	for (int i = 0; i < 4; ++i)
		prim->rect[i] = (float)rect_[i];

	float color[4];
	for (int i = 0; i < 4; ++i)
		color[i] = (float)style->color[i];
	prim->color = fg_pack_unorm_4x8(color);

	float border_properties[2] = {0.0, 0.0};
	if (border) {
		// Border alpha is ignored because it is a pain to deal with
		float border_color[4] = {
			(float)style->border_color[0],
			(float)style->border_color[1],
			(float)style->border_color[2],
			1.0f
		};
		prim->border_color = fg_pack_unorm_4x8(border_color);
		border_properties[0] = (float)style->border_width;
	}
	if (style->flags & FG_GUI_STYLE_ROUNDED_CORNERS)
		border_properties[1] = (float)style->corner_radius;
	prim->border = fg_pack_half_2x16(border_properties);
	prim->flags = ((uint32_t)context->clip) & PRIM_CLIP_MASK;
	return true;
}

static inline void
push_rect(struct fg_gui_context* context, struct fg_gui_rect prim) {
	struct fg_gui_rect_buffer* buffer = &context->rects;
	if (context->layer == FG_GUI_LAYER_OVERLAY) {
		int const size = (buffer->size_back)++;
		buffer->prims[buffer->capacity - size - 1] = prim;
		return;
	}
	buffer->prims[(buffer->size_front)++] = prim;
}

void
fg_draw_gui_rect(
		struct fg_gui_context* context,
		struct fg_gui_style const* style,
		fg_vec4 const rect) {
	// Make sure the prim buffer has enough memory before doing anything
	assert(context->rects.size_front + context->rects.size_back
			< context->rects.capacity);
	struct fg_gui_rect prim;
	if (!make_gui_rect(context, style, rect, &prim))
		return;
	push_rect(context, prim);
}

void
fg_draw_gui_circle(
		struct fg_gui_context* context,
		struct fg_gui_style const* style,
		fg_vec2 const center,
		fg_real radius) {
	// Make sure the prim buffer has enough memory before doing anything
	assert(context->rects.size_front + context->rects.size_back
			< context->rects.capacity);
	fg_real const r2 = radius + radius;
	fg_vec4 const rect = {center[0] - radius, center[1] - radius, r2, r2};
	struct fg_gui_rect prim;
	if (!make_gui_rect(context, style, rect, &prim))
		return;
	prim.flags = prim.flags | (1 << 24);
	push_rect(context, prim);
}

#undef PRIM_CLIP_MASK