summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJeffrey C. Ollie <jeff@ocjtech.us>2024-09-02 00:37:10 -0500
committerMitchell Hashimoto <m@mitchellh.com>2024-09-02 20:47:07 -0700
commit6edeb45e7ed19708e5d261bdeca617c7c9483267 (patch)
treed6081384c58a906a38e9271e480575c28543bb87 /pkg
parente6c3ba2cf920fbd093c25f0f72333360c7d80f5c (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.zig30
-rw-r--r--pkg/wuffs/build.zig.zon17
-rw-r--r--pkg/wuffs/src/c.zig6
-rw-r--r--pkg/wuffs/src/defs.zig21
-rw-r--r--pkg/wuffs/src/error.zig3
-rw-r--r--pkg/wuffs/src/main.zig2
-rw-r--r--pkg/wuffs/src/png.zig138
-rw-r--r--pkg/wuffs/src/swizzle.zig103
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;
+}