add .ico file generation at build-time
This commit is contained in:
parent
6eea7329c8
commit
f563ff931b
106
build.zig
106
build.zig
@ -1,6 +1,83 @@
|
||||
const std = @import("std");
|
||||
const Module = std.Build.Module;
|
||||
|
||||
const AddExecutableIcon = struct {
|
||||
obj: *std.Build.Step.Compile,
|
||||
step: std.Build.Step,
|
||||
icon_file: std.Build.LazyPath,
|
||||
resource_file: std.Build.LazyPath,
|
||||
|
||||
fn init(obj: *std.Build.Step.Compile, icon_file: std.Build.LazyPath) *AddExecutableIcon {
|
||||
const b = obj.step.owner;
|
||||
const self = b.allocator.create(AddExecutableIcon) catch @panic("OOM");
|
||||
|
||||
self.obj = obj;
|
||||
self.step = std.Build.Step.init(.{
|
||||
.id = .custom,
|
||||
.name = "add executable icon",
|
||||
.owner = b,
|
||||
.makeFn = make
|
||||
});
|
||||
|
||||
self.icon_file = icon_file;
|
||||
icon_file.addStepDependencies(&self.step);
|
||||
|
||||
const write_files = b.addWriteFiles();
|
||||
self.resource_file = write_files.add("resource-file.rc", "");
|
||||
self.step.dependOn(&write_files.step);
|
||||
|
||||
self.obj.addWin32ResourceFile(.{
|
||||
.file = self.resource_file,
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn make(step: *std.Build.Step, _: std.Progress.Node) !void {
|
||||
const b = step.owner;
|
||||
const self: *AddExecutableIcon = @fieldParentPtr("step", step);
|
||||
|
||||
const resource_file = try std.fs.createFileAbsolute(self.resource_file.getPath(b), .{ });
|
||||
defer resource_file.close();
|
||||
|
||||
const relative_icon_path = try std.fs.path.relative(
|
||||
b.allocator,
|
||||
self.resource_file.dirname().getPath(b),
|
||||
self.icon_file.getPath(b)
|
||||
);
|
||||
std.mem.replaceScalar(u8, relative_icon_path, '\\', '/');
|
||||
|
||||
try resource_file.writeAll("IDI_ICON ICON \"");
|
||||
try resource_file.writeAll(relative_icon_path);
|
||||
try resource_file.writeAll("\"");
|
||||
}
|
||||
};
|
||||
|
||||
fn buildStbImage(b: *std.Build) *std.Build.Module {
|
||||
const module = b.createModule(.{
|
||||
.root_source_file = b.path("libs/stb_image/root.zig"),
|
||||
.link_libc = true
|
||||
});
|
||||
|
||||
module.addCSourceFile(.{ .file = b.path("libs/stb_image/stb_image.c") });
|
||||
module.addIncludePath(b.path("libs/stb_image/"));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
fn buildCuteAseprite(b: *std.Build, raylib_dep: *std.Build.Dependency) *std.Build.Module {
|
||||
const module = b.createModule(.{
|
||||
.root_source_file = b.path("libs/cute_aseprite/root.zig"),
|
||||
});
|
||||
|
||||
module.addCSourceFile(.{ .file = b.path("libs/cute_aseprite/cute_aseprite.c") });
|
||||
module.addIncludePath(b.path("libs/cute_aseprite/"));
|
||||
module.linkLibrary(raylib_dep.artifact("raylib"));
|
||||
module.addImport("raylib", raylib_dep.module("raylib"));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
@ -10,6 +87,18 @@ pub fn build(b: *std.Build) !void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const stb_image_lib = buildStbImage(b);
|
||||
const cute_aseprite_lib = buildCuteAseprite(b, raylib_dep);
|
||||
|
||||
const png_to_icon_tool = b.addExecutable(.{
|
||||
.name = "png-to-icon",
|
||||
.root_source_file = b.path("tools/png-to-icon.zig"),
|
||||
.target = target,
|
||||
});
|
||||
png_to_icon_tool.root_module.addImport("stb_image", stb_image_lib);
|
||||
|
||||
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "daq-view",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
@ -17,12 +106,10 @@ pub fn build(b: *std.Build) !void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.addIncludePath(b.path("src"));
|
||||
exe.addCSourceFile(.{
|
||||
.file = b.path("src/cute_aseprite.c")
|
||||
});
|
||||
exe.linkLibrary(raylib_dep.artifact("raylib"));
|
||||
exe.root_module.addImport("raylib", raylib_dep.module("raylib"));
|
||||
exe.root_module.addImport("stb_image", stb_image_lib);
|
||||
exe.root_module.addImport("cute_aseprite", cute_aseprite_lib);
|
||||
|
||||
const external_compiler_support_dir = try std.process.getEnvVarOwned(b.allocator, "NIEXTCCOMPILERSUPP");
|
||||
exe.addSystemIncludePath(.{ .cwd_relative = try std.fs.path.join(b.allocator, &.{ external_compiler_support_dir, "include" }) });
|
||||
@ -33,13 +120,12 @@ pub fn build(b: *std.Build) !void {
|
||||
if (target.result.os.tag == .windows) {
|
||||
exe.subsystem = if (optimize == .Debug) .Console else .Windows;
|
||||
|
||||
const resource_file = b.addWriteFiles();
|
||||
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
|
||||
png_to_icon_step.addFileArg(b.path("src/assets/icon.png"));
|
||||
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
|
||||
|
||||
// https://www.ryanliptak.com/blog/zig-is-a-windows-resource-compiler/
|
||||
// TODO: Generate icon file at build time
|
||||
exe.addWin32ResourceFile(.{
|
||||
.file = resource_file.add("daq-view.rc", "IDI_ICON ICON \"./src/assets/icon.ico\""),
|
||||
});
|
||||
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
|
||||
exe.step.dependOn(&add_icon_step.step);
|
||||
|
||||
exe.linkSystemLibrary("Comdlg32");
|
||||
}
|
||||
|
28
libs/stb_image/root.zig
Normal file
28
libs/stb_image/root.zig
Normal file
@ -0,0 +1,28 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
extern fn zig_stbi_load_from_memory(buffer: [*]const u8, len: i32, x: ?*i32, y: ?*i32, comp: ?*i32, req_comp: i32) callconv(.C) ?[*]u8;
|
||||
extern fn zig_stbi_image_free(buffer: ?*anyopaque) callconv(.C) void;
|
||||
|
||||
pub const Image = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: []u8,
|
||||
|
||||
pub fn deinit(self: Image) void {
|
||||
zig_stbi_image_free(self.rgba.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn load(buffer: []const u8) !Image {
|
||||
var width: i32 = 0;
|
||||
var height: i32 = 0;
|
||||
const image_rgba = zig_stbi_load_from_memory(buffer.ptr, @intCast(buffer.len), &width, &height, null, 4);
|
||||
if (image_rgba == null) {
|
||||
return error.PNGDecode;
|
||||
}
|
||||
errdefer zig_stbi_image_free(image_rgba);
|
||||
|
||||
const byte_count: u32 = @intCast(width * height * 4);
|
||||
return Image{ .width = @intCast(width), .height = @intCast(height), .rgba = image_rgba.?[0..byte_count] };
|
||||
}
|
23
libs/stb_image/stb_image.c
Normal file
23
libs/stb_image/stb_image.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// TODO: Use zig allocators
|
||||
|
||||
extern void *stb_image_zig_malloc(uint32_t amount);
|
||||
extern void *stb_image_zig_realloc(void *mem, uint32_t amount);
|
||||
extern void stb_image_zig_free(void *mem);
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_STDIO
|
||||
#define STBI_ONLY_PNG
|
||||
#define STB_IMAGE_STATIC
|
||||
#include "stb_image.h"
|
||||
|
||||
void zig_stbi_image_free(void *retval_from_stbi_load)
|
||||
{
|
||||
return stbi_image_free(retval_from_stbi_load);
|
||||
}
|
||||
|
||||
stbi_uc *zig_stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
|
||||
{
|
||||
return stbi_load_from_memory(buffer, len, x, y, comp, req_comp);
|
||||
}
|
7988
libs/stb_image/stb_image.h
Normal file
7988
libs/stb_image/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
const rl = @import("raylib");
|
||||
const srcery = @import("./srcery.zig");
|
||||
const FontFace = @import("./font-face.zig");
|
||||
const Aseprite = @import("./aseprite.zig");
|
||||
const Aseprite = @import("cute_aseprite");
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 10 KiB |
Binary file not shown.
54
tools/png-to-icon.zig
Normal file
54
tools/png-to-icon.zig
Normal file
@ -0,0 +1,54 @@
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user