aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig
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/jpeg.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig')
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig151
1 files changed, 151 insertions, 0 deletions
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);
+}