daq-view/tools/png-to-icon.zig

54 lines
2.1 KiB
Zig

const std = @import("std");
const stb_image = @import("stb_image");
// https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_file_structure
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
if (args.len != 3) {
std.debug.print("Usage: ./png-to-icon <png-file> <output-ico>", .{});
std.process.exit(1);
}
const cwd = std.fs.cwd();
const input_png_path = args[1];
const output_ico_path = args[2];
const input_png_data = try cwd.readFileAlloc(allocator, input_png_path, 1024 * 1024 * 5);
defer allocator.free(input_png_data);
const png_image = try stb_image.load(input_png_data);
defer png_image.deinit();
std.debug.assert(png_image.width > 0 and png_image.width <= 256);
std.debug.assert(png_image.height > 0 and png_image.height <= 256);
const output_ico_file = try std.fs.cwd().createFile(output_ico_path, .{ });
defer output_ico_file.close();
const writer = output_ico_file.writer();
// ICONDIR structure
try writer.writeInt(u16, 0, .little); // Must always be zero
try writer.writeInt(u16, 1, .little); // Image type. 1 for .ICO
try writer.writeInt(u16, 1, .little); // Number of images
// ICONDIRENTRY structure
try writer.writeInt(u8, @truncate(png_image.width), .little); // Image width
try writer.writeInt(u8, @truncate(png_image.height), .little); // Image height
try writer.writeInt(u8, 0, .little); // Number of colors in color pallete. 0 means that color pallete is not used
try writer.writeInt(u8, 0, .little); // Must always be zero
try writer.writeInt(u16, 0, .little); // Color plane
try writer.writeInt(u16, 32, .little); // Bits per pixel
try writer.writeInt(u32, @intCast(input_png_data.len), .little); // Image size in bytes
try writer.writeInt(u32, 22, .little); // Offset to image data from the start
// PNG image data
try writer.writeAll(input_png_data);
}