mirror of
https://github.com/IllusionMan1212/O2048.git
synced 2026-10-24 10:12:37 +00:00
zephr: consume hover event on top-most ui elements
This commit is contained in:
+15
-15
@@ -12,46 +12,46 @@ create_shader :: proc(vertex_path: string, fragment_path: string) -> (Shader, bo
|
||||
shader: Shader
|
||||
|
||||
program, success := gl.load_shaders(vertex_path, fragment_path)
|
||||
shader.program = program;
|
||||
shader.program = program
|
||||
|
||||
return shader, success;
|
||||
return shader, success
|
||||
}
|
||||
|
||||
use_shader :: proc(shader: Shader) {
|
||||
gl.UseProgram(shader.program);
|
||||
gl.UseProgram(shader.program)
|
||||
}
|
||||
|
||||
set_mat4f :: proc(shader: Shader, name: cstring, mat4: Mat4, transpose: bool = false) {
|
||||
mat4 := mat4
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.UniformMatrix4fv(loc, 1, transpose, raw_data(&mat4))
|
||||
}
|
||||
|
||||
set_float :: proc(shader: Shader, name: cstring, val: f32) {
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
gl.Uniform1f(loc, val);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.Uniform1f(loc, val)
|
||||
}
|
||||
|
||||
set_int :: proc(shader: Shader, name: cstring, val: i32) {
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
gl.Uniform1i(loc, val);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.Uniform1i(loc, val)
|
||||
}
|
||||
|
||||
set_bool :: proc(shader: Shader, name: cstring, val: bool) {
|
||||
set_int(shader, name, cast(i32)val);
|
||||
set_int(shader, name, cast(i32)val)
|
||||
}
|
||||
|
||||
set_vec2f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32) {
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
gl.Uniform2f(loc, val1, val2);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.Uniform2f(loc, val1, val2)
|
||||
}
|
||||
|
||||
set_vec3f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32) {
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
gl.Uniform3f(loc, val1, val2, val3);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.Uniform3f(loc, val1, val2, val3)
|
||||
}
|
||||
|
||||
set_vec4f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32, val4: f32) {
|
||||
loc := gl.GetUniformLocation(shader.program, name);
|
||||
gl.Uniform4f(loc, val1, val2, val3, val4);
|
||||
loc := gl.GetUniformLocation(shader.program, name)
|
||||
gl.Uniform4f(loc, val1, val2, val3, val4)
|
||||
}
|
||||
|
||||
+155
-155
@@ -45,14 +45,14 @@ font_instance_vbo : u32
|
||||
|
||||
|
||||
init_freetype :: proc(font_path: cstring) -> i32 {
|
||||
ft: FT.Library;
|
||||
ft: FT.Library
|
||||
if (FT.Init_FreeType(&ft) != 0) {
|
||||
return -1;
|
||||
return -1
|
||||
}
|
||||
|
||||
face: FT.Face;
|
||||
face: FT.Face
|
||||
if (FT.New_Face(ft, font_path, 0, &face) != 0) {
|
||||
return -2;
|
||||
return -2
|
||||
}
|
||||
|
||||
// sets the variable font to be bold
|
||||
@@ -66,301 +66,301 @@ init_freetype :: proc(font_path: cstring) -> i32 {
|
||||
/* FT_Done_MM_Var(ft, mm); */
|
||||
/* } */
|
||||
|
||||
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE);
|
||||
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE)
|
||||
|
||||
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
|
||||
|
||||
pen_x, pen_y: u32
|
||||
|
||||
/* FT_UInt glyph_idx; */
|
||||
/* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */
|
||||
|
||||
tex_width, tex_height: u32;
|
||||
tex_width, tex_height: u32
|
||||
for i in 32..<128 {
|
||||
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
||||
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
|
||||
fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i)
|
||||
}
|
||||
|
||||
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
||||
|
||||
tex_width += face.glyph.bitmap.width + 1;
|
||||
tex_height = max(tex_height, face.glyph.bitmap.rows);
|
||||
tex_width += face.glyph.bitmap.width + 1
|
||||
tex_height = max(tex_height, face.glyph.bitmap.rows)
|
||||
}
|
||||
|
||||
pixels := make([dynamic]u8, tex_width * tex_height);
|
||||
pixels := make([dynamic]u8, tex_width * tex_height)
|
||||
defer delete(pixels)
|
||||
|
||||
for i in 32..<128 {
|
||||
/* while (glyph_idx) { */
|
||||
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
||||
fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i);
|
||||
fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i)
|
||||
}
|
||||
|
||||
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
||||
|
||||
bmp := &face.glyph.bitmap;
|
||||
bmp := &face.glyph.bitmap
|
||||
|
||||
if (pen_x + bmp.width >= tex_width) {
|
||||
pen_x = 0;
|
||||
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6));
|
||||
pen_x = 0
|
||||
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6))
|
||||
}
|
||||
|
||||
for row in 0..<bmp.rows {
|
||||
for col in 0..<bmp.width {
|
||||
x := pen_x + col;
|
||||
y := pen_y + row;
|
||||
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col];
|
||||
x := pen_x + col
|
||||
y := pen_y + row
|
||||
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col]
|
||||
}
|
||||
}
|
||||
|
||||
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width;
|
||||
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height;
|
||||
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width;
|
||||
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height;
|
||||
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width
|
||||
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height
|
||||
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width
|
||||
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height
|
||||
|
||||
top_left := Vec2{ atlas_x0, atlas_y1 };
|
||||
top_right := Vec2{ atlas_x1, atlas_y1 };
|
||||
bottom_right := Vec2{ atlas_x1, atlas_y0 };
|
||||
bottom_left := Vec2{ atlas_x0, atlas_y0 };
|
||||
top_left := Vec2{ atlas_x0, atlas_y1 }
|
||||
top_right := Vec2{ atlas_x1, atlas_y1 }
|
||||
bottom_right := Vec2{ atlas_x1, atlas_y0 }
|
||||
bottom_left := Vec2{ atlas_x0, atlas_y0 }
|
||||
|
||||
character: Character
|
||||
|
||||
character.tex_coords[0] = top_left;
|
||||
character.tex_coords[1] = top_right;
|
||||
character.tex_coords[2] = bottom_right;
|
||||
character.tex_coords[3] = bottom_left;
|
||||
character.advance = cast(u32)face.glyph.advance.x;
|
||||
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows };
|
||||
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top };
|
||||
character.tex_coords[0] = top_left
|
||||
character.tex_coords[1] = top_right
|
||||
character.tex_coords[2] = bottom_right
|
||||
character.tex_coords[3] = bottom_left
|
||||
character.advance = cast(u32)face.glyph.advance.x
|
||||
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows }
|
||||
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top }
|
||||
|
||||
zephr_ctx.font.characters[i] = character;
|
||||
zephr_ctx.font.characters[i] = character
|
||||
|
||||
pen_x += bmp.width + 1;
|
||||
pen_x += bmp.width + 1
|
||||
|
||||
/* c = FT_Get_Next_Char(face, c, &glyph_idx); */
|
||||
}
|
||||
|
||||
texture: TextureId;
|
||||
gl.GenTextures(1, &texture);
|
||||
gl.BindTexture(gl.TEXTURE_2D, texture);
|
||||
texture: TextureId
|
||||
gl.GenTextures(1, &texture)
|
||||
gl.BindTexture(gl.TEXTURE_2D, texture)
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, cast(i32)tex_width,
|
||||
cast(i32)tex_height, 0, gl.RED, gl.UNSIGNED_BYTE,
|
||||
raw_data(pixels))
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
|
||||
zephr_ctx.font.atlas_tex_id = texture;
|
||||
zephr_ctx.font.atlas_tex_id = texture
|
||||
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||
|
||||
FT.Done_Face(face);
|
||||
FT.Done_FreeType(ft);
|
||||
FT.Done_Face(face)
|
||||
FT.Done_FreeType(ft)
|
||||
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
init_fonts :: proc(font_path: cstring) -> i32 {
|
||||
font_vbo, font_ebo: u32;
|
||||
font_vbo, font_ebo: u32
|
||||
|
||||
res := init_freetype(font_path);
|
||||
res := init_freetype(font_path)
|
||||
if (res != 0) {
|
||||
return res;
|
||||
return res
|
||||
}
|
||||
|
||||
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag");
|
||||
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag")
|
||||
|
||||
if (!success) {
|
||||
fmt.println("[FATAL] Failed to create font shader")
|
||||
return -2;
|
||||
return -2
|
||||
}
|
||||
|
||||
font_shader = l_font_shader;
|
||||
font_shader = l_font_shader
|
||||
|
||||
gl.GenVertexArrays(1, &font_vao);
|
||||
gl.GenBuffers(1, &font_vbo);
|
||||
gl.GenBuffers(1, &font_instance_vbo);
|
||||
gl.GenBuffers(1, &font_ebo);
|
||||
gl.GenVertexArrays(1, &font_vao)
|
||||
gl.GenBuffers(1, &font_vbo)
|
||||
gl.GenBuffers(1, &font_instance_vbo)
|
||||
gl.GenBuffers(1, &font_ebo)
|
||||
|
||||
quad_vertices := [4][2]f32 {
|
||||
{0.0, 1.0}, // top left
|
||||
{1.0, 1.0}, // top right
|
||||
{1.0, 0.0}, // bottom right
|
||||
{0.0, 0.0}, // bottom left
|
||||
};
|
||||
}
|
||||
|
||||
quad_indices := [6]u32 {
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
};
|
||||
}
|
||||
|
||||
gl.BindVertexArray(font_vao);
|
||||
gl.BindVertexArray(font_vao)
|
||||
|
||||
// font quad vbo
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo);
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW);
|
||||
gl.EnableVertexAttribArray(0);
|
||||
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0);
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0)
|
||||
|
||||
// font instance vbo
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||
gl.EnableVertexAttribArray(1);
|
||||
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0);
|
||||
gl.VertexAttribDivisor(1, 1);
|
||||
gl.EnableVertexAttribArray(2);
|
||||
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4));
|
||||
gl.VertexAttribDivisor(2, 1);
|
||||
gl.EnableVertexAttribArray(3);
|
||||
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)));
|
||||
gl.VertexAttribDivisor(3, 1);
|
||||
gl.EnableVertexAttribArray(4);
|
||||
gl.EnableVertexAttribArray(5);
|
||||
gl.EnableVertexAttribArray(6);
|
||||
gl.EnableVertexAttribArray(7);
|
||||
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)));
|
||||
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)));
|
||||
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)));
|
||||
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)));
|
||||
gl.VertexAttribDivisor(4, 1);
|
||||
gl.VertexAttribDivisor(5, 1);
|
||||
gl.VertexAttribDivisor(6, 1);
|
||||
gl.VertexAttribDivisor(7, 1);
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
||||
gl.EnableVertexAttribArray(1)
|
||||
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0)
|
||||
gl.VertexAttribDivisor(1, 1)
|
||||
gl.EnableVertexAttribArray(2)
|
||||
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4))
|
||||
gl.VertexAttribDivisor(2, 1)
|
||||
gl.EnableVertexAttribArray(3)
|
||||
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)))
|
||||
gl.VertexAttribDivisor(3, 1)
|
||||
gl.EnableVertexAttribArray(4)
|
||||
gl.EnableVertexAttribArray(5)
|
||||
gl.EnableVertexAttribArray(6)
|
||||
gl.EnableVertexAttribArray(7)
|
||||
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)))
|
||||
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)))
|
||||
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)))
|
||||
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)))
|
||||
gl.VertexAttribDivisor(4, 1)
|
||||
gl.VertexAttribDivisor(5, 1)
|
||||
gl.VertexAttribDivisor(6, 1)
|
||||
gl.VertexAttribDivisor(7, 1)
|
||||
|
||||
// font ebo
|
||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo);
|
||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW);
|
||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo)
|
||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW)
|
||||
|
||||
use_shader(font_shader);
|
||||
use_shader(font_shader)
|
||||
|
||||
for i in 32..<128 {
|
||||
for j in 0..<4 {
|
||||
buf: [24]byte
|
||||
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j);
|
||||
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y);
|
||||
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j)
|
||||
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y)
|
||||
}
|
||||
}
|
||||
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, 0);
|
||||
gl.BindVertexArray(0);
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
||||
gl.BindVertexArray(0)
|
||||
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
calculate_text_size :: proc(text: string, font_size: u32) -> Vec2 {
|
||||
scale := cast(f32)font_size / FONT_PIXEL_SIZE;
|
||||
scale := cast(f32)font_size / FONT_PIXEL_SIZE
|
||||
size : Vec2 = ---
|
||||
w: f32 = 0;
|
||||
h: f32 = 0;
|
||||
max_bearing_h: f32 = 0;
|
||||
w: f32 = 0
|
||||
h: f32 = 0
|
||||
max_bearing_h: f32 = 0
|
||||
|
||||
// NOTE: I don't like looping through the characters twice, but it's fine for now
|
||||
for i in 0..<len(text) {
|
||||
ch := zephr_ctx.font.characters[text[i]];
|
||||
max_bearing_h = max(max_bearing_h, ch.bearing.y);
|
||||
ch := zephr_ctx.font.characters[text[i]]
|
||||
max_bearing_h = max(max_bearing_h, ch.bearing.y)
|
||||
}
|
||||
|
||||
for i in 0..<len(text) {
|
||||
ch := zephr_ctx.font.characters[text[i]];
|
||||
w += cast(f32)ch.advance / 64;
|
||||
ch := zephr_ctx.font.characters[text[i]]
|
||||
w += cast(f32)ch.advance / 64
|
||||
|
||||
// remove bearing of first character
|
||||
if (i == 0 && len(text) > 1) {
|
||||
w -= ch.bearing.x;
|
||||
w -= ch.bearing.x
|
||||
}
|
||||
|
||||
// remove the trailing width of the last character
|
||||
if (i == len(text) - 1) {
|
||||
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x));
|
||||
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x))
|
||||
}
|
||||
|
||||
// if we only have one character in the text, then remove the bearing width
|
||||
if (len(text) == 1) {
|
||||
w -= (ch.bearing.x);
|
||||
w -= (ch.bearing.x)
|
||||
}
|
||||
|
||||
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y);
|
||||
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y)
|
||||
|
||||
if (text[i] == '\n') {
|
||||
w = 0;
|
||||
h += max_bearing_h;
|
||||
w = 0
|
||||
h += max_bearing_h
|
||||
}
|
||||
}
|
||||
size.x = w * scale;
|
||||
size.y = h * scale;
|
||||
size.x = w * scale
|
||||
size.y = h * scale
|
||||
|
||||
return size;
|
||||
return size
|
||||
}
|
||||
|
||||
draw_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
||||
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
|
||||
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0);
|
||||
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
|
||||
gl.BindVertexArray(font_vao);
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
|
||||
gl.BindVertexArray(font_vao)
|
||||
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW);
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW)
|
||||
|
||||
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(glyph_instance_list))
|
||||
|
||||
gl.BindVertexArray(0);
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||
gl.BindVertexArray(0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||
|
||||
delete(glyph_instance_list)
|
||||
}
|
||||
|
||||
draw_text_batch :: proc(batch: ^GlyphInstanceList) {
|
||||
gl.ActiveTexture(gl.TEXTURE0);
|
||||
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id);
|
||||
gl.BindVertexArray(font_vao);
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
|
||||
gl.BindVertexArray(font_vao)
|
||||
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo);
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW);
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW)
|
||||
|
||||
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(batch))
|
||||
|
||||
gl.BindVertexArray(0);
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0);
|
||||
gl.BindVertexArray(0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, 0)
|
||||
|
||||
delete(batch^)
|
||||
}
|
||||
|
||||
get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) -> GlyphInstanceList {
|
||||
constraints := constraints
|
||||
use_shader(font_shader);
|
||||
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 };
|
||||
use_shader(font_shader)
|
||||
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 }
|
||||
|
||||
set_mat4f(font_shader, "projection", zephr_ctx.projection)
|
||||
|
||||
rect: Rect
|
||||
|
||||
apply_constraints(&constraints, &rect.pos, &rect.size);
|
||||
apply_constraints(&constraints, &rect.pos, &rect.size)
|
||||
|
||||
text_size := calculate_text_size(text, FONT_PIXEL_SIZE);
|
||||
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x;
|
||||
text_size := calculate_text_size(text, FONT_PIXEL_SIZE)
|
||||
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x
|
||||
|
||||
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos);
|
||||
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos)
|
||||
|
||||
model := identity()
|
||||
scale(&model, Vec3{font_scale, font_scale, 1});
|
||||
scale(&model, Vec3{font_scale, font_scale, 1})
|
||||
|
||||
// rotate around the center point of the text
|
||||
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0});
|
||||
rotate(&model, constraints.rotation, Vec3{0, 0, 1});
|
||||
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0});
|
||||
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0})
|
||||
rotate(&model, constraints.rotation, Vec3{0, 0, 1})
|
||||
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0})
|
||||
|
||||
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0});
|
||||
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0})
|
||||
|
||||
max_bearing_h: f32 = 0;
|
||||
max_bearing_h: f32 = 0
|
||||
for i in 0..<len(text) {
|
||||
ch := zephr_ctx.font.characters[text[i]];
|
||||
max_bearing_h = max(max_bearing_h, ch.bearing.y);
|
||||
ch := zephr_ctx.font.characters[text[i]]
|
||||
max_bearing_h = max(max_bearing_h, ch.bearing.y)
|
||||
}
|
||||
|
||||
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x;
|
||||
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x
|
||||
|
||||
glyph_instance_list: GlyphInstanceList
|
||||
reserve(&glyph_instance_list, 16)
|
||||
@@ -369,21 +369,21 @@ get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constrai
|
||||
// scale up or down the model matrix to get the desired font size.
|
||||
// this way everything works out fine and we get to transform the text using the
|
||||
// model matrix
|
||||
c := 0;
|
||||
x: u32 = 0;
|
||||
y: f32 = 0;
|
||||
c := 0
|
||||
x: u32 = 0
|
||||
y: f32 = 0
|
||||
for c != len(text) {
|
||||
ch := zephr_ctx.font.characters[text[c]]
|
||||
// subtract the bearing width of the first character to remove the extra space
|
||||
// at the start of the text and move every char to the left by that width
|
||||
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w));
|
||||
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h));
|
||||
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w))
|
||||
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h))
|
||||
|
||||
if (text[c] == '\n') {
|
||||
x = 0;
|
||||
y += max_bearing_h + (36 * LINE_HEIGHT);
|
||||
c += 1;
|
||||
continue;
|
||||
x = 0
|
||||
y += max_bearing_h + (36 * LINE_HEIGHT)
|
||||
c += 1
|
||||
continue
|
||||
}
|
||||
|
||||
test_model := Mat4 {
|
||||
@@ -402,18 +402,18 @@ get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constrai
|
||||
|
||||
append(&glyph_instance_list, instance)
|
||||
|
||||
x += (ch.advance >> 6);
|
||||
c += 1;
|
||||
x += (ch.advance >> 6)
|
||||
c += 1
|
||||
}
|
||||
|
||||
return glyph_instance_list;
|
||||
return glyph_instance_list
|
||||
}
|
||||
|
||||
add_text_instance :: proc(batch: ^GlyphInstanceList, text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
||||
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment);
|
||||
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
|
||||
|
||||
append(batch, ..glyph_instance_list[:])
|
||||
|
||||
delete(glyph_instance_list);
|
||||
delete(glyph_instance_list)
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -6,40 +6,40 @@ import stb "vendor:stb/image"
|
||||
TextureId :: u32
|
||||
|
||||
load_texture :: proc(path: cstring) -> TextureId {
|
||||
texture_id: TextureId;
|
||||
texture_id: TextureId
|
||||
width, height, channels: i32
|
||||
data := stb.load(path, &width, &height, &channels, 4) // RGBA
|
||||
defer stb.image_free(data)
|
||||
|
||||
format := gl.RGBA;
|
||||
format := gl.RGBA
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
format = gl.RED;
|
||||
break;
|
||||
format = gl.RED
|
||||
break
|
||||
case 2:
|
||||
format = gl.RG;
|
||||
break;
|
||||
format = gl.RG
|
||||
break
|
||||
case 3:
|
||||
format = gl.RGB;
|
||||
break;
|
||||
format = gl.RGB
|
||||
break
|
||||
case 4:
|
||||
format = gl.RGBA;
|
||||
break;
|
||||
format = gl.RGBA
|
||||
break
|
||||
}
|
||||
|
||||
gl.GenTextures(1, &texture_id);
|
||||
gl.BindTexture(gl.TEXTURE_2D, texture_id);
|
||||
gl.GenTextures(1, &texture_id)
|
||||
gl.BindTexture(gl.TEXTURE_2D, texture_id)
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data);
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data)
|
||||
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D);
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
|
||||
return texture_id;
|
||||
return texture_id
|
||||
}
|
||||
|
||||
+398
-353
File diff suppressed because it is too large
Load Diff
+188
-164
@@ -379,6 +379,8 @@ XA_CARDINAL :: x11.Atom(6)
|
||||
FNV_HASH32_INIT :: 0x811c9dc5
|
||||
@private
|
||||
FNV_HASH32_PRIME :: 0x01000193
|
||||
@private
|
||||
INIT_UI_STACK_SIZE :: 256
|
||||
|
||||
COLOR_BLACK :: Color{0, 0, 0, 255}
|
||||
COLOR_WHITE :: Color{255, 255, 255, 255}
|
||||
@@ -651,64 +653,64 @@ x11_go_fullscreen :: proc() {
|
||||
// remove the resizing constraint before going fullscreen so WMs such as gnome
|
||||
// can add the _NET_WM_ACTION_FULLSCREEN action to the _NET_WM_ALLOWED_ACTIONS atom
|
||||
// and properly fullscreen the window
|
||||
size_hints := x11.XAllocSizeHints();
|
||||
size_hints := x11.XAllocSizeHints()
|
||||
|
||||
zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x;
|
||||
zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y;
|
||||
zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x
|
||||
zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y
|
||||
if (size_hints != nil) {
|
||||
size_hints.flags = {.PPosition, .PSize};
|
||||
size_hints.width = cast(i32)zephr_ctx.window.size.x;
|
||||
size_hints.height = cast(i32)zephr_ctx.window.size.y;
|
||||
x11.XSetWMNormalHints(x11_display, x11_window, size_hints);
|
||||
x11.XFree(size_hints);
|
||||
size_hints.flags = {.PPosition, .PSize}
|
||||
size_hints.width = cast(i32)zephr_ctx.window.size.x
|
||||
size_hints.height = cast(i32)zephr_ctx.window.size.y
|
||||
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
|
||||
x11.XFree(size_hints)
|
||||
}
|
||||
|
||||
xev: x11.XEvent;
|
||||
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false);
|
||||
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false);
|
||||
xev.type = .ClientMessage;
|
||||
xev.xclient.window = x11_window;
|
||||
xev.xclient.message_type = wm_state;
|
||||
xev.xclient.format = 32;
|
||||
xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
|
||||
xev.xclient.data.l[1] = cast(int)fullscreen;
|
||||
xev.xclient.data.l[2] = 0;
|
||||
xev: x11.XEvent
|
||||
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false)
|
||||
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false)
|
||||
xev.type = .ClientMessage
|
||||
xev.xclient.window = x11_window
|
||||
xev.xclient.message_type = wm_state
|
||||
xev.xclient.format = 32
|
||||
xev.xclient.data.l[0] = 1 // _NET_WM_STATE_ADD
|
||||
xev.xclient.data.l[1] = cast(int)fullscreen
|
||||
xev.xclient.data.l[2] = 0
|
||||
x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false,
|
||||
{.SubstructureNotify, .SubstructureRedirect}, &xev);
|
||||
{.SubstructureNotify, .SubstructureRedirect}, &xev)
|
||||
}
|
||||
|
||||
@private
|
||||
x11_return_fullscreen :: proc() {
|
||||
xev: x11.XEvent;
|
||||
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false);
|
||||
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false);
|
||||
xev.type = .ClientMessage;
|
||||
xev.xclient.window = x11_window;
|
||||
xev.xclient.message_type = wm_state;
|
||||
xev.xclient.format = 32;
|
||||
xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE
|
||||
xev.xclient.data.l[1] = cast(int)fullscreen;
|
||||
xev.xclient.data.l[2] = 0;
|
||||
xev: x11.XEvent
|
||||
wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false)
|
||||
fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false)
|
||||
xev.type = .ClientMessage
|
||||
xev.xclient.window = x11_window
|
||||
xev.xclient.message_type = wm_state
|
||||
xev.xclient.format = 32
|
||||
xev.xclient.data.l[0] = 0 // _NET_WM_STATE_REMOVE
|
||||
xev.xclient.data.l[1] = cast(int)fullscreen
|
||||
xev.xclient.data.l[2] = 0
|
||||
x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false,
|
||||
{.SubstructureNotify, .SubstructureRedirect}, &xev);
|
||||
{.SubstructureNotify, .SubstructureRedirect}, &xev)
|
||||
|
||||
// restore the resizing constraint as well as the pre-fullscreen window size
|
||||
// when returning from fullscreen
|
||||
size_hints := x11.XAllocSizeHints();
|
||||
size_hints := x11.XAllocSizeHints()
|
||||
|
||||
if (size_hints != nil) {
|
||||
size_hints.flags = {.PPosition, .PSize};
|
||||
size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
|
||||
size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
|
||||
size_hints.flags = {.PPosition, .PSize}
|
||||
size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
|
||||
size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
|
||||
if (zephr_ctx.window.non_resizable) {
|
||||
size_hints.flags |= {.PMinSize, .PMaxSize};
|
||||
size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
|
||||
size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
|
||||
size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x;
|
||||
size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y;
|
||||
size_hints.flags |= {.PMinSize, .PMaxSize}
|
||||
size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
|
||||
size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
|
||||
size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x
|
||||
size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y
|
||||
}
|
||||
x11.XSetWMNormalHints(x11_display, x11_window, size_hints);
|
||||
x11.XFree(size_hints);
|
||||
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
|
||||
x11.XFree(size_hints)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,30 +729,30 @@ x11_toggle_fullscreen :: proc(fullscreen: bool) {
|
||||
@private
|
||||
x11_assign_window_icon :: proc(icon_path: cstring) {
|
||||
icon_width, icon_height: i32
|
||||
icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4);
|
||||
icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4)
|
||||
defer image.image_free(icon_data)
|
||||
assert(icon_data != nil, "Failed to load icon image")
|
||||
|
||||
target_size := 2 + icon_width * icon_height;
|
||||
target_size := 2 + icon_width * icon_height
|
||||
|
||||
data := make([]u64, target_size * size_of(u64))
|
||||
|
||||
// first two elements are width and height
|
||||
data[0] = cast(u64)icon_width;
|
||||
data[1] = cast(u64)icon_height;
|
||||
data[0] = cast(u64)icon_width
|
||||
data[1] = cast(u64)icon_height
|
||||
|
||||
for i in 0..<(icon_width*icon_height) {
|
||||
data[i + 2] = cast(u64)((icon_data[i * 4] << 16) | (icon_data[i * 4 + 1] << 8) | (icon_data[i * 4 + 2] << 0) | (icon_data[i * 4 + 3] << 24))
|
||||
}
|
||||
|
||||
net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false);
|
||||
net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false)
|
||||
|
||||
x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size);
|
||||
x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size)
|
||||
}
|
||||
|
||||
@private
|
||||
x11_get_screen_size :: proc() -> Vec2 {
|
||||
screen := x11.XDefaultScreenOfDisplay(x11_display);
|
||||
screen := x11.XDefaultScreenOfDisplay(x11_display)
|
||||
|
||||
return Vec2{cast(f32)screen.width, cast(f32)screen.height}
|
||||
}
|
||||
@@ -792,7 +794,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
|
||||
{.CWColormap, .CWEventMask}, &attributes)
|
||||
|
||||
if (icon_path != "") {
|
||||
x11_assign_window_icon(icon_path);
|
||||
x11_assign_window_icon(icon_path)
|
||||
}
|
||||
|
||||
// Hints to the WM that the window is a normal window
|
||||
@@ -803,7 +805,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
|
||||
|
||||
wm_delete_window := x11.XInternAtom(x11_display, "WM_DELETE_WINDOW", false)
|
||||
x11.XSetWMProtocols(x11_display, x11_window, &wm_delete_window, 1)
|
||||
zephr_ctx.window_delete_atom = wm_delete_window;
|
||||
zephr_ctx.window_delete_atom = wm_delete_window
|
||||
|
||||
// set window name
|
||||
{
|
||||
@@ -840,11 +842,11 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
|
||||
size_hints.width = cast(i32)window_size.x
|
||||
size_hints.height = cast(i32)window_size.y
|
||||
if (window_non_resizable) {
|
||||
size_hints.flags |= {.PMinSize, .PMaxSize};
|
||||
size_hints.min_width = cast(i32)window_size.x;
|
||||
size_hints.min_height = cast(i32)window_size.y;
|
||||
size_hints.max_width = cast(i32)window_size.x;
|
||||
size_hints.max_height = cast(i32)window_size.y;
|
||||
size_hints.flags |= {.PMinSize, .PMaxSize}
|
||||
size_hints.min_width = cast(i32)window_size.x
|
||||
size_hints.min_height = cast(i32)window_size.y
|
||||
size_hints.max_width = cast(i32)window_size.x
|
||||
size_hints.max_height = cast(i32)window_size.y
|
||||
}
|
||||
x11.XSetWMNormalHints(x11_display, x11_window, size_hints)
|
||||
x11.XFree(size_hints)
|
||||
@@ -877,16 +879,16 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
|
||||
glx.CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
glx.CONTEXT_PROFILE_MASK_ARB, glx.CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
x11.None,
|
||||
};
|
||||
}
|
||||
|
||||
glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes));
|
||||
glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes))
|
||||
|
||||
if glx_context == nil {
|
||||
fmt.eprintf("Failed to create GLX context\n")
|
||||
return
|
||||
}
|
||||
|
||||
glx.MakeCurrent(x11_display, x11_window, glx_context);
|
||||
glx.MakeCurrent(x11_display, x11_window, glx_context)
|
||||
|
||||
// TODO: find a way to properly get the GL version
|
||||
gl_major: i32 = ---
|
||||
@@ -896,7 +898,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c
|
||||
|
||||
fmt.printf("GL version: %d.%d\n", gl_major, gl_minor)
|
||||
|
||||
glx.SwapIntervalEXT(x11_display, x11_window, 1);
|
||||
glx.SwapIntervalEXT(x11_display, x11_window, 1)
|
||||
// we enable blending for text
|
||||
gl.Enable(gl.BLEND)
|
||||
gl.Enable(gl.MULTISAMPLE)
|
||||
@@ -913,12 +915,12 @@ x11_init :: proc(window_title: cstring, window_size: Vec2, icon_path: cstring, w
|
||||
|
||||
@private
|
||||
x11_close :: proc() {
|
||||
glx.MakeCurrent(x11_display, 0, nil);
|
||||
glx.DestroyContext(x11_display, glx_context);
|
||||
glx.MakeCurrent(x11_display, 0, nil)
|
||||
glx.DestroyContext(x11_display, glx_context)
|
||||
|
||||
x11.XDestroyWindow(x11_display, x11_window);
|
||||
x11.XFreeColormap(x11_display, x11_colormap);
|
||||
x11.XCloseDisplay(x11_display);
|
||||
x11.XDestroyWindow(x11_display, x11_window)
|
||||
x11.XFreeColormap(x11_display, x11_colormap)
|
||||
x11.XCloseDisplay(x11_display)
|
||||
}
|
||||
|
||||
|
||||
@@ -938,22 +940,24 @@ init :: proc(font_path: cstring, icon_path: cstring, window_title: cstring, wind
|
||||
|
||||
ui_init(font_path)
|
||||
|
||||
zephr_ctx.ui.elements = make([dynamic]UiElement, INIT_UI_STACK_SIZE)
|
||||
zephr_ctx.mouse.pos = Vec2{-1, -1}
|
||||
zephr_ctx.window.size = window_size
|
||||
zephr_ctx.window.non_resizable = window_non_resizable
|
||||
zephr_ctx.projection = orthographic_projection_2d(0, window_size.x, window_size.y, 0)
|
||||
|
||||
zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr);
|
||||
zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm);
|
||||
zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair);
|
||||
zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1);
|
||||
zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow);
|
||||
zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow);
|
||||
zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr)
|
||||
zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm)
|
||||
zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair)
|
||||
zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1)
|
||||
zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow)
|
||||
zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow)
|
||||
|
||||
// non-standard cursors
|
||||
zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle");
|
||||
zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle")
|
||||
|
||||
zephr_ctx.screen_size = x11_get_screen_size();
|
||||
start_internal_timer();
|
||||
zephr_ctx.screen_size = x11_get_screen_size()
|
||||
start_internal_timer()
|
||||
}
|
||||
|
||||
deinit :: proc() {
|
||||
@@ -965,24 +969,42 @@ should_quit :: proc() -> bool {
|
||||
gl.ClearColor(0, 0, 0, 1)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
|
||||
zephr_ctx.cursor = .ARROW;
|
||||
zephr_ctx.mouse.released = false;
|
||||
zephr_ctx.mouse.pressed = false;
|
||||
zephr_ctx.cursor = .ARROW
|
||||
zephr_ctx.mouse.released = false
|
||||
zephr_ctx.mouse.pressed = false
|
||||
|
||||
//audio_update();
|
||||
|
||||
return zephr_ctx.should_quit;
|
||||
return zephr_ctx.should_quit
|
||||
}
|
||||
|
||||
quit :: proc() {
|
||||
zephr_ctx.should_quit = true;
|
||||
zephr_ctx.should_quit = true
|
||||
}
|
||||
|
||||
@private
|
||||
consume_mouse_events :: proc() -> bool {
|
||||
defer clear(&zephr_ctx.ui.elements)
|
||||
|
||||
#reverse for e in zephr_ctx.ui.elements {
|
||||
if (inside_rect(e.rect, zephr_ctx.mouse.pos)) {
|
||||
zephr_ctx.ui.hovered_element = e.id
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
swap_buffers :: proc() {
|
||||
if (zephr_ctx.ui.popup_open) {
|
||||
draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints);
|
||||
draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints)
|
||||
}
|
||||
zephr_ctx.ui.popup_open = false
|
||||
|
||||
if consume_mouse_events() {
|
||||
zephr_ctx.ui.hovered_element = 0
|
||||
}
|
||||
zephr_ctx.ui.popup_open = false;
|
||||
|
||||
glx.SwapBuffers(x11_display, x11_window)
|
||||
x11.XDefineCursor(x11_display, x11_window, zephr_ctx.cursors[zephr_ctx.cursor])
|
||||
@@ -1010,77 +1032,77 @@ iter_events :: proc(e_out: ^Event) -> bool {
|
||||
}
|
||||
} else if xev.type == .DestroyNotify {
|
||||
// window destroy event
|
||||
e_out.type = .WINDOW_CLOSED;
|
||||
e_out.type = .WINDOW_CLOSED
|
||||
|
||||
return true;
|
||||
return true
|
||||
} else if xev.type == .ClientMessage {
|
||||
// window close event
|
||||
if (cast(x11.Atom)xev.xclient.data.l[0] == zephr_ctx.window_delete_atom) {
|
||||
e_out.type = .WINDOW_CLOSED;
|
||||
e_out.type = .WINDOW_CLOSED
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
} else if xev.type == .KeyPress {
|
||||
xke := xev.xkey;
|
||||
xke := xev.xkey
|
||||
|
||||
evdev_keycode := xke.keycode - 8;
|
||||
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode];
|
||||
evdev_keycode := xke.keycode - 8
|
||||
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]
|
||||
|
||||
e_out.type = .KEY_PRESSED;
|
||||
e_out.key.scancode = scancode;
|
||||
e_out.type = .KEY_PRESSED
|
||||
e_out.key.scancode = scancode
|
||||
//e_out.key.code = keycode;
|
||||
e_out.key.mods = x11_mods_to_zephr_mods(scancode, true);
|
||||
e_out.key.mods = x11_mods_to_zephr_mods(scancode, true)
|
||||
|
||||
return true
|
||||
} else if xev.type == .KeyRelease {
|
||||
xke := xev.xkey;
|
||||
xke := xev.xkey
|
||||
|
||||
evdev_keycode := xke.keycode - 8;
|
||||
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode];
|
||||
evdev_keycode := xke.keycode - 8
|
||||
scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]
|
||||
|
||||
e_out.type = .KEY_RELEASED;
|
||||
e_out.type = .KEY_RELEASED
|
||||
e_out.key.scancode = scancode
|
||||
//e_out.key.code = keycode;
|
||||
e_out.key.mods = x11_mods_to_zephr_mods(scancode, false);
|
||||
e_out.key.mods = x11_mods_to_zephr_mods(scancode, false)
|
||||
|
||||
return true
|
||||
} else if xev.type == .ButtonPress {
|
||||
e_out.type = .MOUSE_BUTTON_PRESSED;
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y};
|
||||
zephr_ctx.mouse.pressed = true;
|
||||
e_out.type = .MOUSE_BUTTON_PRESSED
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}
|
||||
zephr_ctx.mouse.pressed = true
|
||||
|
||||
switch (xev.xbutton.button) {
|
||||
case .Button1:
|
||||
e_out.mouse.button = .BUTTON_LEFT;
|
||||
zephr_ctx.mouse.button = .BUTTON_LEFT;
|
||||
e_out.mouse.button = .BUTTON_LEFT
|
||||
zephr_ctx.mouse.button = .BUTTON_LEFT
|
||||
case .Button2:
|
||||
e_out.mouse.button = .BUTTON_MIDDLE;
|
||||
zephr_ctx.mouse.button = .BUTTON_MIDDLE;
|
||||
e_out.mouse.button = .BUTTON_MIDDLE
|
||||
zephr_ctx.mouse.button = .BUTTON_MIDDLE
|
||||
case .Button3:
|
||||
e_out.mouse.button = .BUTTON_RIGHT;
|
||||
zephr_ctx.mouse.button = .BUTTON_RIGHT;
|
||||
e_out.mouse.button = .BUTTON_RIGHT
|
||||
zephr_ctx.mouse.button = .BUTTON_RIGHT
|
||||
case .Button4:
|
||||
e_out.type = .MOUSE_SCROLL;
|
||||
e_out.mouse.scroll_direction = .UP;
|
||||
e_out.type = .MOUSE_SCROLL
|
||||
e_out.mouse.scroll_direction = .UP
|
||||
case .Button5:
|
||||
e_out.type = .MOUSE_SCROLL;
|
||||
e_out.mouse.scroll_direction = .DOWN;
|
||||
e_out.type = .MOUSE_SCROLL
|
||||
e_out.mouse.scroll_direction = .DOWN
|
||||
case cast(x11.MouseButton)8: // Back
|
||||
e_out.mouse.button = .BUTTON_BACK;
|
||||
zephr_ctx.mouse.button = .BUTTON_BACK;
|
||||
e_out.mouse.button = .BUTTON_BACK
|
||||
zephr_ctx.mouse.button = .BUTTON_BACK
|
||||
case cast(x11.MouseButton)9: // Forward
|
||||
e_out.mouse.button = .BUTTON_FORWARD;
|
||||
zephr_ctx.mouse.button = .BUTTON_FORWARD;
|
||||
e_out.mouse.button = .BUTTON_FORWARD
|
||||
zephr_ctx.mouse.button = .BUTTON_FORWARD
|
||||
case:
|
||||
fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button);
|
||||
fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button)
|
||||
}
|
||||
|
||||
return true
|
||||
} else if xev.type == .ButtonRelease {
|
||||
e_out.type = .MOUSE_BUTTON_RELEASED;
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y};
|
||||
zephr_ctx.mouse.released = true;
|
||||
zephr_ctx.mouse.pressed = false;
|
||||
e_out.type = .MOUSE_BUTTON_RELEASED
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}
|
||||
zephr_ctx.mouse.released = true
|
||||
zephr_ctx.mouse.pressed = false
|
||||
|
||||
switch (xev.xbutton.button) {
|
||||
case .Button1:
|
||||
@@ -1090,34 +1112,34 @@ iter_events :: proc(e_out: ^Event) -> bool {
|
||||
case .Button3:
|
||||
e_out.mouse.button = .BUTTON_RIGHT
|
||||
case .Button4:
|
||||
e_out.type = .MOUSE_SCROLL;
|
||||
e_out.mouse.scroll_direction = .UP;
|
||||
e_out.type = .MOUSE_SCROLL
|
||||
e_out.mouse.scroll_direction = .UP
|
||||
case .Button5:
|
||||
e_out.type = .MOUSE_SCROLL;
|
||||
e_out.mouse.scroll_direction = .DOWN;
|
||||
e_out.type = .MOUSE_SCROLL
|
||||
e_out.mouse.scroll_direction = .DOWN
|
||||
case cast(x11.MouseButton)8: // Back
|
||||
e_out.mouse.button = .BUTTON_BACK;
|
||||
zephr_ctx.mouse.button = .BUTTON_BACK;
|
||||
e_out.mouse.button = .BUTTON_BACK
|
||||
zephr_ctx.mouse.button = .BUTTON_BACK
|
||||
case cast(x11.MouseButton)9: // Forward
|
||||
e_out.mouse.button = .BUTTON_FORWARD;
|
||||
zephr_ctx.mouse.button = .BUTTON_FORWARD;
|
||||
e_out.mouse.button = .BUTTON_FORWARD
|
||||
zephr_ctx.mouse.button = .BUTTON_FORWARD
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
} else if xev.type == .MappingNotify {
|
||||
// input device mapping changed
|
||||
if (xev.xmapping.request != .MappingKeyboard) {
|
||||
break;
|
||||
break
|
||||
}
|
||||
x11.XRefreshKeyboardMapping(&xev.xmapping);
|
||||
x11.XRefreshKeyboardMapping(&xev.xmapping)
|
||||
/* x11_keyboard_map_update(); */
|
||||
break;
|
||||
break
|
||||
} else if xev.type == .MotionNotify {
|
||||
e_out.type = .MOUSE_MOVED;
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y};
|
||||
zephr_ctx.mouse.pos = e_out.mouse.pos;
|
||||
e_out.type = .MOUSE_MOVED
|
||||
e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y}
|
||||
zephr_ctx.mouse.pos = e_out.mouse.pos
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,17 +1147,18 @@ iter_events :: proc(e_out: ^Event) -> bool {
|
||||
}
|
||||
|
||||
get_window_size :: proc() -> Vec2 {
|
||||
return zephr_ctx.window.size;
|
||||
return zephr_ctx.window.size
|
||||
}
|
||||
|
||||
toggle_fullscreen :: proc() {
|
||||
x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen);
|
||||
x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen)
|
||||
|
||||
zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen;
|
||||
zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen
|
||||
}
|
||||
|
||||
@private
|
||||
set_cursor :: proc(cursor: Cursor) {
|
||||
zephr_ctx.cursor = cursor;
|
||||
zephr_ctx.cursor = cursor
|
||||
}
|
||||
|
||||
|
||||
@@ -1153,8 +1176,8 @@ fnv_hash32 :: proc(data: []byte, size: u32, hash: u32) -> u32 {
|
||||
hash := hash
|
||||
|
||||
for i in 0..<size {
|
||||
hash ~= cast(u32)data[i];
|
||||
hash *= FNV_HASH32_PRIME;
|
||||
hash ~= cast(u32)data[i]
|
||||
hash *= FNV_HASH32_PRIME
|
||||
}
|
||||
|
||||
return hash
|
||||
@@ -1167,84 +1190,85 @@ x11_mods_to_zephr_mods :: proc(scancode: Scancode, is_press: bool) -> KeyMod {
|
||||
// TODO: figure out a better way of doing this
|
||||
if (is_press) {
|
||||
if (scancode == .LEFT_SHIFT) {
|
||||
mods |= {.LEFT_SHIFT, .SHIFT};
|
||||
mods |= {.LEFT_SHIFT, .SHIFT}
|
||||
}
|
||||
if (scancode == .RIGHT_SHIFT) {
|
||||
mods |= {.RIGHT_SHIFT, .SHIFT};
|
||||
mods |= {.RIGHT_SHIFT, .SHIFT}
|
||||
}
|
||||
if (scancode == .LEFT_CTRL) {
|
||||
mods |= {.LEFT_CTRL, .CTRL};
|
||||
mods |= {.LEFT_CTRL, .CTRL}
|
||||
}
|
||||
if (scancode == .RIGHT_CTRL) {
|
||||
mods |= {.RIGHT_CTRL, .CTRL};
|
||||
mods |= {.RIGHT_CTRL, .CTRL}
|
||||
}
|
||||
if (scancode == .LEFT_ALT) {
|
||||
mods |= {.LEFT_ALT, .ALT};
|
||||
mods |= {.LEFT_ALT, .ALT}
|
||||
}
|
||||
if (scancode == .RIGHT_ALT) {
|
||||
mods |= {.RIGHT_ALT, .ALT};
|
||||
mods |= {.RIGHT_ALT, .ALT}
|
||||
}
|
||||
if (scancode == .LEFT_META) {
|
||||
mods |= {.LEFT_META, .META};
|
||||
mods |= {.LEFT_META, .META}
|
||||
}
|
||||
if (scancode == .RIGHT_META) {
|
||||
mods |= {.RIGHT_META, .META};
|
||||
mods |= {.RIGHT_META, .META}
|
||||
}
|
||||
if (scancode == .CAPS_LOCK) {
|
||||
mods |= {.CAPS_LOCK};
|
||||
mods |= {.CAPS_LOCK}
|
||||
}
|
||||
if (scancode == .NUM_LOCK_OR_CLEAR) {
|
||||
mods |= {.NUM_LOCK};
|
||||
mods |= {.NUM_LOCK}
|
||||
}
|
||||
} else {
|
||||
if (scancode == .LEFT_SHIFT) {
|
||||
mods &= ~{.LEFT_SHIFT};
|
||||
mods &= ~{.LEFT_SHIFT}
|
||||
}
|
||||
if (scancode == .RIGHT_SHIFT) {
|
||||
mods &= ~{.RIGHT_SHIFT};
|
||||
mods &= ~{.RIGHT_SHIFT}
|
||||
}
|
||||
if (!(.RIGHT_SHIFT in mods) && !(.LEFT_SHIFT in mods)) {
|
||||
mods &= ~{.SHIFT};
|
||||
mods &= ~{.SHIFT}
|
||||
}
|
||||
|
||||
if (scancode == .LEFT_CTRL) {
|
||||
mods &= ~{.LEFT_CTRL};
|
||||
mods &= ~{.LEFT_CTRL}
|
||||
}
|
||||
if (scancode == .RIGHT_CTRL) {
|
||||
mods &= ~{.RIGHT_CTRL};
|
||||
mods &= ~{.RIGHT_CTRL}
|
||||
}
|
||||
if (!(.RIGHT_CTRL in mods) && !(.LEFT_CTRL in mods)) {
|
||||
mods &= ~{.CTRL};
|
||||
mods &= ~{.CTRL}
|
||||
}
|
||||
|
||||
if (scancode == .LEFT_ALT) {
|
||||
mods &= ~{.LEFT_ALT};
|
||||
mods &= ~{.LEFT_ALT}
|
||||
}
|
||||
if (scancode == .RIGHT_ALT) {
|
||||
mods &= ~{.RIGHT_ALT};
|
||||
mods &= ~{.RIGHT_ALT}
|
||||
}
|
||||
if (!(.RIGHT_ALT in mods) && !(.LEFT_ALT in mods)) {
|
||||
mods &= ~{.ALT};
|
||||
mods &= ~{.ALT}
|
||||
}
|
||||
|
||||
if (scancode == .LEFT_META) {
|
||||
mods &= ~{.LEFT_META};
|
||||
mods &= ~{.LEFT_META}
|
||||
}
|
||||
if (scancode == .RIGHT_META) {
|
||||
mods &= ~{.RIGHT_META};
|
||||
mods &= ~{.RIGHT_META}
|
||||
}
|
||||
if (!(.RIGHT_META in mods) && !(.LEFT_META in mods)) {
|
||||
mods &= ~{.META};
|
||||
mods &= ~{.META}
|
||||
}
|
||||
|
||||
if (scancode == .CAPS_LOCK) {
|
||||
mods &= ~{.CAPS_LOCK};
|
||||
mods &= ~{.CAPS_LOCK}
|
||||
}
|
||||
if (scancode == .NUM_LOCK_OR_CLEAR) {
|
||||
mods &= ~{.NUM_LOCK};
|
||||
mods &= ~{.NUM_LOCK}
|
||||
}
|
||||
}
|
||||
|
||||
zephr_ctx.keyboard.mods = mods
|
||||
return mods;
|
||||
return mods
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -12,16 +12,16 @@ Mat4 :: matrix[4, 4]f32
|
||||
Color :: struct { r, g, b, a: u8 }
|
||||
|
||||
orthographic_projection_2d :: proc(left, right, bottom, top: f32) -> Mat4 {
|
||||
result := identity();
|
||||
result := identity()
|
||||
|
||||
result[0][0] = 2 / (right - left);
|
||||
result[3][0] = -(right + left) / (right - left);
|
||||
result[1][1] = 2 / (top - bottom);
|
||||
result[3][1] = -(top + bottom) / (top - bottom);
|
||||
result[2][2] = -1;
|
||||
result[3][3] = 1;
|
||||
result[0][0] = 2 / (right - left)
|
||||
result[3][0] = -(right + left) / (right - left)
|
||||
result[1][1] = 2 / (top - bottom)
|
||||
result[3][1] = -(top + bottom) / (top - bottom)
|
||||
result[2][2] = -1
|
||||
result[3][3] = 1
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
identity :: proc() -> Mat4 {
|
||||
@@ -83,9 +83,9 @@ mult_color :: proc(color: Color, scalar: f32) -> Color {
|
||||
}
|
||||
|
||||
hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color {
|
||||
c := v * s;
|
||||
x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1)));
|
||||
m := v - c;
|
||||
c := v * s
|
||||
x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1)))
|
||||
m := v - c
|
||||
|
||||
r, g, b: f32
|
||||
|
||||
@@ -112,30 +112,30 @@ hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color {
|
||||
} else {
|
||||
r = c
|
||||
g = 0
|
||||
b = x;
|
||||
b = x
|
||||
}
|
||||
|
||||
return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255};
|
||||
return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255}
|
||||
}
|
||||
|
||||
determine_color_contrast :: proc(bg: Color) -> Color {
|
||||
white_contrast := get_contrast(bg, COLOR_WHITE);
|
||||
black_contrast := get_contrast(bg, COLOR_BLACK);
|
||||
white_contrast := get_contrast(bg, COLOR_WHITE)
|
||||
black_contrast := get_contrast(bg, COLOR_BLACK)
|
||||
|
||||
return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK;
|
||||
return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK
|
||||
}
|
||||
|
||||
@private
|
||||
get_srgb :: proc(component: f32) -> f32 {
|
||||
return (component / 255 <= 0.03928
|
||||
? component / 255 / 12.92
|
||||
: math.pow((component / 255 + 0.055) / 1.055, 2.4));
|
||||
: math.pow((component / 255 + 0.055) / 1.055, 2.4))
|
||||
}
|
||||
|
||||
get_luminance :: proc(color: Color) -> f32 {
|
||||
return ((0.2126 * get_srgb(cast(f32)color.r)) +
|
||||
(0.7152 * get_srgb(cast(f32)color.g)) +
|
||||
(0.0722 * get_srgb(cast(f32)color.b))) / 255;
|
||||
(0.0722 * get_srgb(cast(f32)color.b))) / 255
|
||||
}
|
||||
|
||||
@private
|
||||
@@ -143,5 +143,5 @@ get_contrast :: proc(fg: Color, bg: Color) -> f32 {
|
||||
l1 := get_luminance(fg)
|
||||
l2 := get_luminance(bg)
|
||||
|
||||
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05);
|
||||
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user