...
This commit is contained in:
106
ui/renderer.sx
106
ui/renderer.sx
@@ -4,6 +4,8 @@
|
||||
#import "modules/math";
|
||||
#import "ui/types.sx";
|
||||
#import "ui/render.sx";
|
||||
#import "ui/glyph_cache.sx";
|
||||
#import "ui/font.sx";
|
||||
|
||||
// Vertex: pos(2) + uv(2) + color(4) + params(4) = 12 floats
|
||||
UI_VERTEX_FLOATS :s64: 12;
|
||||
@@ -20,6 +22,7 @@ UIRenderer :: struct {
|
||||
vertex_count: s64;
|
||||
screen_width: f32;
|
||||
screen_height: f32;
|
||||
dpi_scale: f32;
|
||||
white_texture: u32;
|
||||
current_texture: u32;
|
||||
|
||||
@@ -61,6 +64,8 @@ UIRenderer :: struct {
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
self.dpi_scale = 1.0;
|
||||
|
||||
// 1x1 white texture for solid rects
|
||||
self.white_texture = create_white_texture();
|
||||
}
|
||||
@@ -137,7 +142,7 @@ UIRenderer :: struct {
|
||||
self.push_quad(node.frame, node.fill_color, node.corner_radius, node.stroke_width);
|
||||
}
|
||||
case .text: {
|
||||
if xx g_font != 0 and g_font.char_data != null {
|
||||
if xx g_font != 0 {
|
||||
self.render_text(node);
|
||||
}
|
||||
}
|
||||
@@ -149,11 +154,12 @@ UIRenderer :: struct {
|
||||
case .clip_push: {
|
||||
self.flush();
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
dpi := self.dpi_scale;
|
||||
glScissor(
|
||||
xx node.frame.origin.x,
|
||||
xx (self.screen_height - node.frame.origin.y - node.frame.size.height),
|
||||
xx node.frame.size.width,
|
||||
xx node.frame.size.height
|
||||
xx (node.frame.origin.x * dpi),
|
||||
xx ((self.screen_height - node.frame.origin.y - node.frame.size.height) * dpi),
|
||||
xx (node.frame.size.width * dpi),
|
||||
xx (node.frame.size.height * dpi)
|
||||
);
|
||||
}
|
||||
case .clip_pop: {
|
||||
@@ -195,8 +201,13 @@ UIRenderer :: struct {
|
||||
|
||||
render_text :: (self: *UIRenderer, node: RenderNode) {
|
||||
font := g_font;
|
||||
scale := node.font_size / font.font_size;
|
||||
if xx font == 0 { return; }
|
||||
|
||||
// Shape text into positioned glyphs
|
||||
font.shape_text(node.text, node.font_size);
|
||||
|
||||
// Flush any new glyphs to the atlas texture before rendering
|
||||
font.flush();
|
||||
self.bind_texture(font.texture_id);
|
||||
|
||||
r := node.text_color.rf();
|
||||
@@ -204,56 +215,51 @@ UIRenderer :: struct {
|
||||
b := node.text_color.bf();
|
||||
a := node.text_color.af();
|
||||
|
||||
// stbtt_GetBakedQuad works at baked size; we scale output positions
|
||||
xpos : f32 = 0.0;
|
||||
ypos : f32 = 0.0;
|
||||
q : AlignedQuad = ---;
|
||||
ascent := font.get_ascent(node.font_size);
|
||||
raster_size := node.font_size * font.dpi_scale;
|
||||
inv_dpi := font.inv_dpi;
|
||||
|
||||
i : s64 = 0;
|
||||
while i < node.text.len {
|
||||
ch : s32 = xx node.text[i];
|
||||
if ch >= FIRST_CHAR and ch < FIRST_CHAR + NUM_CHARS {
|
||||
stbtt_GetBakedQuad(xx font.char_data, ATLAS_W, ATLAS_H, ch - FIRST_CHAR, @xpos, @ypos, xx @q, 1);
|
||||
while i < font.shaped_buf.len {
|
||||
shaped := font.shaped_buf.items[i];
|
||||
cached := font.get_or_rasterize(shaped.glyph_index, raster_size);
|
||||
|
||||
// Scale and offset to frame position
|
||||
// ypos=0 means baseline is at y=0; glyphs go above (negative yoff)
|
||||
// Add ascent so top of text aligns with frame top
|
||||
gx0 := node.frame.origin.x + q.x0 * scale;
|
||||
gy0 := node.frame.origin.y + font.ascent * scale + q.y0 * scale;
|
||||
gx1 := node.frame.origin.x + q.x1 * scale;
|
||||
gy1 := node.frame.origin.y + font.ascent * scale + q.y1 * scale;
|
||||
if xx cached != 0 {
|
||||
if cached.width > 0.0 {
|
||||
// Scale physical pixel dimensions back to logical units
|
||||
gx0 := node.frame.origin.x + shaped.x + cached.offset_x * inv_dpi;
|
||||
gy0 := node.frame.origin.y + ascent + shaped.y + cached.offset_y * inv_dpi;
|
||||
gx1 := gx0 + cached.width * inv_dpi;
|
||||
gy1 := gy0 + cached.height * inv_dpi;
|
||||
|
||||
if self.vertex_count + 6 > MAX_UI_VERTICES {
|
||||
self.flush();
|
||||
u0 := cached.uv_x;
|
||||
v0 := cached.uv_y;
|
||||
u1 := cached.uv_x + cached.uv_w;
|
||||
v1 := cached.uv_y + cached.uv_h;
|
||||
|
||||
if self.vertex_count + 6 > MAX_UI_VERTICES {
|
||||
self.flush();
|
||||
}
|
||||
|
||||
// corner_radius = -1.0 signals "text mode" to the fragment shader
|
||||
neg1 : f32 = 0.0 - 1.0;
|
||||
self.write_vertex(gx0, gy0, u0, v0, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy0, u1, v0, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx0, gy1, u0, v1, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy0, u1, v0, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy1, u1, v1, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx0, gy1, u0, v1, r, g, b, a, neg1, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
// corner_radius = -1.0 signals "text mode" to the fragment shader
|
||||
self.write_vertex(gx0, gy0, q.s0, q.t0, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy0, q.s1, q.t0, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx0, gy1, q.s0, q.t1, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy0, q.s1, q.t0, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx1, gy1, q.s1, q.t1, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
self.write_vertex(gx0, gy1, q.s0, q.t1, r, g, b, a, 0.0 - 1.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Flush any glyphs rasterized during this text draw
|
||||
font.flush();
|
||||
self.bind_texture(self.white_texture);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload font atlas bitmap as GL texture (called after GL init)
|
||||
upload_font_texture :: (font: *FontAtlas) {
|
||||
if font.bitmap == null { return; }
|
||||
glGenTextures(1, @font.texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, font.texture_id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, xx GL_R8, ATLAS_W, ATLAS_H, 0, GL_RED, GL_UNSIGNED_BYTE, font.bitmap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, xx GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, xx GL_LINEAR);
|
||||
context.allocator.dealloc(font.bitmap);
|
||||
font.bitmap = null;
|
||||
}
|
||||
|
||||
create_white_texture :: () -> u32 {
|
||||
tex : u32 = 0;
|
||||
glGenTextures(1, @tex);
|
||||
@@ -311,8 +317,10 @@ void main() {
|
||||
vec2 rectSize = vParams.zw;
|
||||
|
||||
if (radius < 0.0) {
|
||||
float textAlpha = texture(uTex, vUV).r;
|
||||
FragColor = vec4(vColor.rgb, vColor.a * textAlpha);
|
||||
float alpha = texture(uTex, vUV).r;
|
||||
float ew = fwidth(alpha) * 0.7;
|
||||
alpha = smoothstep(0.5 - ew, 0.5 + ew, alpha);
|
||||
FragColor = vec4(vColor.rgb, vColor.a * pow(alpha, 0.9));
|
||||
} else if (radius > 0.0 || border > 0.0) {
|
||||
vec4 texColor = texture(uTex, vUV);
|
||||
vec2 half_size = rectSize * 0.5;
|
||||
@@ -381,8 +389,10 @@ void main() {
|
||||
vec2 rectSize = vParams.zw;
|
||||
|
||||
if (radius < 0.0) {
|
||||
float textAlpha = texture(uTex, vUV).r;
|
||||
FragColor = vec4(vColor.rgb, vColor.a * textAlpha);
|
||||
float alpha = texture(uTex, vUV).r;
|
||||
float ew = fwidth(alpha) * 0.7;
|
||||
alpha = smoothstep(0.5 - ew, 0.5 + ew, alpha);
|
||||
FragColor = vec4(vColor.rgb, vColor.a * pow(alpha, 0.9));
|
||||
} else if (radius > 0.0 || border > 0.0) {
|
||||
vec4 texColor = texture(uTex, vUV);
|
||||
vec2 half_size = rectSize * 0.5;
|
||||
|
||||
Reference in New Issue
Block a user