diff --git a/src/config/Config.zig b/src/config/Config.zig index 66c85e8df..0c1e825ea 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -22,7 +22,6 @@ const fontpkg = @import("../font/main.zig"); const inputpkg = @import("../input.zig"); const internal_os = @import("../os/main.zig"); const cli = @import("../cli.zig"); -const Lab = @import("../lab.zig").Lab; const conditional = @import("conditional.zig"); const Conditional = conditional.Conditional; @@ -5614,50 +5613,6 @@ pub const Palette = struct { } } - pub fn generate( - self: Self, - bg: terminal.color.RGB, - fg: terminal.color.RGB - ) terminal.color.Palette { - var result = self.value; - var base8_lab: [8]Lab = undefined; - for (0..8) |i| base8_lab[i] = Lab.fromTerminalRgb(result[i]); - const bg_lab = Lab.fromTerminalRgb(bg); - const fg_lab = Lab.fromTerminalRgb(fg); - - var idx: usize = 16; - for (0..6) |ri| { - const tr = @as(f32, @floatFromInt(ri)) / 5.0; - const c0 = Lab.lerp(tr, bg_lab, base8_lab[1]); - const c1 = Lab.lerp(tr, base8_lab[2], base8_lab[3]); - const c2 = Lab.lerp(tr, base8_lab[4], base8_lab[5]); - const c3 = Lab.lerp(tr, base8_lab[6], fg_lab); - for (0..6) |gi| { - const tg = @as(f32, @floatFromInt(gi)) / 5.0; - const c4 = Lab.lerp(tg, c0, c1); - const c5 = Lab.lerp(tg, c2, c3); - for (0..6) |bi| { - if (!self.mask.isSet(idx)) { - const c6 = Lab.lerp( - @as(f32, @floatFromInt(bi)) / 5.0, c4, c5); - result[idx] = c6.toTerminalRgb(); - } - idx += 1; - } - } - } - - for (0..24) |i| { - const t = @as(f32, @floatFromInt(i + 1)) / 25.0; - if (!self.mask.isSet(idx)) { - result[idx] = Lab.lerp(t, bg_lab, fg_lab).toTerminalRgb(); - } - idx += 1; - } - - return result; - } - test "parseCLI" { const testing = std.testing; diff --git a/src/lab.zig b/src/lab.zig deleted file mode 100644 index 861bf550b..000000000 --- a/src/lab.zig +++ /dev/null @@ -1,67 +0,0 @@ -const terminal = @import("./terminal/main.zig"); -const std = @import("std"); - -pub const Lab = struct { - l: f32, - a: f32, - b: f32, - - pub fn fromTerminalRgb(rgb: terminal.color.RGB) Lab { - var r: f32 = @as(f32, @floatFromInt(rgb.r)) / 255.0; - var g: f32 = @as(f32, @floatFromInt(rgb.g)) / 255.0; - var b: f32 = @as(f32, @floatFromInt(rgb.b)) / 255.0; - - r = if (r > 0.04045) std.math.pow(f32, (r + 0.055) / 1.055, 2.4) else r / 12.92; - g = if (g > 0.04045) std.math.pow(f32, (g + 0.055) / 1.055, 2.4) else g / 12.92; - b = if (b > 0.04045) std.math.pow(f32, (b + 0.055) / 1.055, 2.4) else b / 12.92; - - var x = (r * 0.4124564 + g * 0.3575761 + b * 0.1804375) / 0.95047; - var y = r * 0.2126729 + g * 0.7151522 + b * 0.0721750; - var z = (r * 0.0193339 + g * 0.1191920 + b * 0.9503041) / 1.08883; - - x = if (x > 0.008856) std.math.cbrt(x) else 7.787 * x + 16.0 / 116.0; - y = if (y > 0.008856) std.math.cbrt(y) else 7.787 * y + 16.0 / 116.0; - z = if (z > 0.008856) std.math.cbrt(z) else 7.787 * z + 16.0 / 116.0; - - return .{ .l = 116.0 * y - 16.0, .a = 500.0 * (x - y), .b = 200.0 * (y - z) }; - } - - pub fn toTerminalRgb(self: Lab) terminal.color.RGB { - const y = (self.l + 16.0) / 116.0; - const x = self.a / 500.0 + y; - const z = y - self.b / 200.0; - - const x3 = x * x * x; - const y3 = y * y * y; - const z3 = z * z * z; - const xf = (if (x3 > 0.008856) x3 else (x - 16.0 / 116.0) / 7.787) * 0.95047; - const yf = if (y3 > 0.008856) y3 else (y - 16.0 / 116.0) / 7.787; - const zf = (if (z3 > 0.008856) z3 else (z - 16.0 / 116.0) / 7.787) * 1.08883; - - var r = xf * 3.2404542 - yf * 1.5371385 - zf * 0.4985314; - var g = -xf * 0.9692660 + yf * 1.8760108 + zf * 0.0415560; - var b = xf * 0.0556434 - yf * 0.2040259 + zf * 1.0572252; - - r = if (r > 0.0031308) 1.055 * std.math.pow(f32, r, 1.0 / 2.4) - 0.055 else 12.92 * r; - g = if (g > 0.0031308) 1.055 * std.math.pow(f32, g, 1.0 / 2.4) - 0.055 else 12.92 * g; - b = if (b > 0.0031308) 1.055 * std.math.pow(f32, b, 1.0 / 2.4) - 0.055 else 12.92 * b; - - return .{ - .r = @intFromFloat(@min(@max(r, 0.0), 1.0) * 255.0 + 0.5), - .g = @intFromFloat(@min(@max(g, 0.0), 1.0) * 255.0 + 0.5), - .b = @intFromFloat(@min(@max(b, 0.0), 1.0) * 255.0 + 0.5), - }; - } - - pub fn lerp(t: f32, a: Lab, b: Lab) Lab { - return .{ - .l = a.l + t * (b.l - a.l), - .a = a.a + t * (b.a - a.a), - .b = a.b + t * (b.b - a.b) - }; - } -}; - - - - diff --git a/src/terminal/color.zig b/src/terminal/color.zig index 1e9e4b642..bbc3e00fd 100644 --- a/src/terminal/color.zig +++ b/src/terminal/color.zig @@ -47,6 +47,51 @@ pub const default: Palette = default: { /// Palette is the 256 color palette. pub const Palette = [256]RGB; +pub fn generate_256_palette( + base: Palette, + mask: std.StaticBitSet(@typeInfo(Palette).array.len), + bg: RGB, + fg: RGB +) Palette { + var palette = base; + var base8_lab: [8]LAB = undefined; + for (0..8) |i| base8_lab[i] = LAB.fromRgb(palette[i]); + const bg_lab = LAB.fromRgb(bg); + const fg_lab = LAB.fromRgb(fg); + + var idx: usize = 16; + for (0..6) |ri| { + const tr = @as(f32, @floatFromInt(ri)) / 5.0; + const c0 = LAB.lerp(tr, bg_lab, base8_lab[1]); + const c1 = LAB.lerp(tr, base8_lab[2], base8_lab[3]); + const c2 = LAB.lerp(tr, base8_lab[4], base8_lab[5]); + const c3 = LAB.lerp(tr, base8_lab[6], fg_lab); + for (0..6) |gi| { + const tg = @as(f32, @floatFromInt(gi)) / 5.0; + const c4 = LAB.lerp(tg, c0, c1); + const c5 = LAB.lerp(tg, c2, c3); + for (0..6) |bi| { + if (!mask.isSet(idx)) { + const c6 = LAB.lerp( + @as(f32, @floatFromInt(bi)) / 5.0, c4, c5); + palette[idx] = c6.toRgb(); + } + idx += 1; + } + } + } + + for (0..24) |i| { + const t = @as(f32, @floatFromInt(i + 1)) / 25.0; + if (!mask.isSet(idx)) { + palette[idx] = LAB.lerp(t, bg_lab, fg_lab).toRgb(); + } + idx += 1; + } + + return palette; +} + /// A palette that can have its colors changed and reset. Purposely built /// for terminal color operations. pub const DynamicPalette = struct { @@ -519,6 +564,69 @@ pub const RGB = packed struct(u24) { } }; +/// LAB color space +const LAB = struct { + l: f32, + a: f32, + b: f32, + + pub fn fromRgb(rgb: RGB) LAB { + var r: f32 = @as(f32, @floatFromInt(rgb.r)) / 255.0; + var g: f32 = @as(f32, @floatFromInt(rgb.g)) / 255.0; + var b: f32 = @as(f32, @floatFromInt(rgb.b)) / 255.0; + + r = if (r > 0.04045) std.math.pow(f32, (r + 0.055) / 1.055, 2.4) else r / 12.92; + g = if (g > 0.04045) std.math.pow(f32, (g + 0.055) / 1.055, 2.4) else g / 12.92; + b = if (b > 0.04045) std.math.pow(f32, (b + 0.055) / 1.055, 2.4) else b / 12.92; + + var x = (r * 0.4124564 + g * 0.3575761 + b * 0.1804375) / 0.95047; + var y = r * 0.2126729 + g * 0.7151522 + b * 0.0721750; + var z = (r * 0.0193339 + g * 0.1191920 + b * 0.9503041) / 1.08883; + + x = if (x > 0.008856) std.math.cbrt(x) else 7.787 * x + 16.0 / 116.0; + y = if (y > 0.008856) std.math.cbrt(y) else 7.787 * y + 16.0 / 116.0; + z = if (z > 0.008856) std.math.cbrt(z) else 7.787 * z + 16.0 / 116.0; + + return .{ .l = 116.0 * y - 16.0, .a = 500.0 * (x - y), .b = 200.0 * (y - z) }; + } + + pub fn toRgb(self: LAB) RGB { + const y = (self.l + 16.0) / 116.0; + const x = self.a / 500.0 + y; + const z = y - self.b / 200.0; + + const x3 = x * x * x; + const y3 = y * y * y; + const z3 = z * z * z; + const xf = (if (x3 > 0.008856) x3 else (x - 16.0 / 116.0) / 7.787) * 0.95047; + const yf = if (y3 > 0.008856) y3 else (y - 16.0 / 116.0) / 7.787; + const zf = (if (z3 > 0.008856) z3 else (z - 16.0 / 116.0) / 7.787) * 1.08883; + + var r = xf * 3.2404542 - yf * 1.5371385 - zf * 0.4985314; + var g = -xf * 0.9692660 + yf * 1.8760108 + zf * 0.0415560; + var b = xf * 0.0556434 - yf * 0.2040259 + zf * 1.0572252; + + r = if (r > 0.0031308) 1.055 * std.math.pow(f32, r, 1.0 / 2.4) - 0.055 else 12.92 * r; + g = if (g > 0.0031308) 1.055 * std.math.pow(f32, g, 1.0 / 2.4) - 0.055 else 12.92 * g; + b = if (b > 0.0031308) 1.055 * std.math.pow(f32, b, 1.0 / 2.4) - 0.055 else 12.92 * b; + + return .{ + .r = @intFromFloat(@min(@max(r, 0.0), 1.0) * 255.0 + 0.5), + .g = @intFromFloat(@min(@max(g, 0.0), 1.0) * 255.0 + 0.5), + .b = @intFromFloat(@min(@max(b, 0.0), 1.0) * 255.0 + 0.5), + }; + } + + pub fn lerp(t: f32, a: LAB, b: LAB) LAB { + return .{ + .l = a.l + t * (b.l - a.l), + .a = a.a + t * (b.a - a.a), + .b = a.b + t * (b.b - a.b) + }; + } +}; + + test "palette: default" { const testing = std.testing; diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index f58fcd397..31ab6feee 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -176,7 +176,9 @@ pub const DerivedConfig = struct { const alloc = arena.allocator(); const palette = if (config.@"generate-256-palette") - config.palette.generate( + terminalpkg.color.generate_256_palette( + config.palette.value, + config.palette.mask, config.background.toTerminalRGB(), config.foreground.toTerminalRGB() )