aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/pkg/wuffs/src
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/wuffs/src
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/wuffs/src')
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/c.zig18
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/error.zig13
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig151
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/main.zig20
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/png.zig151
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig121
6 files changed, 474 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/c.zig b/vendor/libghostty-vt/pkg/wuffs/src/c.zig
new file mode 100644
index 00000000..d94247df
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/c.zig
@@ -0,0 +1,18 @@
+pub const c = @cImport({
+ 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/vendor/libghostty-vt/pkg/wuffs/src/error.zig b/vendor/libghostty-vt/pkg/wuffs/src/error.zig
new file mode 100644
index 00000000..0be55cf4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/error.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+
+const c = @import("c.zig").c;
+
+pub const Error = std.mem.Allocator.Error || error{ WuffsError, Overflow };
+
+pub fn check(log: anytype, status: *const c.struct_wuffs_base__status__struct) error{WuffsError}!void {
+ if (!c.wuffs_base__status__is_ok(status)) {
+ const e = c.wuffs_base__status__message(status);
+ log.warn("decode err={s}", .{e});
+ return error.WuffsError;
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig b/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig
new file mode 100644
index 00000000..69d91c8a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig
@@ -0,0 +1,151 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const Error = @import("error.zig").Error;
+const check = @import("error.zig").check;
+const ImageData = @import("main.zig").ImageData;
+const maximum_image_size = @import("main.zig").maximum_image_size;
+const mul = std.math.mul;
+
+const log = std.log.scoped(.wuffs_jpeg);
+
+/// Decode a JPEG image.
+pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
+ // 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_jpeg__decoder());
+ defer alloc.free(decoder_buf);
+
+ const decoder: ?*c.wuffs_jpeg__decoder = @ptrCast(decoder_buf);
+ {
+ const status = c.wuffs_jpeg__decoder__initialize(
+ decoder,
+ c.sizeof__wuffs_jpeg__decoder(),
+ c.WUFFS_VERSION,
+ 0,
+ );
+ try check(log, &status);
+ }
+
+ var source_buffer: c.wuffs_base__io_buffer = .{
+ .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len },
+ .meta = .{
+ .wi = data.len,
+ .ri = 0,
+ .pos = 0,
+ .closed = true,
+ },
+ };
+
+ var image_config: c.wuffs_base__image_config = undefined;
+ {
+ const status = c.wuffs_jpeg__decoder__decode_image_config(
+ decoder,
+ &image_config,
+ &source_buffer,
+ );
+ try check(log, &status);
+ }
+
+ 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_NONPREMUL,
+ c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
+ width,
+ height,
+ );
+
+ const size: usize = try mul(
+ usize,
+ try mul(usize, width, height),
+ @sizeOf(c.wuffs_base__color_u32_argb_premul),
+ );
+
+ if (size > maximum_image_size) {
+ log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
+ return error.Overflow;
+ }
+
+ const destination = try alloc.alloc(
+ u8,
+ size,
+ );
+ errdefer alloc.free(destination);
+
+ // temporary buffer for intermediate processing of image
+ const work_buffer = try alloc.alloc(
+ u8,
+
+ // 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_jpeg__decoder__workbuf_len(decoder).max_incl,
+ ) orelse return error.OutOfMemory,
+ );
+ 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),
+ );
+ try check(log, &status);
+ }
+
+ {
+ const status = c.wuffs_jpeg__decoder__decode_frame(
+ decoder,
+ &pixel_buffer,
+ &source_buffer,
+ c.WUFFS_BASE__PIXEL_BLEND__SRC,
+ work_slice,
+ null,
+ );
+ try check(log, &status);
+ }
+
+ return .{
+ .width = width,
+ .height = height,
+ .data = destination,
+ };
+}
+
+test "jpeg_decode_000000" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#000000.jpg"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0, 0, 255 }, data.data);
+}
+
+test "jpeg_decode_FFFFFF" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#FFFFFF.jpg"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
+}
+
+test "jpeg: too big" {
+ const data = decode(std.testing.allocator, @embedFile("too_big.jpg"));
+ try std.testing.expectError(error.Overflow, data);
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/main.zig b/vendor/libghostty-vt/pkg/wuffs/src/main.zig
new file mode 100644
index 00000000..207d83f9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/main.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+
+pub const png = @import("png.zig");
+pub const jpeg = @import("jpeg.zig");
+pub const swizzle = @import("swizzle.zig");
+pub const Error = @import("error.zig").Error;
+
+/// The maximum image size, based on the 4G limit of Ghostty's
+/// `image-storage-limit` config.
+pub const maximum_image_size = 4 * 1024 * 1024 * 1024;
+
+pub const ImageData = struct {
+ width: u32,
+ height: u32,
+ data: []u8,
+};
+
+test {
+ std.testing.refAllDeclsRecursive(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/png.zig b/vendor/libghostty-vt/pkg/wuffs/src/png.zig
new file mode 100644
index 00000000..57a0e63b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/png.zig
@@ -0,0 +1,151 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const Error = @import("error.zig").Error;
+const check = @import("error.zig").check;
+const ImageData = @import("main.zig").ImageData;
+const maximum_image_size = @import("main.zig").maximum_image_size;
+const mul = std.math.mul;
+
+const log = std.log.scoped(.wuffs_png);
+
+/// Decode a PNG image.
+pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
+ // 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 = @ptrCast(decoder_buf);
+ {
+ const status = c.wuffs_png__decoder__initialize(
+ decoder,
+ c.sizeof__wuffs_png__decoder(),
+ c.WUFFS_VERSION,
+ 0,
+ );
+ try check(log, &status);
+ }
+
+ var source_buffer: c.wuffs_base__io_buffer = .{
+ .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len },
+ .meta = .{
+ .wi = data.len,
+ .ri = 0,
+ .pos = 0,
+ .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,
+ );
+ try check(log, &status);
+ }
+
+ 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_NONPREMUL,
+ c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
+ width,
+ height,
+ );
+
+ const size: usize = try mul(
+ usize,
+ try mul(usize, width, height),
+ @sizeOf(c.wuffs_base__color_u32_argb_premul),
+ );
+
+ if (size > maximum_image_size) {
+ log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
+ return error.Overflow;
+ }
+
+ const destination = try alloc.alloc(
+ u8,
+ size,
+ );
+ errdefer alloc.free(destination);
+
+ // temporary buffer for intermediate processing of image
+ const work_buffer = try alloc.alloc(
+ u8,
+
+ // 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);
+
+ 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),
+ );
+ try check(log, &status);
+ }
+
+ {
+ const status = c.wuffs_png__decoder__decode_frame(
+ decoder,
+ &pixel_buffer,
+ &source_buffer,
+ c.WUFFS_BASE__PIXEL_BLEND__SRC,
+ work_slice,
+ null,
+ );
+ try check(log, &status);
+ }
+
+ return .{
+ .width = width,
+ .height = height,
+ .data = destination,
+ };
+}
+
+test "png_decode_000000" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#000000.png"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0, 0, 255 }, data.data);
+}
+
+test "png_decode_FFFFFF" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#FFFFFF.png"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
+}
+
+test "png: too big" {
+ const data = decode(std.testing.allocator, @embedFile("too_big.png"));
+ try std.testing.expectError(error.Overflow, data);
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig b/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig
new file mode 100644
index 00000000..352cf2b5
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig
@@ -0,0 +1,121 @@
+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: 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: 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: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGB,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn bgrToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__BGR,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn bgraToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+fn swizzle(
+ alloc: 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,
+ );
+
+ 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;
+
+ const src_fmt = c.wuffs_base__make_pixel_format(
+ src_pixel_format,
+ );
+
+ 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;
+
+ 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,
+ );
+ 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;
+}