Complete grayscale filter project
This commit is contained in:
112
grayscale_filter/src/main.zig
Normal file
112
grayscale_filter/src/main.zig
Normal file
@@ -0,0 +1,112 @@
|
||||
const std = @import("std");
|
||||
const c = @cImport({
|
||||
@cDefine("_NO_CRT_STDIO_INLINE", "1");
|
||||
@cInclude("stdio.h");
|
||||
@cInclude("spng.h");
|
||||
});
|
||||
|
||||
const PngError = error {
|
||||
CouldNotOpenImage,
|
||||
CouldNotCloseImage,
|
||||
CouldNotGetImageHeader,
|
||||
CouldNotCalcOutputSize,
|
||||
CouldNotDecodeImage,
|
||||
CouldNotEncodeImage,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = arena.allocator();
|
||||
defer arena.deinit();
|
||||
|
||||
const path = "pedro_pascal.png";
|
||||
const fd = try open_image(path, "rb");
|
||||
defer close_image(fd) catch @panic("Failed to close file");
|
||||
|
||||
const ctx = c.spng_ctx_new(0) orelse unreachable;
|
||||
defer c.spng_ctx_free(ctx);
|
||||
|
||||
_ = c.spng_set_png_file(ctx, fd);
|
||||
var header = try get_png_header(ctx);
|
||||
const size = try calc_output_size(ctx);
|
||||
var buffer = try allocator.alloc(u8, size);
|
||||
@memset(buffer[0..], 0);
|
||||
|
||||
try read_data_to_buffer(ctx, buffer[0..]);
|
||||
try apply_grayscale_filter(buffer[0..]);
|
||||
try save_image(&header, buffer[0..]);
|
||||
}
|
||||
|
||||
fn open_image(path: [*c]const u8, mode: [*c]const u8) !?*c.FILE {
|
||||
const fd = c.fopen(path, mode);
|
||||
if (fd == null) {
|
||||
return error.CouldNotOpenImage;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
fn close_image(fd: ?*c.FILE) !void {
|
||||
if (c.fclose(fd) != 0) {
|
||||
return error.CouldNotCloseImage;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_png_header(ctx: *c.spng_ctx) !c.spng_ihdr {
|
||||
var header: c.spng_ihdr = undefined;
|
||||
if (c.spng_get_ihdr(ctx, &header) != 0) {
|
||||
return error.CouldNotGetImageHeader;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
fn calc_output_size(ctx: *c.spng_ctx) !u64 {
|
||||
var output_size: u64 = 0;
|
||||
if (c.spng_decoded_image_size(ctx, c.SPNG_FMT_RGBA8, &output_size) != 0) {
|
||||
return error.CouldNotCalcOutputSize;
|
||||
}
|
||||
|
||||
return output_size;
|
||||
}
|
||||
|
||||
fn read_data_to_buffer(ctx: *c.spng_ctx, buffer: []u8) !void {
|
||||
if (c.spng_decode_image(ctx, buffer.ptr, buffer.len, c.SPNG_FMT_RGBA8, 0) != 0) {
|
||||
return error.CouldNotDecodeImage;
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_grayscale_filter(buffer: []u8) !void {
|
||||
const rfactor: f16 = 0.2126;
|
||||
const gfactor: f16 = 0.7152;
|
||||
const bfactor: f16 = 0.0722;
|
||||
var index: u64 = 0;
|
||||
|
||||
while (index < buffer.len) : (index += 4) {
|
||||
const r: f16 = @floatFromInt(buffer[index]);
|
||||
const g: f16 = @floatFromInt(buffer[index + 1]);
|
||||
const b: f16 = @floatFromInt(buffer[index + 2]);
|
||||
|
||||
const y: f16 = r * rfactor + g * gfactor + b * bfactor;
|
||||
|
||||
buffer[index] = @intFromFloat(y);
|
||||
buffer[index + 1] = @intFromFloat(y);
|
||||
buffer[index + 2] = @intFromFloat(y);
|
||||
}
|
||||
}
|
||||
|
||||
fn save_image(header: *c.spng_ihdr, buffer: []u8) !void {
|
||||
const path = "pedro_pascal_grayscale.png";
|
||||
const fd = try open_image(path, "wb");
|
||||
defer close_image(fd) catch @panic("Failed to close file");
|
||||
|
||||
const ctx = c.spng_ctx_new(c.SPNG_CTX_ENCODER) orelse unreachable;
|
||||
defer c.spng_ctx_free(ctx);
|
||||
|
||||
_ = c.spng_set_png_file(ctx, fd);
|
||||
_ = c.spng_set_ihdr(ctx, header);
|
||||
|
||||
if (c.spng_encode_image(ctx, buffer.ptr, buffer.len, c.SPNG_FMT_PNG, c.SPNG_ENCODE_FINALIZE) != 0) {
|
||||
return error.CouldNotEncodeImage;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user