From 6dbb82c2592d4c33751b86eb12aade01a4b36e69 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sun, 1 Sep 2024 14:02:33 -0500 Subject: [PATCH 01/10] kitty graphics: performance enhancements Improve the performance of Kitty graphics by switching to the WUFFS library for decoding PNG images and for "swizzling" G, GA, and RGB data to RGBA formats needed by the renderers. WUFFS claims 2-3x performance benefits over other implementations, as well as memory-safe operations. Although not thorougly benchmarked, performance is on par with Kitty's graphics decoding. https://github.com/google/wuffs --- build.zig | 16 +++ build.zig.zon | 4 + src/renderer/opengl/image.zig | 64 ++---------- src/terminal/kitty/graphics_image.zig | 50 +++------ src/wuffs/c.zig | 6 ++ src/wuffs/defs.zig | 21 ++++ src/wuffs/error.zig | 3 + src/wuffs/main.zig | 2 + src/wuffs/png.zig | 139 ++++++++++++++++++++++++++ src/wuffs/swizzle.zig | 102 +++++++++++++++++++ 10 files changed, 315 insertions(+), 92 deletions(-) create mode 100644 src/wuffs/c.zig create mode 100644 src/wuffs/defs.zig create mode 100644 src/wuffs/error.zig create mode 100644 src/wuffs/main.zig create mode 100644 src/wuffs/png.zig create mode 100644 src/wuffs/swizzle.zig diff --git a/build.zig b/build.zig index 862d50d39..8bf51def8 100644 --- a/build.zig +++ b/build.zig @@ -1038,6 +1038,7 @@ fn addDeps( .images = false, .text_input = false, }); + const wuffs_dep = b.dependency("wuffs", .{}); // Wasm we do manually since it is such a different build. if (step.rootModuleTarget().cpu.arch == .wasm32) { @@ -1062,6 +1063,21 @@ fn addDeps( step.addIncludePath(b.path("src/apprt/gtk")); } + step.addIncludePath(wuffs_dep.path("release/c")); + step.addCSourceFile( + .{ + .file = wuffs_dep.path("release/c/wuffs-v0.4.c"), + .flags = f: { + const flags = @import("src/wuffs/defs.zig").build; + var a: [flags.len][]const u8 = undefined; + inline for (flags, 0..) |flag, i| { + a[i] = "-D" ++ flag ++ "=1"; + } + break :f &a; + }, + }, + ); + // C++ files step.linkLibCpp(); step.addIncludePath(b.path("src")); diff --git a/build.zig.zon b/build.zig.zon index 08ed9cd71..90bcc8ecd 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -41,6 +41,10 @@ .simdutf = .{ .path = "./pkg/simdutf" }, .utfcpp = .{ .path = "./pkg/utfcpp" }, .zlib = .{ .path = "./pkg/zlib" }, + .wuffs = .{ + .url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.8.tar.gz", + .hash = "12200984439edc817fbcbbaff564020e5104a0d04a2d0f53080700827052de700462", + }, // Shader translation .glslang = .{ .path = "./pkg/glslang" }, diff --git a/src/renderer/opengl/image.zig b/src/renderer/opengl/image.zig index f9904d8b5..414da81bd 100644 --- a/src/renderer/opengl/image.zig +++ b/src/renderer/opengl/image.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const gl = @import("opengl"); +const wuffs = @import("../../wuffs/main.zig"); /// Represents a single image placement on the grid. A placement is a /// request to render an instance of an image. @@ -300,9 +301,8 @@ pub const Image = union(enum) { // RGB needs to be converted to RGBA because Metal textures // don't support RGB. .pending_rgb => |*p| { - // Note: this is the slowest possible way to do this... const data = p.dataSlice(3); - const rgba = try rgbToRgba(alloc, data); + const rgba = try wuffs.swizzle.rgbToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -310,7 +310,7 @@ pub const Image = union(enum) { .replace_rgb => |*r| { const data = r.pending.dataSlice(3); - const rgba = try rgbToRgba(alloc, data); + const rgba = try wuffs.swizzle.rgbToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -319,7 +319,7 @@ pub const Image = union(enum) { // Gray and Gray+Alpha need to be converted to RGBA, too. .pending_gray => |*p| { const data = p.dataSlice(1); - const rgba = try grayToRgba(alloc, data); + const rgba = try wuffs.swizzle.gToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -327,7 +327,7 @@ pub const Image = union(enum) { .replace_gray => |*r| { const data = r.pending.dataSlice(2); - const rgba = try grayToRgba(alloc, data); + const rgba = try wuffs.swizzle.gToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -335,7 +335,7 @@ pub const Image = union(enum) { .pending_gray_alpha => |*p| { const data = p.dataSlice(2); - const rgba = try gaToRgba(alloc, data); + const rgba = try wuffs.swizzle.gaToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -343,7 +343,7 @@ pub const Image = union(enum) { .replace_gray_alpha => |*r| { const data = r.pending.dataSlice(2); - const rgba = try gaToRgba(alloc, data); + const rgba = try wuffs.swizzle.gaToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -351,56 +351,6 @@ pub const Image = union(enum) { } } - fn grayToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const rgba_i = i * 4; - rgba[rgba_i] = data[i]; - rgba[rgba_i + 1] = data[i]; - rgba[rgba_i + 2] = data[i]; - rgba[rgba_i + 3] = 255; - } - - return rgba; - } - - fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len / 2; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const data_i = i * 2; - const rgba_i = i * 4; - rgba[rgba_i] = data[data_i]; - rgba[rgba_i + 1] = data[data_i]; - rgba[rgba_i + 2] = data[data_i]; - rgba[rgba_i + 3] = data[data_i + 1]; - } - - return rgba; - } - - fn rgbToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len / 3; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const data_i = i * 3; - const rgba_i = i * 4; - rgba[rgba_i] = data[data_i]; - rgba[rgba_i + 1] = data[data_i + 1]; - rgba[rgba_i + 2] = data[data_i + 2]; - rgba[rgba_i + 3] = 255; - } - - return rgba; - } - /// Upload the pending image to the GPU and change the state of this /// image to ready. pub fn upload( diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 2dd12ccc6..3af3874c9 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -10,7 +10,7 @@ const command = @import("graphics_command.zig"); const point = @import("../point.zig"); const PageList = @import("../PageList.zig"); const internal_os = @import("../../os/main.zig"); -const stb = @import("../../stb/main.zig"); +const wuffs = @import("../../wuffs/main.zig"); const log = std.log.scoped(.kitty_gfx); @@ -412,47 +412,27 @@ pub const LoadingImage = struct { fn decodePng(self: *LoadingImage, alloc: Allocator) !void { assert(self.image.format == .png); - // Decode PNG - var width: c_int = 0; - var height: c_int = 0; - var bpp: c_int = 0; - const data = stb.stbi_load_from_memory( - self.data.items.ptr, - @intCast(self.data.items.len), - &width, - &height, - &bpp, - 0, - ) orelse return error.InvalidData; - defer stb.stbi_image_free(data); - const len: usize = @intCast(width * height * bpp); - if (len > max_size) { - log.warn("png image too large size={} max_size={}", .{ len, max_size }); - return error.InvalidData; - } + const result = wuffs.png.decode(alloc, self.data.items) catch |err| switch (err) { + error.WuffsError => return error.InvalidData, + else => |e| return e, + }; + defer alloc.free(result.data); - // Validate our bpp - if (bpp < 1 or bpp > 4) { - log.warn("png with unsupported bpp={}", .{bpp}); - return error.UnsupportedDepth; + if (result.data.len > max_size) { + log.warn("png image too large size={} max_size={}", .{ result.data.len, max_size }); + return error.InvalidData; } // Replace our data self.data.deinit(alloc); self.data = .{}; - try self.data.ensureUnusedCapacity(alloc, len); - try self.data.appendSlice(alloc, data[0..len]); + try self.data.ensureUnusedCapacity(alloc, result.data.len); + try self.data.appendSlice(alloc, result.data[0..result.data.len]); // Store updated image dimensions - self.image.width = @intCast(width); - self.image.height = @intCast(height); - self.image.format = switch (bpp) { - 1 => .gray, - 2 => .gray_alpha, - 3 => .rgb, - 4 => .rgba, - else => unreachable, // validated above - }; + self.image.width = result.width; + self.image.height = result.height; + self.image.format = .rgba; } }; @@ -792,6 +772,6 @@ test "image load: png, not compressed, regular file" { var img = try loading.complete(alloc); defer img.deinit(alloc); try testing.expect(img.compression == .none); - try testing.expect(img.format == .rgb); + try testing.expect(img.format == .rgba); try tmp_dir.dir.access(path, .{}); } diff --git a/src/wuffs/c.zig b/src/wuffs/c.zig new file mode 100644 index 000000000..ab86dadcb --- /dev/null +++ b/src/wuffs/c.zig @@ -0,0 +1,6 @@ +pub const c = @cImport({ + for (@import("defs.zig").cimport) |d| { + @cDefine(d, "1"); + } + @cInclude("wuffs-v0.4.c"); +}); diff --git a/src/wuffs/defs.zig b/src/wuffs/defs.zig new file mode 100644 index 000000000..dacaa914d --- /dev/null +++ b/src/wuffs/defs.zig @@ -0,0 +1,21 @@ +//! Define all of the C macros that WUFFS uses to configure itself here so +//! that the settings used to import the C "header" file stay in sync with the +//! settings used build the C "source" file. + +pub const cimport = [_][]const u8{ + "WUFFS_CONFIG__MODULES", + "WUFFS_CONFIG__MODULE__AUX__BASE", + "WUFFS_CONFIG__MODULE__AUX__IMAGE", + "WUFFS_CONFIG__MODULE__BASE", + "WUFFS_CONFIG__MODULE__ADLER32", + "WUFFS_CONFIG__MODULE__CRC32", + "WUFFS_CONFIG__MODULE__DEFLATE", + "WUFFS_CONFIG__MODULE__JPEG", + "WUFFS_CONFIG__MODULE__PNG", + "WUFFS_CONFIG__MODULE__ZLIB", +}; + +// The only difference should be that the "build" defines WUFFS_IMPLEMENTATION +pub const build = [_][]const u8{ + "WUFFS_IMPLEMENTATION", +} ++ cimport; diff --git a/src/wuffs/error.zig b/src/wuffs/error.zig new file mode 100644 index 000000000..609deec9c --- /dev/null +++ b/src/wuffs/error.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub const Error = std.mem.Allocator.Error || error{WuffsError}; diff --git a/src/wuffs/main.zig b/src/wuffs/main.zig new file mode 100644 index 000000000..3f03a4158 --- /dev/null +++ b/src/wuffs/main.zig @@ -0,0 +1,2 @@ +pub const png = @import("png.zig"); +pub const swizzle = @import("swizzle.zig"); diff --git a/src/wuffs/png.zig b/src/wuffs/png.zig new file mode 100644 index 000000000..c827edba5 --- /dev/null +++ b/src/wuffs/png.zig @@ -0,0 +1,139 @@ +const std = @import("std"); + +const c = @import("c.zig").c; +const Error = @import("error.zig").Error; + +const log = std.log.scoped(.wuffs_png); + +pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { + width: u32, + height: u32, + data: []const u8, +} { + log.info("data is {d} bytes", .{data.len}); + + // Work around some wierdness in WUFFS/Zig, there are some structs that + // are defined as "extern" by the Zig compiler which means that Zig won't + // allocate them on the stack at compile time. WUFFS has functions for + // dynamically allocating these structs but they use the C malloc/free. This + // gets around that by using the Zig allocator to allocate enough memory for + // the struct and then casts it to the appropropriate pointer. + + const decoder_buf = try alloc.alloc(u8, c.sizeof__wuffs_png__decoder()); + defer alloc.free(decoder_buf); + + const decoder: ?*c.wuffs_png__decoder = @constCast(@ptrCast(decoder_buf)); + { + const status = c.wuffs_png__decoder__initialize( + decoder, + c.sizeof__wuffs_png__decoder(), + c.WUFFS_VERSION, + 0, + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + + var source_buffer = std.mem.zeroes(c.wuffs_base__io_buffer); + source_buffer.data.ptr = @constCast(@ptrCast(data.ptr)); + source_buffer.data.len = data.len; + source_buffer.meta.wi = data.len; + source_buffer.meta.ri = 0; + source_buffer.meta.pos = 0; + source_buffer.meta.closed = true; + + var image_config = std.mem.zeroes(c.wuffs_base__image_config); + { + const status = c.wuffs_png__decoder__decode_image_config( + decoder, + &image_config, + &source_buffer, + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + + const width = c.wuffs_base__pixel_config__width(&image_config.pixcfg); + const height = c.wuffs_base__pixel_config__height(&image_config.pixcfg); + + c.wuffs_base__pixel_config__set( + &image_config.pixcfg, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, + width, + height, + ); + + const color = c.wuffs_base__color_u32_argb_premul; + + const destination = try alloc.alloc(u8, width * height * @sizeOf(color)); + errdefer alloc.free(destination); + + // temporary buffer for intermediate processing of image + const work_buffer = try alloc.alloc( + u8, + c.wuffs_png__decoder__workbuf_len(decoder).max_incl, + ); + defer alloc.free(work_buffer); + + const work_slice = c.wuffs_base__make_slice_u8( + work_buffer.ptr, + work_buffer.len, + ); + + var pixel_buffer = std.mem.zeroes(c.wuffs_base__pixel_buffer); + { + const status = c.wuffs_base__pixel_buffer__set_from_slice( + &pixel_buffer, + &image_config.pixcfg, + c.wuffs_base__make_slice_u8(destination.ptr, destination.len), + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + + var frame_config = std.mem.zeroes(c.wuffs_base__frame_config); + { + const status = c.wuffs_png__decoder__decode_frame_config( + decoder, + &frame_config, + &source_buffer, + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + + { + const status = c.wuffs_png__decoder__decode_frame( + decoder, + &pixel_buffer, + &source_buffer, + c.WUFFS_BASE__PIXEL_BLEND__SRC_OVER, + work_slice, + null, + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + + return .{ + .width = width, + .height = height, + .data = destination, + }; +} diff --git a/src/wuffs/swizzle.zig b/src/wuffs/swizzle.zig new file mode 100644 index 000000000..6f2c4de2d --- /dev/null +++ b/src/wuffs/swizzle.zig @@ -0,0 +1,102 @@ +const std = @import("std"); + +const c = @import("c.zig").c; +const Error = @import("error.zig").Error; + +const log = std.log.scoped(.wuffs_swizzler); + +pub fn gToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { + return swizzle( + alloc, + src, + c.WUFFS_BASE__PIXEL_FORMAT__Y, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + ); +} + +pub fn gaToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { + return swizzle( + alloc, + src, + c.WUFFS_BASE__PIXEL_FORMAT__YA_PREMUL, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + ); +} + +pub fn rgbToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { + return swizzle( + alloc, + src, + c.WUFFS_BASE__PIXEL_FORMAT__RGB, + c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL, + ); +} + +fn swizzle( + alloc: std.mem.Allocator, + src: []const u8, + comptime src_pixel_format: u32, + comptime dst_pixel_format: u32, +) Error![]u8 { + const src_slice = c.wuffs_base__make_slice_u8( + @constCast(src.ptr), + src.len, + ); + + const dst_fmt = c.wuffs_base__make_pixel_format( + dst_pixel_format, + ); + + std.debug.assert(c.wuffs_base__pixel_format__is_direct(&dst_fmt)); + std.debug.assert(c.wuffs_base__pixel_format__is_interleaved(&dst_fmt)); + std.debug.assert(c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) % 8 == 0); + + const dst_size = c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) / 8; + + const src_fmt = c.wuffs_base__make_pixel_format( + src_pixel_format, + ); + + std.debug.assert(c.wuffs_base__pixel_format__is_direct(&src_fmt)); + std.debug.assert(c.wuffs_base__pixel_format__is_interleaved(&src_fmt)); + std.debug.assert(c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) % 8 == 0); + + const src_size = c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) / 8; + std.debug.assert(src.len % src_size == 0); + + const dst = try alloc.alloc(u8, src.len * dst_size / src_size); + errdefer alloc.free(dst); + + const dst_slice = c.wuffs_base__make_slice_u8( + dst.ptr, + dst.len, + ); + + var swizzler: c.wuffs_base__pixel_swizzler = undefined; + + { + const status = c.wuffs_base__pixel_swizzler__prepare( + &swizzler, + dst_fmt, + c.wuffs_base__empty_slice_u8(), + src_fmt, + c.wuffs_base__empty_slice_u8(), + c.WUFFS_BASE__PIXEL_BLEND__SRC_OVER, + ); + if (!c.wuffs_base__status__is_ok(&status)) { + const e = c.wuffs_base__status__message(&status); + log.err("{s}", .{e}); + return error.WuffsError; + } + } + { + _ = c.wuffs_base__pixel_swizzler__swizzle_interleaved_from_slice( + &swizzler, + dst_slice, + c.wuffs_base__empty_slice_u8(), + src_slice, + ); + } + + return dst; +} From 151bb030deb504f2d0d50da457fbc7431ed5eebe Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sun, 1 Sep 2024 20:01:54 -0500 Subject: [PATCH 02/10] fix typos --- src/wuffs/png.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wuffs/png.zig b/src/wuffs/png.zig index c827edba5..e8cf7cc4f 100644 --- a/src/wuffs/png.zig +++ b/src/wuffs/png.zig @@ -12,12 +12,12 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { } { log.info("data is {d} bytes", .{data.len}); - // Work around some wierdness in WUFFS/Zig, there are some structs that + // Work around some weirdness in WUFFS/Zig, there are some structs that // are defined as "extern" by the Zig compiler which means that Zig won't // allocate them on the stack at compile time. WUFFS has functions for // dynamically allocating these structs but they use the C malloc/free. This // gets around that by using the Zig allocator to allocate enough memory for - // the struct and then casts it to the appropropriate pointer. + // the struct and then casts it to the appropriate pointer. const decoder_buf = try alloc.alloc(u8, c.sizeof__wuffs_png__decoder()); defer alloc.free(decoder_buf); From e6c3ba2cf920fbd093c25f0f72333360c7d80f5c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:46:08 -0700 Subject: [PATCH 03/10] update zig cache hash --- nix/zigCacheHash.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/zigCacheHash.nix b/nix/zigCacheHash.nix index 1c6335189..842f39762 100644 --- a/nix/zigCacheHash.nix +++ b/nix/zigCacheHash.nix @@ -1,3 +1,3 @@ # This file is auto-generated! check build-support/check-zig-cache-hash.sh for # more details. -"sha256-mIUl5j3JxtydoV7ayy3aNrt/jR8+a68lQw6lQimLZEw=" +"sha256-YLopoyRgXV6GYiTiaKt64mH6lWjlKJbi61ck0fO4WvQ=" From 6edeb45e7ed19708e5d261bdeca617c7c9483267 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Sep 2024 00:37:10 -0500 Subject: [PATCH 04/10] kitty graphics: address review comments - move wuffs code from src/ to pkg/ - eliminate stray debug logs - make logs a warning instead of an error - remove initialization of some structs to zero --- build.zig | 21 ++++------------- build.zig.zon | 5 +--- pkg/wuffs/build.zig | 30 ++++++++++++++++++++++++ pkg/wuffs/build.zig.zon | 17 ++++++++++++++ {src/wuffs => pkg/wuffs/src}/c.zig | 0 {src/wuffs => pkg/wuffs/src}/defs.zig | 0 {src/wuffs => pkg/wuffs/src}/error.zig | 0 {src/wuffs => pkg/wuffs/src}/main.zig | 0 {src/wuffs => pkg/wuffs/src}/png.zig | 27 ++++++++++----------- {src/wuffs => pkg/wuffs/src}/swizzle.zig | 3 ++- src/renderer/opengl/image.zig | 2 +- src/terminal/kitty/graphics_image.zig | 2 +- 12 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 pkg/wuffs/build.zig create mode 100644 pkg/wuffs/build.zig.zon rename {src/wuffs => pkg/wuffs/src}/c.zig (100%) rename {src/wuffs => pkg/wuffs/src}/defs.zig (100%) rename {src/wuffs => pkg/wuffs/src}/error.zig (100%) rename {src/wuffs => pkg/wuffs/src}/main.zig (100%) rename {src/wuffs => pkg/wuffs/src}/png.zig (85%) rename {src/wuffs => pkg/wuffs/src}/swizzle.zig (98%) diff --git a/build.zig b/build.zig index 8bf51def8..5227e9895 100644 --- a/build.zig +++ b/build.zig @@ -1038,7 +1038,10 @@ fn addDeps( .images = false, .text_input = false, }); - const wuffs_dep = b.dependency("wuffs", .{}); + const wuffs_dep = b.dependency("wuffs", .{ + .target = target, + .optimize = optimize, + }); // Wasm we do manually since it is such a different build. if (step.rootModuleTarget().cpu.arch == .wasm32) { @@ -1063,21 +1066,6 @@ fn addDeps( step.addIncludePath(b.path("src/apprt/gtk")); } - step.addIncludePath(wuffs_dep.path("release/c")); - step.addCSourceFile( - .{ - .file = wuffs_dep.path("release/c/wuffs-v0.4.c"), - .flags = f: { - const flags = @import("src/wuffs/defs.zig").build; - var a: [flags.len][]const u8 = undefined; - inline for (flags, 0..) |flag, i| { - a[i] = "-D" ++ flag ++ "=1"; - } - break :f &a; - }, - }, - ); - // C++ files step.linkLibCpp(); step.addIncludePath(b.path("src")); @@ -1139,6 +1127,7 @@ fn addDeps( step.root_module.addImport("sentry", sentry_dep.module("sentry")); step.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph")); step.root_module.addImport("vaxis", vaxis_dep.module("vaxis")); + step.root_module.addImport("wuffs", wuffs_dep.module("wuffs")); // Mac Stuff if (step.rootModuleTarget().isDarwin()) { diff --git a/build.zig.zon b/build.zig.zon index 90bcc8ecd..bd86b3427 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -40,11 +40,8 @@ .sentry = .{ .path = "./pkg/sentry" }, .simdutf = .{ .path = "./pkg/simdutf" }, .utfcpp = .{ .path = "./pkg/utfcpp" }, + .wuffs = .{ .path = "./pkg/wuffs" }, .zlib = .{ .path = "./pkg/zlib" }, - .wuffs = .{ - .url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.8.tar.gz", - .hash = "12200984439edc817fbcbbaff564020e5104a0d04a2d0f53080700827052de700462", - }, // Shader translation .glslang = .{ .path = "./pkg/glslang" }, diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig new file mode 100644 index 000000000..967acf75c --- /dev/null +++ b/pkg/wuffs/build.zig @@ -0,0 +1,30 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const wuffs = b.dependency("wuffs", .{}); + + const module = b.addModule("wuffs", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .link_libc = true, + }); + + module.addIncludePath(wuffs.path("release/c")); + module.addCSourceFile( + .{ + .file = wuffs.path("release/c/wuffs-v0.4.c"), + .flags = f: { + const flags = @import("src/defs.zig").build; + var a: [flags.len][]const u8 = undefined; + inline for (flags, 0..) |flag, i| { + a[i] = "-D" ++ flag ++ "=1"; + } + break :f &a; + }, + }, + ); +} diff --git a/pkg/wuffs/build.zig.zon b/pkg/wuffs/build.zig.zon new file mode 100644 index 000000000..f37f6d8a0 --- /dev/null +++ b/pkg/wuffs/build.zig.zon @@ -0,0 +1,17 @@ +.{ + .name = "wuffs", + + .version = "0.0.0", + .dependencies = .{ + .wuffs = .{ + .url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.8.tar.gz", + .hash = "12200984439edc817fbcbbaff564020e5104a0d04a2d0f53080700827052de700462", + }, + }, + + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/src/wuffs/c.zig b/pkg/wuffs/src/c.zig similarity index 100% rename from src/wuffs/c.zig rename to pkg/wuffs/src/c.zig diff --git a/src/wuffs/defs.zig b/pkg/wuffs/src/defs.zig similarity index 100% rename from src/wuffs/defs.zig rename to pkg/wuffs/src/defs.zig diff --git a/src/wuffs/error.zig b/pkg/wuffs/src/error.zig similarity index 100% rename from src/wuffs/error.zig rename to pkg/wuffs/src/error.zig diff --git a/src/wuffs/main.zig b/pkg/wuffs/src/main.zig similarity index 100% rename from src/wuffs/main.zig rename to pkg/wuffs/src/main.zig diff --git a/src/wuffs/png.zig b/pkg/wuffs/src/png.zig similarity index 85% rename from src/wuffs/png.zig rename to pkg/wuffs/src/png.zig index e8cf7cc4f..c81118286 100644 --- a/src/wuffs/png.zig +++ b/pkg/wuffs/src/png.zig @@ -10,8 +10,6 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { height: u32, data: []const u8, } { - log.info("data is {d} bytes", .{data.len}); - // Work around some weirdness in WUFFS/Zig, there are some structs that // are defined as "extern" by the Zig compiler which means that Zig won't // allocate them on the stack at compile time. WUFFS has functions for @@ -32,12 +30,12 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } - var source_buffer = std.mem.zeroes(c.wuffs_base__io_buffer); + var source_buffer: c.wuffs_base__io_buffer = undefined; source_buffer.data.ptr = @constCast(@ptrCast(data.ptr)); source_buffer.data.len = data.len; source_buffer.meta.wi = data.len; @@ -45,7 +43,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { source_buffer.meta.pos = 0; source_buffer.meta.closed = true; - var image_config = std.mem.zeroes(c.wuffs_base__image_config); + var image_config: c.wuffs_base__image_config = undefined; { const status = c.wuffs_png__decoder__decode_image_config( decoder, @@ -54,7 +52,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } @@ -70,9 +68,10 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { height, ); - const color = c.wuffs_base__color_u32_argb_premul; - - const destination = try alloc.alloc(u8, width * height * @sizeOf(color)); + const destination = try alloc.alloc( + u8, + width * height * @sizeOf(c.wuffs_base__color_u32_argb_premul), + ); errdefer alloc.free(destination); // temporary buffer for intermediate processing of image @@ -87,7 +86,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { work_buffer.len, ); - var pixel_buffer = std.mem.zeroes(c.wuffs_base__pixel_buffer); + var pixel_buffer: c.wuffs_base__pixel_buffer = undefined; { const status = c.wuffs_base__pixel_buffer__set_from_slice( &pixel_buffer, @@ -96,12 +95,12 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } - var frame_config = std.mem.zeroes(c.wuffs_base__frame_config); + var frame_config: c.wuffs_base__frame_config = undefined; { const status = c.wuffs_png__decoder__decode_frame_config( decoder, @@ -110,7 +109,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } @@ -126,7 +125,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } diff --git a/src/wuffs/swizzle.zig b/pkg/wuffs/src/swizzle.zig similarity index 98% rename from src/wuffs/swizzle.zig rename to pkg/wuffs/src/swizzle.zig index 6f2c4de2d..8a691c19d 100644 --- a/src/wuffs/swizzle.zig +++ b/pkg/wuffs/src/swizzle.zig @@ -62,6 +62,7 @@ fn swizzle( std.debug.assert(c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) % 8 == 0); const src_size = c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) / 8; + std.debug.assert(src.len % src_size == 0); const dst = try alloc.alloc(u8, src.len * dst_size / src_size); @@ -85,7 +86,7 @@ fn swizzle( ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.err("{s}", .{e}); + log.warn("{s}", .{e}); return error.WuffsError; } } diff --git a/src/renderer/opengl/image.zig b/src/renderer/opengl/image.zig index 414da81bd..85f59f1f3 100644 --- a/src/renderer/opengl/image.zig +++ b/src/renderer/opengl/image.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const gl = @import("opengl"); -const wuffs = @import("../../wuffs/main.zig"); +const wuffs = @import("wuffs"); /// Represents a single image placement on the grid. A placement is a /// request to render an instance of an image. diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 3af3874c9..d1a20f160 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -10,7 +10,7 @@ const command = @import("graphics_command.zig"); const point = @import("../point.zig"); const PageList = @import("../PageList.zig"); const internal_os = @import("../../os/main.zig"); -const wuffs = @import("../../wuffs/main.zig"); +const wuffs = @import("wuffs"); const log = std.log.scoped(.kitty_gfx); From d6e5c8e20f1b9225c9c97c9c39f4a2566c3b4e3b Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Sep 2024 02:48:22 -0500 Subject: [PATCH 05/10] remove unnecessary @constCast --- pkg/wuffs/src/png.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig index c81118286..65258224e 100644 --- a/pkg/wuffs/src/png.zig +++ b/pkg/wuffs/src/png.zig @@ -20,7 +20,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { const decoder_buf = try alloc.alloc(u8, c.sizeof__wuffs_png__decoder()); defer alloc.free(decoder_buf); - const decoder: ?*c.wuffs_png__decoder = @constCast(@ptrCast(decoder_buf)); + const decoder: ?*c.wuffs_png__decoder = @ptrCast(decoder_buf); { const status = c.wuffs_png__decoder__initialize( decoder, From c6e187865af5383758c2aa46cf3d61005c24ebbf Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:27:39 -0700 Subject: [PATCH 06/10] pkg/wuffs: define build defines separately, not in defs.zig --- pkg/wuffs/build.zig | 26 ++++++++++++-------------- pkg/wuffs/src/c.zig | 18 +++++++++++++++--- pkg/wuffs/src/defs.zig | 21 --------------------- 3 files changed, 27 insertions(+), 38 deletions(-) delete mode 100644 pkg/wuffs/src/defs.zig diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig index 967acf75c..ac8bc90eb 100644 --- a/pkg/wuffs/build.zig +++ b/pkg/wuffs/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn build(b: *std.Build) void { +pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -13,18 +13,16 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); + var flags = std.ArrayList([]const u8).init(b.allocator); + defer flags.deinit(); + try flags.append("-DWUFFS_IMPLEMENTATION"); + inline for (@import("src/c.zig").defines) |key| { + try flags.append("-D" ++ key); + } + module.addIncludePath(wuffs.path("release/c")); - module.addCSourceFile( - .{ - .file = wuffs.path("release/c/wuffs-v0.4.c"), - .flags = f: { - const flags = @import("src/defs.zig").build; - var a: [flags.len][]const u8 = undefined; - inline for (flags, 0..) |flag, i| { - a[i] = "-D" ++ flag ++ "=1"; - } - break :f &a; - }, - }, - ); + module.addCSourceFile(.{ + .file = wuffs.path("release/c/wuffs-v0.4.c"), + .flags = flags.items, + }); } diff --git a/pkg/wuffs/src/c.zig b/pkg/wuffs/src/c.zig index ab86dadcb..d94247df3 100644 --- a/pkg/wuffs/src/c.zig +++ b/pkg/wuffs/src/c.zig @@ -1,6 +1,18 @@ pub const c = @cImport({ - for (@import("defs.zig").cimport) |d| { - @cDefine(d, "1"); - } + for (defines) |d| @cDefine(d, "1"); @cInclude("wuffs-v0.4.c"); }); + +/// All the C macros defined so that the header matches the build. +pub const defines: []const []const u8 = &[_][]const u8{ + "WUFFS_CONFIG__MODULES", + "WUFFS_CONFIG__MODULE__AUX__BASE", + "WUFFS_CONFIG__MODULE__AUX__IMAGE", + "WUFFS_CONFIG__MODULE__BASE", + "WUFFS_CONFIG__MODULE__ADLER32", + "WUFFS_CONFIG__MODULE__CRC32", + "WUFFS_CONFIG__MODULE__DEFLATE", + "WUFFS_CONFIG__MODULE__JPEG", + "WUFFS_CONFIG__MODULE__PNG", + "WUFFS_CONFIG__MODULE__ZLIB", +}; diff --git a/pkg/wuffs/src/defs.zig b/pkg/wuffs/src/defs.zig deleted file mode 100644 index dacaa914d..000000000 --- a/pkg/wuffs/src/defs.zig +++ /dev/null @@ -1,21 +0,0 @@ -//! Define all of the C macros that WUFFS uses to configure itself here so -//! that the settings used to import the C "header" file stay in sync with the -//! settings used build the C "source" file. - -pub const cimport = [_][]const u8{ - "WUFFS_CONFIG__MODULES", - "WUFFS_CONFIG__MODULE__AUX__BASE", - "WUFFS_CONFIG__MODULE__AUX__IMAGE", - "WUFFS_CONFIG__MODULE__BASE", - "WUFFS_CONFIG__MODULE__ADLER32", - "WUFFS_CONFIG__MODULE__CRC32", - "WUFFS_CONFIG__MODULE__DEFLATE", - "WUFFS_CONFIG__MODULE__JPEG", - "WUFFS_CONFIG__MODULE__PNG", - "WUFFS_CONFIG__MODULE__ZLIB", -}; - -// The only difference should be that the "build" defines WUFFS_IMPLEMENTATION -pub const build = [_][]const u8{ - "WUFFS_IMPLEMENTATION", -} ++ cimport; From de612934a08cf4d3a7f9b6ab995fae2332c1a6e4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:41:59 -0700 Subject: [PATCH 07/10] some tweaks for wuffs --- pkg/wuffs/src/png.zig | 31 +++++++++++++++------------ pkg/wuffs/src/swizzle.zig | 26 +++++++++++----------- src/terminal/kitty/graphics_image.zig | 7 ++++-- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig index 65258224e..ad47074bd 100644 --- a/pkg/wuffs/src/png.zig +++ b/pkg/wuffs/src/png.zig @@ -1,11 +1,12 @@ const std = @import("std"); - +const Allocator = std.mem.Allocator; const c = @import("c.zig").c; const Error = @import("error.zig").Error; const log = std.log.scoped(.wuffs_png); -pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { +/// Decode a PNG image. +pub fn decode(alloc: Allocator, data: []const u8) Error!struct { width: u32, height: u32, data: []const u8, @@ -30,18 +31,20 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.warn("{s}", .{e}); + log.warn("decode err={s}", .{e}); return error.WuffsError; } } - var source_buffer: c.wuffs_base__io_buffer = undefined; - source_buffer.data.ptr = @constCast(@ptrCast(data.ptr)); - source_buffer.data.len = data.len; - source_buffer.meta.wi = data.len; - source_buffer.meta.ri = 0; - source_buffer.meta.pos = 0; - source_buffer.meta.closed = true; + var source_buffer: c.wuffs_base__io_buffer = .{ + .data = .{ .ptr = @constCast(@ptrCast(data.ptr)), .len = data.len }, + .meta = .{ + .wi = data.len, + .ri = 0, + .pos = 0, + .closed = true, + }, + }; var image_config: c.wuffs_base__image_config = undefined; { @@ -52,7 +55,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.warn("{s}", .{e}); + log.warn("decode err={s}", .{e}); return error.WuffsError; } } @@ -95,7 +98,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.warn("{s}", .{e}); + log.warn("decode err={s}", .{e}); return error.WuffsError; } } @@ -109,7 +112,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.warn("{s}", .{e}); + log.warn("decode err={s}", .{e}); return error.WuffsError; } } @@ -125,7 +128,7 @@ pub fn decode(alloc: std.mem.Allocator, data: []const u8) Error!struct { ); if (!c.wuffs_base__status__is_ok(&status)) { const e = c.wuffs_base__status__message(&status); - log.warn("{s}", .{e}); + log.warn("decode err={s}", .{e}); return error.WuffsError; } } diff --git a/pkg/wuffs/src/swizzle.zig b/pkg/wuffs/src/swizzle.zig index 8a691c19d..8a0e4e82b 100644 --- a/pkg/wuffs/src/swizzle.zig +++ b/pkg/wuffs/src/swizzle.zig @@ -1,11 +1,12 @@ const std = @import("std"); - +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const c = @import("c.zig").c; const Error = @import("error.zig").Error; const log = std.log.scoped(.wuffs_swizzler); -pub fn gToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { +pub fn gToRgba(alloc: Allocator, src: []const u8) Error![]u8 { return swizzle( alloc, src, @@ -14,7 +15,7 @@ pub fn gToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { ); } -pub fn gaToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { +pub fn gaToRgba(alloc: Allocator, src: []const u8) Error![]u8 { return swizzle( alloc, src, @@ -23,7 +24,7 @@ pub fn gaToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { ); } -pub fn rgbToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { +pub fn rgbToRgba(alloc: Allocator, src: []const u8) Error![]u8 { return swizzle( alloc, src, @@ -33,7 +34,7 @@ pub fn rgbToRgba(alloc: std.mem.Allocator, src: []const u8) Error![]u8 { } fn swizzle( - alloc: std.mem.Allocator, + alloc: Allocator, src: []const u8, comptime src_pixel_format: u32, comptime dst_pixel_format: u32, @@ -47,9 +48,9 @@ fn swizzle( dst_pixel_format, ); - std.debug.assert(c.wuffs_base__pixel_format__is_direct(&dst_fmt)); - std.debug.assert(c.wuffs_base__pixel_format__is_interleaved(&dst_fmt)); - std.debug.assert(c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) % 8 == 0); + assert(c.wuffs_base__pixel_format__is_direct(&dst_fmt)); + assert(c.wuffs_base__pixel_format__is_interleaved(&dst_fmt)); + assert(c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) % 8 == 0); const dst_size = c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) / 8; @@ -57,13 +58,13 @@ fn swizzle( src_pixel_format, ); - std.debug.assert(c.wuffs_base__pixel_format__is_direct(&src_fmt)); - std.debug.assert(c.wuffs_base__pixel_format__is_interleaved(&src_fmt)); - std.debug.assert(c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) % 8 == 0); + assert(c.wuffs_base__pixel_format__is_direct(&src_fmt)); + assert(c.wuffs_base__pixel_format__is_interleaved(&src_fmt)); + assert(c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) % 8 == 0); const src_size = c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) / 8; - std.debug.assert(src.len % src_size == 0); + assert(src.len % src_size == 0); const dst = try alloc.alloc(u8, src.len * dst_size / src_size); errdefer alloc.free(dst); @@ -74,7 +75,6 @@ fn swizzle( ); var swizzler: c.wuffs_base__pixel_swizzler = undefined; - { const status = c.wuffs_base__pixel_swizzler__prepare( &swizzler, diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index d1a20f160..5adac9128 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -412,9 +412,12 @@ pub const LoadingImage = struct { fn decodePng(self: *LoadingImage, alloc: Allocator) !void { assert(self.image.format == .png); - const result = wuffs.png.decode(alloc, self.data.items) catch |err| switch (err) { + const result = wuffs.png.decode( + alloc, + self.data.items, + ) catch |err| switch (err) { error.WuffsError => return error.InvalidData, - else => |e| return e, + error.OutOfMemory => return error.OutOfMemory, }; defer alloc.free(result.data); From 8ffa7328a956514e4fc42f8ebd99558413fe52bb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:43:55 -0700 Subject: [PATCH 08/10] renderer/metal: use wuffs for format conversion --- src/renderer/metal/image.zig | 64 ++++-------------------------------- 1 file changed, 7 insertions(+), 57 deletions(-) diff --git a/src/renderer/metal/image.zig b/src/renderer/metal/image.zig index 567570534..9d72cae96 100644 --- a/src/renderer/metal/image.zig +++ b/src/renderer/metal/image.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const objc = @import("objc"); +const wuffs = @import("wuffs"); const mtl = @import("api.zig"); @@ -301,9 +302,8 @@ pub const Image = union(enum) { // RGB needs to be converted to RGBA because Metal textures // don't support RGB. .pending_rgb => |*p| { - // Note: this is the slowest possible way to do this... const data = p.dataSlice(3); - const rgba = try rgbToRgba(alloc, data); + const rgba = try wuffs.swizzle.rgbToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -311,7 +311,7 @@ pub const Image = union(enum) { .replace_rgb => |*r| { const data = r.pending.dataSlice(3); - const rgba = try rgbToRgba(alloc, data); + const rgba = try wuffs.swizzle.rgbToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -320,7 +320,7 @@ pub const Image = union(enum) { // Gray and Gray+Alpha need to be converted to RGBA, too. .pending_gray => |*p| { const data = p.dataSlice(1); - const rgba = try grayToRgba(alloc, data); + const rgba = try wuffs.swizzle.gToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -328,7 +328,7 @@ pub const Image = union(enum) { .replace_gray => |*r| { const data = r.pending.dataSlice(2); - const rgba = try grayToRgba(alloc, data); + const rgba = try wuffs.swizzle.gToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -336,7 +336,7 @@ pub const Image = union(enum) { .pending_gray_alpha => |*p| { const data = p.dataSlice(2); - const rgba = try gaToRgba(alloc, data); + const rgba = try wuffs.swizzle.gaToRgba(alloc, data); alloc.free(data); p.data = rgba.ptr; self.* = .{ .pending_rgba = p.* }; @@ -344,7 +344,7 @@ pub const Image = union(enum) { .replace_gray_alpha => |*r| { const data = r.pending.dataSlice(2); - const rgba = try gaToRgba(alloc, data); + const rgba = try wuffs.swizzle.gaToRgba(alloc, data); alloc.free(data); r.pending.data = rgba.ptr; self.* = .{ .replace_rgba = r.* }; @@ -352,56 +352,6 @@ pub const Image = union(enum) { } } - fn grayToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const rgba_i = i * 4; - rgba[rgba_i] = data[i]; - rgba[rgba_i + 1] = data[i]; - rgba[rgba_i + 2] = data[i]; - rgba[rgba_i + 3] = 255; - } - - return rgba; - } - - fn gaToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len / 2; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const data_i = i * 2; - const rgba_i = i * 4; - rgba[rgba_i] = data[data_i]; - rgba[rgba_i + 1] = data[data_i]; - rgba[rgba_i + 2] = data[data_i]; - rgba[rgba_i + 3] = data[data_i + 1]; - } - - return rgba; - } - - fn rgbToRgba(alloc: Allocator, data: []const u8) ![]u8 { - const pixels = data.len / 3; - var rgba = try alloc.alloc(u8, pixels * 4); - errdefer alloc.free(rgba); - var i: usize = 0; - while (i < pixels) : (i += 1) { - const data_i = i * 3; - const rgba_i = i * 4; - rgba[rgba_i] = data[data_i]; - rgba[rgba_i + 1] = data[data_i + 1]; - rgba[rgba_i + 2] = data[data_i + 2]; - rgba[rgba_i + 3] = 255; - } - - return rgba; - } - /// Upload the pending image to the GPU and change the state of this /// image to ready. pub fn upload( From 781a721fc7b5fe41ff700c919918f5d87d3186c8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:57:17 -0700 Subject: [PATCH 09/10] pkg/wuffs: fix builds for 32-bit systems --- pkg/wuffs/src/png.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig index ad47074bd..8ada7222c 100644 --- a/pkg/wuffs/src/png.zig +++ b/pkg/wuffs/src/png.zig @@ -80,7 +80,13 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!struct { // temporary buffer for intermediate processing of image const work_buffer = try alloc.alloc( u8, - c.wuffs_png__decoder__workbuf_len(decoder).max_incl, + + // The type of this is a u64 on all systems but our allocator + // uses a usize which is a u32 on 32-bit systems. + std.math.cast( + usize, + c.wuffs_png__decoder__workbuf_len(decoder).max_incl, + ) orelse return error.OutOfMemory, ); defer alloc.free(work_buffer); From 5f1c9dd0df02408786e3b3ae98f6177cbcb0eb14 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 2 Sep 2024 20:59:40 -0700 Subject: [PATCH 10/10] pkg/wuffs: use proper apple include paths on darwin targets --- pkg/wuffs/build.zig | 5 +++++ pkg/wuffs/build.zig.zon | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig index ac8bc90eb..36bb5a07c 100644 --- a/pkg/wuffs/build.zig +++ b/pkg/wuffs/build.zig @@ -13,6 +13,11 @@ pub fn build(b: *std.Build) !void { .link_libc = true, }); + if (target.result.isDarwin()) { + const apple_sdk = @import("apple_sdk"); + try apple_sdk.addPaths(b, module); + } + var flags = std.ArrayList([]const u8).init(b.allocator); defer flags.deinit(); try flags.append("-DWUFFS_IMPLEMENTATION"); diff --git a/pkg/wuffs/build.zig.zon b/pkg/wuffs/build.zig.zon index f37f6d8a0..126e43aba 100644 --- a/pkg/wuffs/build.zig.zon +++ b/pkg/wuffs/build.zig.zon @@ -1,14 +1,14 @@ .{ .name = "wuffs", - .version = "0.0.0", .dependencies = .{ .wuffs = .{ .url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.8.tar.gz", .hash = "12200984439edc817fbcbbaff564020e5104a0d04a2d0f53080700827052de700462", }, - }, + .apple_sdk = .{ .path = "../apple-sdk" }, + }, .paths = .{ "build.zig", "build.zig.zon",