2024-10-14 12:54:53 -05:00
|
|
|
.{math, memory, dt} := @use("../../stn/src/lib.hb");
|
2024-10-26 03:23:28 -05:00
|
|
|
.{Color, text} := @use("lib.hb");
|
2024-11-05 19:47:22 -06:00
|
|
|
.{get_glyph, get_glyph_unicode, Font, UNC_TABLE_SIZE} := text;
|
2024-10-12 15:39:09 -05:00
|
|
|
.{Vec2} := math
|
2024-09-13 16:41:31 -05:00
|
|
|
|
2024-11-03 16:31:53 -06:00
|
|
|
// safety: don't use before init() or you will get a memory access violation
|
2024-11-08 07:47:24 -06:00
|
|
|
framebuffer := memory.dangling(Color)
|
2024-11-03 16:31:53 -06:00
|
|
|
|
2024-10-17 09:31:42 -05:00
|
|
|
Surface := struct {
|
|
|
|
buf: ^Color,
|
2024-10-25 10:37:38 -05:00
|
|
|
width: uint,
|
|
|
|
height: uint,
|
2024-10-17 09:31:42 -05:00
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
new_surface := fn(width: uint, height: uint): Surface {
|
2024-10-17 09:31:42 -05:00
|
|
|
return .(
|
|
|
|
@inline(memory.alloc, Color, width * height),
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
surface_from_ptr := fn(ptr: ^Color, width: uint, height: uint): Surface {
|
2024-10-17 09:31:42 -05:00
|
|
|
return .(
|
|
|
|
ptr,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
clone_surface := fn(surface: ^Surface): Surface {
|
|
|
|
new := new_surface(surface.width, surface.height)
|
|
|
|
@inline(memory.copy, Color, surface.buf, new.buf, @intcast(surface.width * surface.height))
|
|
|
|
return new
|
|
|
|
}
|
|
|
|
|
2024-10-23 15:22:28 -05:00
|
|
|
// ! is broken, check memory.free function
|
|
|
|
free_surface := fn(surface: Surface): void {
|
|
|
|
return @inline(memory.free, Color, surface.buf, @intcast(surface.width * surface.height), false)
|
2024-10-17 09:31:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
init := fn(doublebuffer: bool): Surface {
|
2024-10-14 19:24:29 -05:00
|
|
|
framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0")
|
2024-10-25 10:37:38 -05:00
|
|
|
width := dt.get(uint, "framebuffer/fb0/width\0")
|
|
|
|
height := dt.get(uint, "framebuffer/fb0/height\0")
|
2024-10-17 09:31:42 -05:00
|
|
|
if doublebuffer {
|
2024-10-14 19:24:29 -05:00
|
|
|
return new_surface(width, height)
|
2024-09-13 16:41:31 -05:00
|
|
|
} else {
|
2024-10-14 19:24:29 -05:00
|
|
|
return .(framebuffer, width, height)
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-14 19:24:29 -05:00
|
|
|
clear := fn(surface: Surface, color: Color): void {
|
2024-10-25 10:37:38 -05:00
|
|
|
return @inline(memory.set, Color, &color, surface.buf, surface.width * surface.height)
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
|
2024-10-14 19:24:29 -05:00
|
|
|
sync := fn(surface: Surface): void {
|
2024-10-23 15:22:28 -05:00
|
|
|
// vague safety
|
|
|
|
if surface.buf == framebuffer {
|
|
|
|
return
|
|
|
|
}
|
2024-10-14 19:24:29 -05:00
|
|
|
return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height))
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
index := fn(surface: Surface, x: uint, y: uint): uint {
|
2024-10-14 19:24:29 -05:00
|
|
|
return x + surface.width * y
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
indexptr := fn(surface: Surface, x: uint, y: uint): ^Color {
|
2024-10-19 09:54:19 -05:00
|
|
|
return surface.buf + @inline(index, surface, x, y)
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_pixel := fn(surface: Surface, pos: Vec2(uint), color: Color): void {
|
2024-10-19 09:54:19 -05:00
|
|
|
*@inline(indexptr, surface, pos.x, pos.y) = color
|
2024-09-13 16:41:31 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
|
2024-10-19 09:54:19 -05:00
|
|
|
top_start_idx := @inline(indexptr, surface, pos.x, pos.y)
|
|
|
|
bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1)
|
2024-10-15 15:11:06 -05:00
|
|
|
rows_to_fill := tr.y
|
|
|
|
|
|
|
|
loop if rows_to_fill <= 1 break else {
|
2024-11-03 16:31:53 -06:00
|
|
|
// inline is broked
|
|
|
|
memory.set(Color, &color, top_start_idx, @bitcast(tr.x))
|
|
|
|
memory.set(Color, &color, bottom_start_idx, @bitcast(tr.x))
|
2024-10-14 19:24:29 -05:00
|
|
|
|
|
|
|
top_start_idx += surface.width
|
|
|
|
bottom_start_idx -= surface.width
|
2024-10-15 15:11:06 -05:00
|
|
|
rows_to_fill -= 2
|
2024-10-14 19:24:29 -05:00
|
|
|
}
|
2024-10-13 19:31:23 -05:00
|
|
|
|
2024-10-15 15:11:06 -05:00
|
|
|
if rows_to_fill == 1 {
|
|
|
|
@inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x))
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
2024-10-13 19:31:23 -05:00
|
|
|
|
2024-09-13 16:41:31 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
|
2024-10-19 09:54:19 -05:00
|
|
|
start_idx := @inline(indexptr, surface, pos.x, pos.y)
|
|
|
|
end_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y)
|
|
|
|
right_start_idx := @inline(indexptr, surface, pos.x + tr.x, pos.y)
|
2024-10-13 19:31:23 -05:00
|
|
|
|
|
|
|
loop if start_idx > end_idx break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
*start_idx = color;
|
|
|
|
*right_start_idx = color
|
2024-10-14 19:24:29 -05:00
|
|
|
start_idx += surface.width
|
|
|
|
right_start_idx += surface.width
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
2024-10-13 19:31:23 -05:00
|
|
|
|
2024-10-19 09:54:19 -05:00
|
|
|
@inline(memory.set, Color, &color, @inline(indexptr, surface, pos.x, pos.y), @bitcast(tr.x + 1))
|
|
|
|
@inline(memory.set, Color, &color, @inline(indexptr, surface, pos.x, pos.y + tr.y), @bitcast(tr.x + 1))
|
2024-10-13 19:31:23 -05:00
|
|
|
|
2024-09-13 16:41:31 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_line_low := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
|
|
|
|
dx := @as(int, @bitcast(p1.x - p0.x))
|
|
|
|
dy := @as(int, @bitcast(p1.y - p0.y))
|
2024-09-13 16:41:31 -05:00
|
|
|
yi := 1
|
|
|
|
if dy < 0 {
|
2024-09-14 05:26:32 -05:00
|
|
|
yi = -1
|
|
|
|
dy = -dy
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
2024-10-25 10:37:38 -05:00
|
|
|
D := @as(int, 2) * dy - dx
|
2024-09-13 16:41:31 -05:00
|
|
|
y := p0.y
|
|
|
|
x := p0.x
|
|
|
|
loop if x == p1.x break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
*@inline(indexptr, surface, x, y) = color
|
2024-09-13 16:41:31 -05:00
|
|
|
if D > 0 {
|
|
|
|
y += yi
|
|
|
|
D += 2 * (dy - dx)
|
|
|
|
} else {
|
|
|
|
D += 2 * dy
|
|
|
|
}
|
|
|
|
x += 1
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_line_high := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
|
|
|
|
dx := @as(int, @bitcast(p1.x - p0.x))
|
|
|
|
dy := @as(int, @bitcast(p1.y - p0.y))
|
2024-09-13 16:41:31 -05:00
|
|
|
xi := 1
|
|
|
|
if dy < 0 {
|
2024-09-14 05:26:32 -05:00
|
|
|
xi = -1
|
|
|
|
dx = -dx
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
2024-10-25 10:37:38 -05:00
|
|
|
D := @as(int, 2) * dx - dy
|
2024-09-13 16:41:31 -05:00
|
|
|
x := p0.x
|
|
|
|
y := p0.y
|
|
|
|
loop if y == p1.y break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
*@inline(indexptr, surface, x, y) = color
|
2024-09-13 16:41:31 -05:00
|
|
|
if D > 0 {
|
|
|
|
x += xi
|
|
|
|
D += 2 * (dx - dy)
|
|
|
|
} else {
|
|
|
|
D += 2 * dx
|
|
|
|
}
|
|
|
|
y += 1
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_line := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
|
|
|
|
if math.abs(uint, p1.y - p0.y) < math.abs(uint, p1.x - p0.x) {
|
2024-09-13 16:41:31 -05:00
|
|
|
if p0.x > p1.x {
|
2024-10-14 19:24:29 -05:00
|
|
|
@inline(put_line_low, surface, p1, p0, color)
|
2024-09-13 16:41:31 -05:00
|
|
|
} else {
|
2024-10-14 19:24:29 -05:00
|
|
|
@inline(put_line_low, surface, p0, p1, color)
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if p0.y > p1.y {
|
2024-10-14 19:24:29 -05:00
|
|
|
@inline(put_line_high, surface, p1, p0, color)
|
2024-09-13 16:41:31 -05:00
|
|
|
} else {
|
2024-10-14 19:24:29 -05:00
|
|
|
@inline(put_line_high, surface, p0, p1, color)
|
2024-09-13 16:41:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-25 10:37:38 -05:00
|
|
|
put_surface := fn(surface: Surface, top: Surface, pos: Vec2(uint), flip_v: bool): void {
|
2024-10-19 09:54:19 -05:00
|
|
|
top_start_idx := @inline(indexptr, surface, pos.x, pos.y)
|
|
|
|
bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + top.height - 1)
|
2024-10-15 15:11:06 -05:00
|
|
|
rows_to_copy := top.height
|
|
|
|
top_cursor := top.buf
|
|
|
|
bottom_cursor := top.buf + top.width * (top.height - 1)
|
|
|
|
|
|
|
|
loop if rows_to_copy <= 1 break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
if flip_v {
|
|
|
|
@inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(top.width))
|
|
|
|
@inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(top.width))
|
|
|
|
} else {
|
|
|
|
@inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width))
|
|
|
|
@inline(memory.copy, Color, bottom_cursor, bottom_start_idx, @bitcast(top.width))
|
|
|
|
}
|
2024-10-14 19:24:29 -05:00
|
|
|
|
|
|
|
top_start_idx += surface.width
|
|
|
|
bottom_start_idx -= surface.width
|
|
|
|
top_cursor += top.width
|
|
|
|
bottom_cursor -= top.width
|
2024-10-15 15:11:06 -05:00
|
|
|
rows_to_copy -= 2
|
2024-10-14 19:24:29 -05:00
|
|
|
}
|
2024-10-13 19:31:23 -05:00
|
|
|
|
2024-10-15 15:11:06 -05:00
|
|
|
if rows_to_copy == 1 {
|
|
|
|
@inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width))
|
2024-10-13 17:38:43 -05:00
|
|
|
}
|
2024-10-14 19:24:29 -05:00
|
|
|
|
2024-10-13 17:38:43 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// peony-made
|
2024-10-25 10:37:38 -05:00
|
|
|
put_trirect := fn(surface: Surface, pos: Vec2(uint), size: Vec2(int), color0: Color, color1: Color): void {
|
2024-10-13 17:38:43 -05:00
|
|
|
step := Vec2(int).(1, 1)
|
|
|
|
if size.x < 0 {
|
|
|
|
step.x = -1
|
|
|
|
}
|
|
|
|
if size.y < 0 {
|
2024-10-25 10:37:38 -05:00
|
|
|
step.y /= @bitcast(size.x)
|
2024-10-13 17:38:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
start_y := pos.y
|
2024-10-25 10:37:38 -05:00
|
|
|
target := pos + @bitcast(size)
|
2024-10-13 17:38:43 -05:00
|
|
|
|
|
|
|
loop if pos.x == target.x break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
@inline(put_vline, surface, pos.x, pos.y, target.y, color0)
|
2024-10-14 19:24:29 -05:00
|
|
|
@inline(put_vline, surface, pos.x, pos.y, start_y, color1)
|
2024-10-25 10:37:38 -05:00
|
|
|
pos += @bitcast(step)
|
2024-10-13 17:38:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// peony-made
|
2024-10-25 10:37:38 -05:00
|
|
|
put_vline := fn(surface: Surface, x: uint, y0: uint, y1: uint, color: Color): void {
|
2024-10-13 17:38:43 -05:00
|
|
|
if y1 < y0 {
|
|
|
|
tmp := y0
|
|
|
|
y0 = y1
|
|
|
|
y1 = tmp
|
|
|
|
}
|
|
|
|
y := y0
|
|
|
|
|
|
|
|
loop if y == y1 break else {
|
2024-10-19 09:54:19 -05:00
|
|
|
*@inline(indexptr, surface, x, y) = color
|
2024-10-13 17:38:43 -05:00
|
|
|
y += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// peony-made
|
2024-10-25 10:37:38 -05:00
|
|
|
put_hline := fn(surface: Surface, y: uint, x0: uint, x1: uint, color: Color): void {
|
2024-10-13 17:38:43 -05:00
|
|
|
if x1 < x0 {
|
|
|
|
tmp := x0
|
|
|
|
x0 = x1
|
|
|
|
x1 = tmp
|
|
|
|
}
|
2024-10-19 09:54:19 -05:00
|
|
|
@inline(memory.set, Color, &color, @inline(indexptr, surface, x0, y), @bitcast(x1 - x0 - 1))
|
2024-10-13 17:38:43 -05:00
|
|
|
|
2024-10-26 03:23:28 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: ^u8): void {
|
|
|
|
cursor := Vec2(uint).(pos.x, pos.y)
|
|
|
|
current_char := str
|
|
|
|
|
|
|
|
loop if *current_char == 0 break else {
|
2024-11-08 07:47:24 -06:00
|
|
|
glyph_data := memory.dangling(u8)
|
2024-11-05 19:47:22 -06:00
|
|
|
|
|
|
|
if font.unicode != null {
|
|
|
|
code_point := @as(uint, 0)
|
|
|
|
first_byte := *current_char
|
|
|
|
num_bytes := 1
|
|
|
|
|
|
|
|
if (first_byte & 0x80) == 0 {
|
|
|
|
code_point = first_byte
|
|
|
|
} else if (first_byte & 0xE0) == 0xC0 {
|
|
|
|
num_bytes = 2
|
|
|
|
code_point = first_byte & 0x1F
|
|
|
|
} else if (first_byte & 0xF0) == 0xE0 {
|
|
|
|
num_bytes = 3
|
|
|
|
code_point = first_byte & 0xF
|
|
|
|
} else if (first_byte & 0xF8) == 0xF0 {
|
|
|
|
// handle later
|
|
|
|
current_char += 1
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
current_char += 1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
valid_sequence := true
|
|
|
|
i := 1
|
|
|
|
loop if i >= num_bytes break else {
|
|
|
|
current_char += 1
|
|
|
|
// have to check twice due to odd compiler bug...?
|
|
|
|
if *current_char == 0 | (*current_char & 0xC0) != 0x80 {
|
|
|
|
valid_sequence = false
|
|
|
|
}
|
|
|
|
if valid_sequence == false {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
code_point = code_point << 6 | *current_char & 0x3F
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if valid_sequence == false {
|
|
|
|
current_char += 1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
current_char += 1
|
|
|
|
|
|
|
|
if code_point == 10 {
|
|
|
|
cursor.x = pos.x
|
|
|
|
cursor.y += font.height + font.line_gap
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if code_point < UNC_TABLE_SIZE {
|
|
|
|
glyph_index := *(font.unicode + code_point)
|
|
|
|
|
|
|
|
if glyph_index == 0xFFFF {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
glyph_data = font.data + glyph_index * font.bytes_per_glyph
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if *current_char > font.num_glyphs {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
glyph_data = @inline(get_glyph, font, *current_char)
|
|
|
|
if *current_char == 10 {
|
|
|
|
cursor.x = pos.x
|
|
|
|
cursor.y += font.height + font.line_gap
|
|
|
|
current_char += 1
|
|
|
|
continue
|
|
|
|
}
|
2024-10-26 03:23:28 -05:00
|
|
|
current_char += 1
|
|
|
|
}
|
|
|
|
|
2024-11-05 19:47:22 -06:00
|
|
|
if cursor.y + font.height > surface.height break
|
2024-10-26 03:23:28 -05:00
|
|
|
|
|
|
|
if cursor.x % surface.width + font.width >= surface.width {
|
|
|
|
cursor.x = pos.x
|
|
|
|
cursor.y += font.height + font.line_gap
|
|
|
|
}
|
|
|
|
|
|
|
|
dest := @inline(indexptr, surface, cursor.x, cursor.y)
|
|
|
|
rows_remaining := font.height
|
|
|
|
|
|
|
|
loop if rows_remaining == 0 break else {
|
|
|
|
byte := *glyph_data
|
|
|
|
pixel_dest := dest
|
|
|
|
mask := @as(u8, 0x80)
|
|
|
|
bits_remaining := font.width
|
|
|
|
|
|
|
|
loop if bits_remaining == 0 break else {
|
|
|
|
if (byte & mask) != 0 {
|
|
|
|
*pixel_dest = color
|
|
|
|
}
|
|
|
|
pixel_dest += 1
|
|
|
|
mask >>= 1
|
2024-11-05 19:47:22 -06:00
|
|
|
if mask == 0 & bits_remaining > 0 {
|
2024-10-26 03:23:28 -05:00
|
|
|
glyph_data += 1
|
|
|
|
byte = *glyph_data
|
|
|
|
mask = 0x80
|
|
|
|
}
|
|
|
|
bits_remaining -= 1
|
|
|
|
}
|
|
|
|
|
2024-11-05 19:47:22 -06:00
|
|
|
if mask != 0x80 {
|
|
|
|
glyph_data += 1
|
|
|
|
}
|
2024-10-26 03:23:28 -05:00
|
|
|
dest += surface.width
|
|
|
|
rows_remaining -= 1
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor.x += font.width + font.char_gap
|
|
|
|
}
|
|
|
|
|
2024-09-13 16:41:31 -05:00
|
|
|
return
|
|
|
|
}
|