diff options
Diffstat (limited to 'src/estd/graphics/root.zig')
| -rw-r--r-- | src/estd/graphics/root.zig | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/estd/graphics/root.zig b/src/estd/graphics/root.zig new file mode 100644 index 0000000..da09450 --- /dev/null +++ b/src/estd/graphics/root.zig @@ -0,0 +1,197 @@ +const std = @import("std"); + +pub const Pixel = packed struct(u32) { + blue: u8, + green: u8, + red: u8, + _padding: u8 = 0x0, +}; + +pub const Canvas = struct { + const Self = @This(); + + width: u32, + height: u32, + buffer: []volatile Pixel, + + pub fn fill(self: *const Canvas, pixel: Pixel) void { + @memset(self.buffer, pixel); + } + + fn gaussian_mean(values: [9]u32) u32 { + return (values[0] + values[1] * 2 + values[2] + + values[3] * 2 + values[4] * 8 + values[5] * 2 + + values[6] + values[7] * 2 + values[8]) / 20; + } + + pub fn set_with_anti_aliasing(self: *const Canvas, x: u32, y: u32) void { + var neighbors_red: [9]u32 = undefined; + var neighbors_green: [9]u32 = undefined; + var neighbors_blue: [9]u32 = undefined; + + for (0..3) |h| { + for (0..3) |w| { + const pixel = self.buffer[(y + 1 - h) * self.width + x + 1 - w]; + neighbors_red[w + 3 * h] = pixel.red; + neighbors_green[w + 3 * h] = pixel.green; + neighbors_blue[w + 3 * h] = pixel.blue; + } + } + + self.buffer[x + (y * self.width)] = .{ + .red = @intCast(Self.gaussian_mean(neighbors_red)), + .green = @intCast(Self.gaussian_mean(neighbors_green)), + .blue = @intCast(Self.gaussian_mean(neighbors_blue)), + }; + } +}; + +pub const Box = struct { + const Self = @This(); + + x: u32, + y: u32, + width: u32, + height: u32, + color: Pixel, + radius: u32 = 0, + + fn render_rect( + self: *const Self, + x: u32, + y: u32, + width: u32, + height: u32, + canvas: *const Canvas + ) void { + if (x >= canvas.width or y >= canvas.height) { + return; + } + + const x_end = @min(x + width, canvas.width); + const y_end = @min(y + height, canvas.height); + + for (y..y_end) |yp| { + const offset = yp * canvas.width; + @memset( + canvas.buffer[(offset + x)..(offset + x_end)], + self.color + ); + } + } + + pub fn render(self: *const Self, canvas: *const Canvas) void { + if (self.radius == 0) { + self.render_rect(self.x, self.y, self.width, self.height, canvas); + return; + } + + const radius = @min(self.radius, self.width / 2, self.height / 2); + const core_width = self.width - 2 * radius; + const core_height = self.height - 2 * radius; + + self.render_rect( + self.x + radius, + self.y, + core_width, + radius, + canvas + ); + + self.render_rect( + self.x + radius, + self.y + radius + core_height, + core_width, + radius, + canvas + ); + + self.render_rect( + self.x, + self.y + radius, + self.width, + core_height, + canvas + ); + + for (0..radius) |yp| { + const width: u32 = @intFromFloat(std.math.round( + @as(f64, @floatFromInt(radius)) * + @cos(std.math.asin( + @as(f64, @floatFromInt(yp)) / @as(f64, @floatFromInt(radius)) + )) + )); + const spacing = radius - width; + + var offset = ((radius - yp) + self.y) * canvas.width + self.x + spacing; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = ((radius - yp) + self.y) * canvas.width + self.x + core_width + radius; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = (yp + self.y + radius + core_height) * canvas.width + self.x + spacing; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = (yp + self.y + radius + core_height) * canvas.width + self.x + core_width + radius; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + } + + for (0..radius) |yp| { + const width: u32 = @intFromFloat(std.math.round( + @as(f64, @floatFromInt(radius)) * + @cos(std.math.asin( + @as(f64, @floatFromInt(yp)) / @as(f64, @floatFromInt(radius)) + )) + )); + + const spacing = radius - width; + + canvas.set_with_anti_aliasing( + self.x + spacing, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + spacing - 1, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + core_width + radius + width, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + spacing, + @intCast(self.y + yp + radius + core_height), + ); + + canvas.set_with_anti_aliasing( + self.x + core_width + radius + width, + @intCast(self.y + yp + radius + core_height), + ); + } + + for (0..core_width) |w| { + canvas.set_with_anti_aliasing(@intCast(self.x + radius + w), self.y); + canvas.set_with_anti_aliasing(@intCast(self.x + radius + w), self.y + self.height); + } + + for (0..core_height) |h| { + canvas.set_with_anti_aliasing(self.x, @intCast(self.y + radius + h)); + canvas.set_with_anti_aliasing(self.x + self.width, @intCast(self.y + radius + h)); + } + } +}; |