diff --git a/YUV.zig b/YUV.zig new file mode 100644 index 0000000000000000000000000000000000000000..7093a2bf3ee4f88ce8bb387fcacbe962902ed94f --- /dev/null +++ b/YUV.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const Self = @This(); +const color = @import("./mod.zig"); +const _x = @import("./_x.zig"); +const vec4 = @Vector(4, f32); + +y: f32, // [ 0 .. 1 ] +u: f32, // [ -0.436 .. +0.436 ] +v: f32, // [ -0.615 .. +0.615 ] +a: f32, // [ 0 .. 1 ] + +pub fn initYUVA(y: f32, u: f32, v: f32, a: f32) Self { + return Self{ + .y = y, + .u = u, + .v = v, + .a = a, + }; +} + +pub fn initYUV(y: f32, u: f32, v: f32) Self { + return initYUVA(y, u, v, 255); +} + +pub fn eql(x: Self, y: Self) bool { + return x.y == y.y and x.u == y.u and x.v == y.v and x.a == y.a; +} + +pub usingnamespace _x.mixin(@This(), f32, .y, .u, .v); + +// https://web.archive.org/web/20180423091842/http://www.equasys.de/colorconversion.html +pub fn to_srgb(x: Self) color.sRGB { + // zig fmt: off + const y, const u, const v, const a = x.to_array(); + const r = y * 1.000 + u * 0.000 + v * 1.140; + const g = y * 1.000 + u * -0.395 + v * -0.581; + const b = y * 1.000 + u * 2.032 + v * 0.000; + return color.sRGB.from_float(.{ r, g, b, a }); + // zig fmt: on +} + +pub fn normal(x: Self) vec4 { + const v: vec4 = x.to_array(); + return v * @as(vec4, .{ 255, 255, 255, 1 }); +} diff --git a/mod.zig b/mod.zig index d3b8ef2736d7d139c3e858a09e4bce901212d401..5f27f30f3507a04102ba21385f1089ef157b5049 100644 --- a/mod.zig +++ b/mod.zig @@ -7,3 +7,4 @@ pub const CMYK = @import("./CMYK.zig"); pub const HSL = @import("./HSL.zig"); pub const HSV = @import("./HSV.zig"); pub const YCbCr = @import("./YCbCr.zig"); +pub const YUV = @import("./YUV.zig"); diff --git a/sRGB.zig b/sRGB.zig index d7ad80f5e1afd539aeb6a15a9300b3de8968997e..246b2aa70ff607e8075ca75b270c1457af0adcaa 100644 --- a/sRGB.zig +++ b/sRGB.zig @@ -4,6 +4,7 @@ const std = @import("std"); const Self = @This(); const color = @import("./mod.zig"); const _x = @import("./_x.zig"); +const vec4 = @Vector(4, f32); r: u8, // red value g: u8, // green value @@ -104,8 +105,8 @@ pub fn to_float(x: Self) Float { }; } -pub fn from_float(y: @Vector(4, f32)) Self { - const r, const g, const b, const a = y * @as(@Vector(4, f32), @splat(255.0)); +pub fn from_float(y: vec4) Self { + const r, const g, const b, const a = y * @as(vec4, @splat(255.0)); return .{ .r = @intFromFloat(r), .g = @intFromFloat(g), @@ -174,3 +175,14 @@ pub fn to_ycbcr(x: Self) color.YCbCr { }); // zig fmt: on } + +// https://web.archive.org/web/20180423091842/http://www.equasys.de/colorconversion.html +pub fn to_yuv(x: Self) color.YUV { + // zig fmt: off + const r, const g, const b, const a = x.to_vec() / @as(vec4, @splat(255)); + const y = r * 0.299 + g * 0.587 + b * 0.114; + const u = r * -0.147 + g * -0.289 + b * 0.436; + const v = r * 0.615 + g * -0.515 + b * -0.100; + return color.YUV.initYUVA(y, u, v, a); + // zig fmt: on +} diff --git a/test.zig b/test.zig index d20d3a4f9b2a38ef1163a7abfc1e9269e30722ed..f199f1c1da7e29cde0e5b15f9f54cb38f9081018 100644 --- a/test.zig +++ b/test.zig @@ -72,4 +72,13 @@ test { try expect(o.to_ycbcr().to_array()).toEqual(.{ 166, 173, 65, 255 }); try expect(o.to_ycbcr().to_srgb().to_array()).toEqual(.{ 77, 195, 245, 255 }); } + { + // Y: 166.244, U: 39.741, V: -76.541 + const y, const u, const v, const a = o.to_yuv().normal(); + try std.testing.expectApproxEqAbs(166.244, y, 0.001); + try std.testing.expectApproxEqAbs(39.723, u, 0.001); + try std.testing.expectApproxEqAbs(-76.539, v, 0.001); + try std.testing.expect(a == 1.0); + try expect(o.to_yuv().to_srgb().to_array()).toEqual(.{ 78, 195, 246, 255 }); + } }