Files
ghostty/pkg/wuffs/src/jpeg.zig
Chris Marchesi e8525c0fd9 Update to Zig 0.16.0
This commit represents the majority of the work necessary to upgrade
Ghostty to use Zig 0.16.0.

Key parts:

* In addition to its previous responsibilities, the global state now
  houses state for global I/O implementations and the process
  environment. It is now also utilized in the main application along
  with the C library. Where necessary, global state is isolated from key
  parts of the implementation (e.g., in libghostty subsystems), and it's
  expected that this list will grow.

* We currently manage our own C translation layer where necessary. In
  these cases, cImport has been removed in favor of the new external
  translate-c package. Due to fixes that have needed be made to properly
  translate the dependencies that were swapped out, as mentioned, we
  have had to backport fixes from the current translate-c package (and
  the upstream Arocc dependency). We will host this ourselves until Zig
  0.17.0 is released with these fixes.

* Where necessary (only a small number of cases), some stdlib code from
  0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in
  lib/compat.

Co-authored-by: Leah Amelia Chen <hi@pluie.me>
2026-07-21 12:35:05 -07:00

152 lines
4.6 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("wuffs_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);
}