| 1 | //! object representing a single color in the Linear RGB colorspace with channel values from 0-1 |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const Self = @This(); |
| 5 | const color = @import("./mod.zig"); |
| 6 | const _x = @import("./_x.zig"); |
| 7 | const vec4 = @Vector(4, f32); |
| 8 | |
| 9 | r: f32, // red [ 0 .. 1] |
| 10 | g: f32, // green [ 0 .. 1] |
| 11 | b: f32, // blue [ 0 .. 1] |
| 12 | a: f32, // alpha [ 0 .. 1] |
| 13 | |
| 14 | pub fn initRGBA(r: f32, g: f32, b: f32, a: f32) Self { |
| 15 | std.debug.assert(r >= 0.0 and r <= 1.0); |
| 16 | std.debug.assert(g >= 0.0 and g <= 1.0); |
| 17 | std.debug.assert(b >= 0.0 and b <= 1.0); |
| 18 | std.debug.assert(a >= 0.0 and a <= 1.0); |
| 19 | return Self{ |
| 20 | .r = r, |
| 21 | .g = g, |
| 22 | .b = b, |
| 23 | .a = a, |
| 24 | }; |
| 25 | } |
| 26 | |
| 27 | pub fn eql(x: Self, y: Self) bool { |
| 28 | return x.r == y.r and x.g == y.g and x.b == y.b and x.a == y.a; |
| 29 | } |
| 30 | |
| 31 | // https://www.w3.org/TR/WCAG/#dfn-relative-luminance |
| 32 | pub fn relative_luminance(x: Self) f32 { |
| 33 | const r, const g, const b, _ = x.to_array(); |
| 34 | return (0.2126 * r) + (0.7152 * g) + (0.0722 * b); |
| 35 | } |
| 36 | |
| 37 | const M = _x.mixin(@This(), f32, .r, .g, .b); |
| 38 | pub const to_vec = M.to_vec; |
| 39 | pub const from_vec = M.from_vec; |
| 40 | pub const to_array = M.to_array; |
| 41 | |
| 42 | // https://gamedev.stackexchange.com/a/194038 |
| 43 | pub fn to_srgb(x: Self) color.sRGB { |
| 44 | const v = x.to_vec(); |
| 45 | const cutoff = v < @as(vec4, @splat(0.0031308)); |
| 46 | const higher = @as(vec4, @splat(1.055)) * std.math.pow(vec4, v, @splat(1.0 / 2.4)) - @as(vec4, @splat(0.055)); |
| 47 | const lower = v * @as(vec4, @splat(12.92)); |
| 48 | return color.sRGB.from_float(@select(f32, cutoff, higher, lower)); |
| 49 | } |