ReviewOS

also looking at this

home-lang/home

feat(video): add checked capture plane packing

#915
Merged chris wants to merge feat/checked-plane-packing into main
3 files +68 -0
packages/video/src/core/plane_pack.zigadded+43-0
Changes to packages/video/src/core/plane_pack.zig
@@ -0,0 +1,43 @@
1//! Checked copies from padded capture planes into tightly packed owned buffers.
2//! Callers retain source ownership and must provide non-overlapping destination.
3const std = ;
4pub fn rows(source: []const u8, stride: usize, width: usize, height: usize, destination: []u8) !void {
5 if (width == 0 or height == 0 or stride < width) return error.InvalidGeometry;
6 const size = std.math.mul(usize, width, height) catch return error.InvalidGeometry;
7 const last = std.math.mul(usize, stride, height - 1) catch return error.InvalidGeometry;
8 const needed = std.math.add(usize, last, width) catch return error.InvalidGeometry;
9 if (source.len < needed or destination.len < size) return error.BufferTooSmall;
10 for (0..height) |y| @memcpy(destination[y * width ..][0..width], source[y * stride ..][0..width]);
11}
12/// I420 Y,U,V planes; strides are independent. Dimensions must be even.
13/// Validate all three planes before touching destination (failure is atomic).
14pub fn packI420(planes: [3][]const u8, strides: [3]usize, width: usize, height: usize, destination: []u8) !void {
15 if (width == 0 or height == 0 or (width | height) & 1 != 0) return error.InvalidGeometry;
16 const pixels = std.math.mul(usize, width, height) catch return error.InvalidGeometry;
17 const size = std.math.add(usize, pixels, pixels / 2) catch return error.InvalidGeometry;
18 if (destination.len < size) return error.BufferTooSmall;
19 for (0..3) |i| {
20 const w = if (i == 0) width else width / 2;
21 const h = if (i == 0) height else height / 2;
22 if (strides[i] < w) return error.InvalidGeometry;
23 const last = std.math.mul(usize, strides[i], h - 1) catch return error.InvalidGeometry;
24 const needed = std.math.add(usize, last, w) catch return error.InvalidGeometry;
25 if (planes[i].len < needed) return error.BufferTooSmall;
26 }
27 try rows(planes[0], strides[0], width, height, destination[0..pixels]);
28 try rows(planes[1], strides[1], width / 2, height / 2, destination[pixels..][0 .. pixels / 4]);
29 try rows(planes[2], strides[2], width / 2, height / 2, destination[pixels + pixels / 4 ..][0 .. pixels / 4]);
30}
31test "I420 padded planes omit padding and accept absent final padding" {
32 var out: [12]u8 = undefined;
33 try packI420(.{ &.{ 1, 2, 3, 4, 99, 99, 5, 6, 7, 8 }, &.{ 9, 10 }, &.{ 11, 12 } }, .{ 6, 4, 3 }, 4, 2, &out);
34 try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, &out);
35}
36test "invalid geometry, overflow and truncated chroma never write output" {
37 var out: [12]u8 = @splat(42);
38 try std.testing.expectError(error.BufferTooSmall, packI420(.{ &.{ 1, 2, 3, 4, 5, 6, 7, 8 }, &.{ 9, 10 }, &.{11} }, .{ 4, 2, 2 }, 4, 2, &out));
39 try std.testing.expectEqualSlices(u8, &@as([12]u8, @splat(42)), &out);
40 try std.testing.expectError(error.InvalidGeometry, rows(&.{}, std.math.maxInt(usize), 2, 3, &out));
41 try std.testing.expectError(error.InvalidGeometry, packI420(.{ &.{}, &.{}, &.{} }, .{ 0, 0, 0 }, 3, 2, &out));
42 try std.testing.expectError(error.BufferTooSmall, rows(&.{ 1, 2, 3, 4 }, 2, 2, 2, out[0..3]));
43}