.{math, memory, dt} := @use("../../stn/src/lib.hb"); .{Color} := @use("lib.hb"); .{Vec2} := math Surface := struct { buf: ^Color, width: int, height: int, } new_surface := fn(width: int, height: int): Surface { return .( @inline(memory.alloc, Color, width * height), width, height, ) } surface_from_ptr := fn(ptr: ^Color, width: int, height: int): Surface { 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 } free_surface := fn(surface: ^Surface): void { // todo: depends on stn.memory.free return } framebuffer := @as(^Color, idk) init := fn(doublebuffer: bool): Surface { framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0") width := dt.get(int, "framebuffer/fb0/width\0") height := dt.get(int, "framebuffer/fb0/height\0") if doublebuffer { return new_surface(width, height) } else { return .(framebuffer, width, height) } } clear := fn(surface: Surface, color: Color): void { return @inline(memory.set, Color, &color, surface.buf, @bitcast(surface.width * surface.height)) } sync := fn(surface: Surface): void { return @inline(memory.copy, Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height)) } index := fn(surface: Surface, x: int, y: int): int { return x + surface.width * y } indexptr := fn(surface: Surface, x: int, y: int): ^Color { return surface.buf + @inline(index, surface, x, y) } put_pixel := fn(surface: Surface, pos: Vec2(int), color: Color): void { *@inline(indexptr, surface, pos.x, pos.y) = color return } put_filled_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { top_start_idx := @inline(indexptr, surface, pos.x, pos.y) bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1) rows_to_fill := tr.y loop if rows_to_fill <= 1 break else { @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) @inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x)) top_start_idx += surface.width bottom_start_idx -= surface.width rows_to_fill -= 2 } if rows_to_fill == 1 { @inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x)) } return } put_rect := fn(surface: Surface, pos: Vec2(int), tr: Vec2(int), color: Color): void { 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) loop if start_idx > end_idx break else { *start_idx = color; *right_start_idx = color start_idx += surface.width right_start_idx += surface.width } @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)) return } put_line_low := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { dx := p1.x - p0.x dy := p1.y - p0.y yi := 1 if dy < 0 { yi = -1 dy = -dy } D := 2 * dy - dx y := p0.y x := p0.x loop if x == p1.x break else { *@inline(indexptr, surface, x, y) = color if D > 0 { y += yi D += 2 * (dy - dx) } else { D += 2 * dy } x += 1 } return } put_line_high := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { dx := p1.x - p0.x dy := p1.y - p0.y xi := 1 if dy < 0 { xi = -1 dx = -dx } D := 2 * dx - dy x := p0.x y := p0.y loop if y == p1.y break else { *@inline(indexptr, surface, x, y) = color if D > 0 { x += xi D += 2 * (dx - dy) } else { D += 2 * dx } y += 1 } return } put_line := fn(surface: Surface, p0: Vec2(int), p1: Vec2(int), color: Color): void { if math.abs(int, p1.y - p0.y) < math.abs(int, p1.x - p0.x) { if p0.x > p1.x { @inline(put_line_low, surface, p1, p0, color) } else { @inline(put_line_low, surface, p0, p1, color) } } else { if p0.y > p1.y { @inline(put_line_high, surface, p1, p0, color) } else { @inline(put_line_high, surface, p0, p1, color) } } return } put_surface := fn(surface: Surface, top: Surface, pos: Vec2(int), flip_v: bool): void { top_start_idx := @inline(indexptr, surface, pos.x, pos.y) bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + top.height - 1) 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 { 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)) } top_start_idx += surface.width bottom_start_idx -= surface.width top_cursor += top.width bottom_cursor -= top.width rows_to_copy -= 2 } if rows_to_copy == 1 { @inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(top.width)) } return } // peony-made put_trirect := fn(surface: Surface, pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void { step := Vec2(int).(1, 1) if size.x < 0 { step.x = -1 } if size.y < 0 { step.y = size.y / size.x } start_y := pos.y target := pos + size loop if pos.x == target.x break else { @inline(put_vline, surface, pos.x, pos.y, target.y, color0) @inline(put_vline, surface, pos.x, pos.y, start_y, color1) pos += step } return } // peony-made put_vline := fn(surface: Surface, x: int, y0: int, y1: int, color: Color): void { if y1 < y0 { tmp := y0 y0 = y1 y1 = tmp } y := y0 loop if y == y1 break else { *@inline(indexptr, surface, x, y) = color y += 1 } return } // peony-made put_hline := fn(surface: Surface, y: int, x0: int, x1: int, color: Color): void { if x1 < x0 { tmp := x0 x0 = x1 x1 = tmp } @inline(memory.set, Color, &color, @inline(indexptr, surface, x0, y), @bitcast(x1 - x0 - 1)) return }