ableos/sysdata/libraries/render/src/software.hb

472 lines
11 KiB
Plaintext
Raw Normal View History

.{math, memory, dt} := @use("../../stn/src/lib.hb");
2024-10-26 03:23:28 -05:00
.{Color, text} := @use("lib.hb");
.{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,
size: 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 .(
memory.alloc(Color, width * height),
2024-10-17 09:31:42 -05:00
width,
height,
width * height,
2024-10-17 09:31:42 -05:00
)
}
clone_surface := fn(surface: ^Surface): Surface {
new := new_surface(surface.width, surface.height)
memory.copy(Color, surface.buf, new.buf, @intcast(surface.size))
2024-10-17 09:31:42 -05:00
return new
}
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 {
return .(framebuffer, width, height, width * height)
2024-09-13 16:41:31 -05:00
}
}
$clear := fn(surface: Surface, color: Color): void {
memory.set(Color, &color, surface.buf, surface.width * surface.height)
2024-09-13 16:41:31 -05:00
}
$sync := fn(surface: Surface): void {
memory.copy(Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height))
2024-09-13 16:41:31 -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
}
$indexptr := fn(surface: Surface, x: uint, y: uint): ^Color {
return surface.buf + index(surface, x, y)
}
$put_pixel := fn(surface: Surface, pos: Vec2(uint), color: Color): void {
return *indexptr(surface, pos.x, pos.y) = color
2024-09-13 16:41:31 -05:00
}
2024-10-25 10:37:38 -05:00
put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
top_start_idx := indexptr(surface, pos.x, pos.y)
bottom_start_idx := indexptr(surface, pos.x, pos.y + tr.y - 1)
rows_to_fill := tr.y
loop if rows_to_fill <= 1 break else {
memory.set(Color, &color, top_start_idx, tr.x)
memory.set(Color, &color, bottom_start_idx, tr.x)
2024-10-14 19:24:29 -05:00
top_start_idx += surface.width
bottom_start_idx -= surface.width
rows_to_fill -= 2
2024-10-14 19:24:29 -05:00
}
2024-10-13 19:31:23 -05:00
if rows_to_fill == 1 {
memory.set(Color, &color, top_start_idx, 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 {
start_idx := indexptr(surface, pos.x, pos.y)
end_idx := indexptr(surface, pos.x, pos.y + tr.y)
right_start_idx := indexptr(surface, pos.x + tr.x, pos.y)
2024-10-13 19:31:23 -05:00
loop if start_idx > end_idx break else {
*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
memory.set(Color, &color, indexptr(surface, pos.x, pos.y), @bitcast(tr.x + 1))
memory.set(Color, &color, 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 {
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 {
*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 {
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 {
*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-11-10 05:48:44 -06:00
src_top_cursor := top.buf
src_bottom_cursor := top.buf + top.width * (top.height - 1)
dst_top_idx := indexptr(surface, pos.x, pos.y)
dst_bottom_idx := indexptr(surface, pos.x, pos.y + top.height - 1)
2024-11-10 05:48:44 -06:00
dst_increment := surface.width
if flip_v {
dst_increment = -dst_increment
tmp := dst_top_idx
dst_top_idx = dst_bottom_idx
dst_bottom_idx = tmp
}
rows_to_copy := top.height
loop if rows_to_copy <= 1 break else {
memory.copy(Color, src_top_cursor, dst_top_idx, top.width)
memory.copy(Color, src_bottom_cursor, dst_bottom_idx, top.width)
2024-10-14 19:24:29 -05:00
2024-11-10 05:48:44 -06:00
dst_top_idx += dst_increment
dst_bottom_idx -= dst_increment
src_top_cursor += top.width
src_bottom_cursor -= top.width
rows_to_copy -= 2
2024-10-14 19:24:29 -05:00
}
2024-10-13 19:31:23 -05:00
if rows_to_copy == 1 {
memory.copy(Color, src_top_cursor, dst_top_idx, top.width)
}
2024-10-14 19:24:29 -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 {
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)
}
start_y := pos.y
2024-10-25 10:37:38 -05:00
target := pos + @bitcast(size)
loop if pos.x == target.x break else {
@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)
}
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 {
if y1 < y0 {
tmp := y0
y0 = y1
y1 = tmp
}
y := y0
loop if y == y1 break else {
*indexptr(surface, x, y) = color
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 {
if x1 < x0 {
tmp := x0
x0 = x1
x1 = tmp
}
// x0 = math.min(x0, x1)
memory.set(Color, &color, indexptr(surface, x0, y), @bitcast(x1 - x0 - 1))
2024-10-26 03:23:28 -05:00
return
}
put_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius);
*indexptr(surface, pos.x + radius, pos.y) = color;
*indexptr(surface, pos.x - radius, pos.y) = color;
*indexptr(surface, pos.x, pos.y + radius) = color;
*indexptr(surface, pos.x, pos.y - radius) = color
loop if y < x break else {
x += 1
if error > 0 {
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
};
*indexptr(surface, pos.x + x, pos.y + y) = color;
*indexptr(surface, pos.x + y, pos.y + x) = color;
*indexptr(surface, pos.x - x, pos.y + y) = color;
*indexptr(surface, pos.x - y, pos.y + x) = color;
*indexptr(surface, pos.x + x, pos.y - y) = color;
*indexptr(surface, pos.x + y, pos.y - x) = color;
*indexptr(surface, pos.x - x, pos.y - y) = color;
*indexptr(surface, pos.x - y, pos.y - x) = color
}
return
}
put_filled_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius)
put_hline(surface, pos.y - x, pos.x - radius, pos.x + radius, color);
*indexptr(surface, pos.x, pos.y + radius) = color;
*indexptr(surface, pos.x, pos.y - radius) = color
loop if y < x break else {
x += 1
if error > 0 {
put_hline(surface, pos.y + y, pos.x - x, pos.x + x, color)
put_hline(surface, pos.y - y, pos.x - x, pos.x + x, color)
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
}
put_hline(surface, pos.y + x, pos.x - y, pos.x + y, color)
put_hline(surface, pos.y - x, pos.x - y, pos.x + y, color)
}
return
}
put_textured_circle := fn(surface: Surface, source: Surface, source_pos: Vec2(uint), pos: Vec2(uint), radius: uint): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius)
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y), indexptr(surface, pos.x - y, pos.y), 2 * y);
*indexptr(surface, pos.x, pos.y + y) = *indexptr(source, source_pos.x, source_pos.y + y);
*indexptr(surface, pos.x, pos.y - y) = *indexptr(source, source_pos.x, source_pos.y - y)
loop if y < x break else {
x += 1
if error > 0 {
memory.copy(Color, indexptr(source, source_pos.x - x, source_pos.y + y), indexptr(surface, pos.x - x, pos.y + y), 2 * x)
memory.copy(Color, indexptr(source, source_pos.x - x, source_pos.y - y), indexptr(surface, pos.x - x, pos.y - y), 2 * x)
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
}
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y + x), indexptr(surface, pos.x - y, pos.y + x), 2 * y)
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y - x), indexptr(surface, pos.x - y, pos.y - x), 2 * y)
}
return
}
2024-11-10 05:48:44 -06:00
utf8_len_table := [u8].(0, 0, 2, 3)
2024-10-26 03:23:28 -05:00
put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: ^u8): void {
cursor := Vec2(uint).(pos.x, pos.y)
2024-11-10 05:48:44 -06:00
max_y := surface.height - font.height
next_line_y := font.height + font.line_gap
char_advance := font.width + font.char_gap
surface_width := surface.width
loop if *str == 0 break else {
if cursor.y > max_y break
glyph_data := @as(^u8, idk)
code_point := @as(uint, 0)
if (*str & 0x80) == 0 {
if *str == 10 {
cursor.x = pos.x
cursor.y += next_line_y
str += 1
continue
2024-11-10 05:48:44 -06:00
}
if font.unicode == null {
if *str > font.num_glyphs {
str += 1
continue
}
glyph_data = get_glyph(font, *str)
} else {
2024-11-10 05:48:44 -06:00
if *str < UNC_TABLE_SIZE {
glyph_index := *(font.unicode + *str)
if glyph_index == 0xFFFF {
str += 1
continue
}
glyph_data = font.data + glyph_index * font.bytes_per_glyph
} else {
str += 1
continue
}
}
str += 1
} else if font.unicode != null {
first_byte := *str
num_bytes := @as(uint, 0)
num_bytes = utf8_len_table[first_byte >> 5 & 0x3]
if num_bytes == 0 {
str += 1
continue
}
2024-11-10 05:48:44 -06:00
code_point = first_byte & (0x7F >> num_bytes | 0x1F)
valid_sequence := true
2024-11-10 05:48:44 -06:00
bytes_processed := 1
loop if bytes_processed >= num_bytes break else {
str += 1
if *str == 0 | (*str & 0xC0) != 0x80 {
valid_sequence = false
}
if valid_sequence == false {
break
}
2024-11-10 05:48:44 -06:00
code_point = code_point << 6 | *str & 0x3F
bytes_processed += 1
}
if valid_sequence == false {
2024-11-10 05:48:44 -06:00
str += 1
continue
}
2024-11-10 05:48:44 -06:00
str += 1
if code_point == 10 {
cursor.x = pos.x
2024-11-10 05:48:44 -06:00
cursor.y += next_line_y
continue
}
2024-11-10 05:48:44 -06:00
if code_point >= UNC_TABLE_SIZE {
continue
}
2024-11-10 05:48:44 -06:00
glyph_index := *(font.unicode + code_point)
if glyph_index == 0xFFFF {
continue
}
2024-11-10 05:48:44 -06:00
glyph_data = font.data + glyph_index * font.bytes_per_glyph
2024-10-26 03:23:28 -05:00
}
2024-11-10 05:48:44 -06:00
if cursor.x + font.width >= surface_width {
2024-10-26 03:23:28 -05:00
cursor.x = pos.x
2024-11-10 05:48:44 -06:00
cursor.y += next_line_y
2024-10-26 03:23:28 -05:00
}
dest := indexptr(surface, cursor.x, cursor.y)
2024-11-10 05:48:44 -06:00
rows := font.height
2024-10-26 03:23:28 -05:00
2024-11-10 05:48:44 -06:00
loop if rows == 0 break else {
2024-10-26 03:23:28 -05:00
byte := *glyph_data
pixel_dest := dest
mask := @as(u8, 0x80)
2024-11-10 05:48:44 -06:00
bits := font.width
2024-10-26 03:23:28 -05:00
2024-11-10 05:48:44 -06:00
loop if bits == 0 break else {
2024-10-26 03:23:28 -05:00
if (byte & mask) != 0 {
*pixel_dest = color
}
pixel_dest += 1
mask >>= 1
2024-11-10 05:48:44 -06:00
if mask == 0 & bits > 0 {
2024-10-26 03:23:28 -05:00
glyph_data += 1
byte = *glyph_data
mask = 0x80
}
2024-11-10 05:48:44 -06:00
bits -= 1
2024-10-26 03:23:28 -05:00
}
if mask != 0x80 {
glyph_data += 1
}
2024-11-10 05:48:44 -06:00
dest += surface_width
rows -= 1
2024-10-26 03:23:28 -05:00
}
2024-11-10 05:48:44 -06:00
cursor.x += char_advance
2024-10-26 03:23:28 -05:00
}
2024-09-13 16:41:31 -05:00
return
}