I wanted to use this example to learn a bit more about Zig’s reflection features, so I wrote this helper function as an exercise:
/// Conveniently cast between number types (.Int and .Float)
fn asNum(comptime T: type, from: anytype) T {
switch (@typeInfo(@TypeOf(from))) {
.Int => {
switch (@typeInfo(T)) {
.Int => return @intCast(from),
.Float => return @floatFromInt(from),
else => @compileError("Only .Int and .Float types supported"),
}
},
.Float => {
switch (@typeInfo(T)) {
.Float => return @floatCast(from),
.Int => return @intFromFloat(from),
else => @compileError("Only .Int and .Float types supported"),
}
},
else => @compileError("Only .Int and .Float types supported"),
}
}
Now, instead of doing something like:
@as(f32, @floatFromInt(val));
I can just do:
asNum(f32, val);
So, the previous code would look like this:
/// Blur an image by applying a box filter using an integral image.
fn applyBoxFilter(integral: [*]i32, blur: [*]u8, rows: usize, cols: usize, radius: usize) void {
for (0..rows) |r| {
for (0..cols) |c| {
const pos = r * cols + c;
const r1 = @max(asNum(i32, r) - asNum(i32, radius), 0);
const c1 = @max(asNum(i32, c) - asNum(i32, radius), 0);
const r2 = @min(asNum(i32, r) + asNum(i32, radius), asNum(i32, rows) - 1);
const c2 = @min(asNum(i32, c) + asNum(i32, radius), asNum(i32, cols) - 1);
const pos11: usize = @intCast(r1 * asNum(i32, cols) + c1);
const pos12: usize = @intCast(r1 * asNum(i32, cols) + c2);
const pos21: usize = @intCast(r2 * asNum(i32, cols) + c1);
const pos22: usize = @intCast(r2 * asNum(i32, cols) + c2);
const area: f32 = @floatFromInt((r2 - r1) * (c2 - c1));
const sum: f32 = @floatFromInt(integral[pos22] - integral[pos21] - integral[pos12] + integral[pos11]);
blur[pos] = @intFromFloat(@round(sum / area));
}
}
}
Not as good as Andrew’s suggestion, though.
Feedback appreciated.