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 ", .{}); 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); }