From 6065695af97dcd40618764e2bee17bc76c67b65b Mon Sep 17 00:00:00 2001 From: Jeeves Date: Mon, 8 Apr 2024 12:15:54 -0600 Subject: [PATCH] colors with transparency (to fix later) --- src/color.zig | 64 +++++++++++++++++++++++++++------ src/main.zig | 98 +++++++++++++++++++++++---------------------------- 2 files changed, 98 insertions(+), 64 deletions(-) diff --git a/src/color.zig b/src/color.zig index 62fad2e..ce666c1 100644 --- a/src/color.zig +++ b/src/color.zig @@ -18,16 +18,45 @@ pub const RGB = struct { } }; -// pub const Default256 = [_]RGB{ -// RGB{ .r = 0, .g = 0, .b = 0 }, -// RGB{ .r = 204, .g = 4, .b = 3 }, -// RGB{ .r = 25, .g = 203, .b = 0 }, -// RGB{ .r = 206, .g = 203, .b = 0 }, -// RGB{ .r = 13, .g = 115, .b = 204 }, -// RGB{ .r = 203, .g = 30, .b = 209 }, -// RGB{ .r = 13, .g = 205, .b = 205 }, -// RGB{ .r = 221, .g = 221, .b = 221 }, -// }; +pub const RGBA = struct { + r: f32, + g: f32, + b: f32, + /// Alpha channel between 0.0 - 1.0 + a: f32 = 1.0, + + /// Create from string at comptime + pub fn init(comptime string: []const u8) !RGBA { + std.debug.assert(string.len == 8 or string.len == 9); + comptime var index: usize = 0; + if (string[0] == '#') index += 1; + const r = try fmt.parseInt(u8, string[index .. index + 2], 16); + const g = try fmt.parseInt(u8, string[index + 2 .. index + 4], 16); + const b = try fmt.parseInt(u8, string[index + 4 .. index + 6], 16); + const a = try fmt.parseInt(u8, string[index + 6 .. index + 8], 16); + return .{ + .r = @as(f32, @floatFromInt(r)) / 0xff, + .g = @as(f32, @floatFromInt(g)) / 0xff, + .b = @as(f32, @floatFromInt(b)) / 0xff, + .a = @as(f32, @floatFromInt(a)) / 0xff, + }; + } + + pub fn blend(base: *const RGBA, add: *const RGBA) RGBA { + var r: RGBA = undefined; + r.a = 1 - (1 - add.a) * (1 - base.a); + if (r.a < std.math.floatEps(f32)) { + r.r = 0; + r.g = 0; + r.b = 0; + return r; + } + r.r = (add.r * add.a / r.a + base.r * base.a * (1 - add.a)) / r.a; + r.g = (add.g * add.a / r.a + base.g * base.a * (1 - add.a)) / r.a; + r.b = (add.b * add.a / r.a + base.b * base.a * (1 - add.a)) / r.a; + return r; + } +}; test "RGB init from string" { const red = try RGB.init("#ff0000"); @@ -47,3 +76,18 @@ test "RGB init from string" { try std.testing.expectError(fmt.ParseIntError.InvalidCharacter, RGB.init("xyzxyz")); } + +test "RGBA blend" { + const red = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 0.0 }; + const blue = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.0 }; + const result = red.blend(&blue); + std.debug.print("\n{any}\n", .{result}); + const reeb = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 1.0 }; + const blueb = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.0 }; + const resultt = reeb.blend(&blueb); + std.debug.print("{any}\n", .{resultt}); + const roob = RGBA{ .r = 1.0, .g = 0, .b = 0, .a = 0.5 }; + const bloob = RGBA{ .r = 0, .g = 0, .b = 1.0, .a = 0.5 }; + const resulttt = roob.blend(&bloob); + std.debug.print("{any}\n", .{resulttt}); +} diff --git a/src/main.zig b/src/main.zig index 1f92b1f..2f8ccdc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,28 +18,27 @@ pub fn main() !void { var box1 = Terminal.Box.init(allocator); defer box1.deinit(); - box1.border_fg = try Terminal.Color.init("#ff8855"); - box1.top = 1; - box1.bottom = 1; - box1.left = 1; - box1.right = 1; + box1.border_bg = try Terminal.Color.init("#3ace37ff"); + box1.top = 0; + box1.bottom = 0; + box1.left = 0; + box1.right = 0; var box2 = Terminal.Box.init(allocator); defer box2.deinit(); - box2.border_fg = try Terminal.Color.init("#aaff55"); + box2.border_bg = try Terminal.Color.init("#000000c0"); box2.top = 1; - box2.bottom = 1; - box2.left = 1; - box2.right = 1; + box2.bottom = 3; + box2.left = 20; + box2.right = 2; var box3 = Terminal.Box.init(allocator); defer box3.deinit(); - box3.border_bg = try Terminal.Color.init("#aa33ff"); - box3.border_type = .bg; - box3.top = 1; + box3.border_bg = try Terminal.Color.init("#48d5eaa0"); + box3.top = 2; box3.bottom = 1; - box3.left = 1; - box3.right = 1; + box3.left = 20; + box3.right = 20; try term.box.addChild(&box1); try box1.addChild(&box2); @@ -48,38 +47,9 @@ pub fn main() !void { while (true) { const events = try term.getEvents(); _ = events; - // for (events) |ev| if (ev.system == .winch) std.debug.print("hii\n", .{}); try term.draw(); std.time.sleep(1000); } - - // var box = Terminal.Box{ - // .x = 0, - // .y = 0, - // .width = 13, - // .height = 1, - - // .content = "hello world", - // .border = true, - // }; - // try box.draw(&term); - - // try term.clearScreen(); - // try term.print("fooboo", .{}); - // try term.cursorLeft(); - // try term.cursorLeft(); - // try term.print("ar", .{}); - // // try term.cursorSet(12, 3); - // try term.blinkOn(); - // try term.boldOn(); - // try term.underlineOn(); - // try term.italicsOn(); - // try term.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ", .{}); - // try term.blinkOff(); - // try term.boldOff(); - // try term.underlineOff(); - // try term.italicsOff(); - // try term.print("eeheeveehee\n", .{}); } pub const Terminal = struct { @@ -289,15 +259,23 @@ pub const Terminal = struct { // } pub fn setFg(self: *Terminal, color: Color) !void { - try self.info.writeString(.set_rgb_foreground, self.tty.writer(), &[_]u32{ @intCast(color.r), @intCast(color.g), @intCast(color.b) }); + try self.info.writeString(.set_rgb_foreground, self.tty.writer(), &[_]u32{ + @intFromFloat(color.r * 0xff), + @intFromFloat(color.g * 0xff), + @intFromFloat(color.b * 0xff), + }); } pub fn setBg(self: *Terminal, color: Color) !void { - try self.info.writeString(.set_rgb_background, self.tty.writer(), &[_]u32{ @intCast(color.r), @intCast(color.g), @intCast(color.b) }); + try self.info.writeString(.set_rgb_background, self.tty.writer(), &[_]u32{ + @intFromFloat(color.r * 0xff), + @intFromFloat(color.g * 0xff), + @intFromFloat(color.b * 0xff), + }); } pub const Info = @import("terminfo.zig"); - pub const Color = @import("color.zig").RGB; + pub const Color = @import("color.zig").RGBA; pub const Box = struct { parent: ?*Box, @@ -311,10 +289,10 @@ pub const Terminal = struct { top: u32, bottom: u32, - border_type: enum { line, bg } = .line, + border_type: enum { line, bg } = .bg, border_char: u8 = ' ', - border_bg: Color = Color{ .r = 0, .g = 0, .b = 0 }, - border_fg: Color = Color{ .r = 0xff, .g = 0xff, .b = 0xff }, + border_bg: Color = Color{ .r = 0.0, .g = 0.0, .b = 0.0 }, + border_fg: Color = Color{ .r = 1.0, .g = 1.0, .b = 1.0 }, content: []const u8 = "", @@ -369,13 +347,11 @@ pub const Terminal = struct { pub fn draw(self: *Box, term: *Terminal) !void { if (self.dirty) { - // if (self != &term.box) self.calcRect(); const rect = self.getRect(); - // std.debug.print("{any}\r\n", .{rect}); switch (self.border_type) { .line => { - try term.setFg(self.border_fg); - try term.setBg(self.border_bg); + try term.setFg(self.getBorderFgColor()); + try term.setBg(self.getBorderBgColor()); var x: u32 = 0; var y: u32 = 0; try term.cursorSet(rect.x, rect.y); @@ -396,7 +372,7 @@ pub const Terminal = struct { try term.print("┘", .{}); }, .bg => { - try term.setBg(self.border_bg); + try term.setBg(self.getBorderBgColor()); var y: u32 = 0; try term.cursorSet(rect.x, rect.y); while (y < rect.h) : (y += 1) { @@ -423,5 +399,19 @@ pub const Terminal = struct { return rect; } const Rect = struct { x: u32, y: u32, w: u32, h: u32 }; + + fn getBorderFgColor(self: *Box) Color { + if (self.parent) |parent| { + const parent_color = parent.getBorderFgColor(); + return parent_color.blend(&self.border_fg); + } else return self.border_fg; + } + + fn getBorderBgColor(self: *Box) Color { + if (self.parent) |parent| { + const parent_color = parent.getBorderBgColor(); + return parent_color.blend(&self.border_bg); + } else return self.border_bg; + } }; };