diff options
| author | Jeffrey C. Ollie <jeff@ocjtech.us> | 2024-09-02 00:37:10 -0500 |
|---|---|---|
| committer | Mitchell Hashimoto <m@mitchellh.com> | 2024-09-02 20:47:07 -0700 |
| commit | 6edeb45e7ed19708e5d261bdeca617c7c9483267 (patch) | |
| tree | d6081384c58a906a38e9271e480575c28543bb87 /pkg | |
| parent | e6c3ba2cf920fbd093c25f0f72333360c7d80f5c (diff) | |
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
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/wuffs/build.zig | 30 | ||||
| -rw-r--r-- | pkg/wuffs/build.zig.zon | 17 | ||||
| -rw-r--r-- | pkg/wuffs/src/c.zig | 6 | ||||
| -rw-r--r-- | pkg/wuffs/src/defs.zig | 21 | ||||
| -rw-r--r-- | pkg/wuffs/src/error.zig | 3 | ||||
| -rw-r--r-- | pkg/wuffs/src/main.zig | 2 | ||||
| -rw-r--r-- | pkg/wuffs/src/png.zig | 138 | ||||
| -rw-r--r-- | pkg/wuffs/src/swizzle.zig | 103 |
8 files changed, 320 insertions, 0 deletions
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/pkg/wuffs/src/c.zig b/pkg/wuffs/src/c.zig new file mode 100644 index 000000000..ab86dadcb --- /dev/null +++ b/pkg/wuffs/src/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/pkg/wuffs/src/defs.zig b/pkg/wuffs/src/defs.zig new file mode 100644 index 000000000..dacaa914d --- /dev/null +++ b/pkg/wuffs/src/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/pkg/wuffs/src/error.zig b/pkg/wuffs/src/error.zig new file mode 100644 index 000000000..609deec9c --- /dev/null +++ b/pkg/wuffs/src/error.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub const Error = std.mem.Allocator.Error || error{WuffsError}; diff --git a/pkg/wuffs/src/main.zig b/pkg/wuffs/src/main.zig new file mode 100644 index 000000000..3f03a4158 --- /dev/null +++ b/pkg/wuffs/src/main.zig @@ -0,0 +1,2 @@ +pub const png = @import("png.zig"); +pub const swizzle = @import("swizzle.zig"); diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig new file mode 100644 index 000000000..c81118286 --- /dev/null +++ b/pkg/wuffs/src/png.zig @@ -0,0 +1,138 @@ +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, +} { + // 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 appropriate 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.warn("{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 image_config: c.wuffs_base__image_config = undefined; + { + 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.warn("{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 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 + 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: c.wuffs_base__pixel_buffer = undefined; + { + 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.warn("{s}", .{e}); + return error.WuffsError; + } + } + + var frame_config: c.wuffs_base__frame_config = undefined; + { + 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.warn("{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.warn("{s}", .{e}); + return error.WuffsError; + } + } + + return .{ + .width = width, + .height = height, + .data = destination, + }; +} diff --git a/pkg/wuffs/src/swizzle.zig b/pkg/wuffs/src/swizzle.zig new file mode 100644 index 000000000..8a691c19d --- /dev/null +++ b/pkg/wuffs/src/swizzle.zig @@ -0,0 +1,103 @@ +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.warn("{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; +} |
