mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
terminal/kitty-gfx: move all image decompression to loadingimage
This commit is contained in:
@ -107,13 +107,68 @@ pub const LoadingImage = struct {
|
|||||||
|
|
||||||
/// Complete the chunked image, returning a completed image.
|
/// Complete the chunked image, returning a completed image.
|
||||||
pub fn complete(self: *LoadingImage, alloc: Allocator) !Image {
|
pub fn complete(self: *LoadingImage, alloc: Allocator) !Image {
|
||||||
|
const img = &self.image;
|
||||||
|
|
||||||
|
// Validate our dimensions.
|
||||||
|
if (img.width == 0 or img.height == 0) return error.DimensionsRequired;
|
||||||
|
if (img.width > max_dimension or img.height > max_dimension) return error.DimensionsTooLarge;
|
||||||
|
|
||||||
|
// Decompress the data if it is compressed.
|
||||||
|
try self.decompress(alloc);
|
||||||
|
|
||||||
|
// Data length must be what we expect
|
||||||
|
const bpp: u32 = switch (img.format) {
|
||||||
|
.rgb => 3,
|
||||||
|
.rgba => 4,
|
||||||
|
};
|
||||||
|
const expected_len = img.width * img.height * bpp;
|
||||||
|
const actual_len = self.data.items.len;
|
||||||
|
std.log.debug(
|
||||||
|
"complete image id={} width={} height={} bpp={} expected_len={} actual_len={}",
|
||||||
|
.{ img.id, img.width, img.height, bpp, expected_len, actual_len },
|
||||||
|
);
|
||||||
|
if (actual_len != expected_len) return error.InvalidData;
|
||||||
|
|
||||||
|
// Everything looks good, copy the image data over.
|
||||||
var result = self.image;
|
var result = self.image;
|
||||||
result.data = try self.data.toOwnedSlice(alloc);
|
result.data = try self.data.toOwnedSlice(alloc);
|
||||||
errdefer result.deinit(alloc);
|
errdefer result.deinit(alloc);
|
||||||
self.image = .{};
|
self.image = .{};
|
||||||
try result.complete(alloc);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decompress the data in-place.
|
||||||
|
fn decompress(self: *LoadingImage, alloc: Allocator) !void {
|
||||||
|
return switch (self.image.compression) {
|
||||||
|
.none => {},
|
||||||
|
.zlib_deflate => self.decompressZlib(alloc),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decompressZlib(self: *LoadingImage, alloc: Allocator) !void {
|
||||||
|
// Open our zlib stream
|
||||||
|
var fbs = std.io.fixedBufferStream(self.data.items);
|
||||||
|
var stream = std.compress.zlib.decompressStream(alloc, fbs.reader()) catch |err| {
|
||||||
|
log.warn("zlib decompression failed: {}", .{err});
|
||||||
|
return error.DecompressionFailed;
|
||||||
|
};
|
||||||
|
defer stream.deinit();
|
||||||
|
|
||||||
|
// Write it to an array list
|
||||||
|
var list = std.ArrayList(u8).init(alloc);
|
||||||
|
errdefer list.deinit();
|
||||||
|
stream.reader().readAllArrayList(&list, max_size) catch |err| {
|
||||||
|
log.warn("failed to read decompressed data: {}", .{err});
|
||||||
|
return error.DecompressionFailed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Empty our current data list, take ownership over managed array list
|
||||||
|
self.data.deinit(alloc);
|
||||||
|
self.data = .{ .items = list.items, .capacity = list.capacity };
|
||||||
|
|
||||||
|
// Make sure we note that our image is no longer compressed
|
||||||
|
self.image.compression = .none;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Image represents a single fully loaded image.
|
/// Image represents a single fully loaded image.
|
||||||
@ -137,6 +192,17 @@ pub const Image = struct {
|
|||||||
UnsupportedMedium,
|
UnsupportedMedium,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *Image, alloc: Allocator) void {
|
||||||
|
if (self.data.len > 0) alloc.free(self.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mostly for logging
|
||||||
|
pub fn withoutData(self: *const Image) Image {
|
||||||
|
var copy = self.*;
|
||||||
|
copy.data = "";
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
/// Debug function to write the data to a file. This is useful for
|
/// Debug function to write the data to a file. This is useful for
|
||||||
/// capturing some test data for unit tests.
|
/// capturing some test data for unit tests.
|
||||||
pub fn debugDump(self: Image) !void {
|
pub fn debugDump(self: Image) !void {
|
||||||
@ -161,74 +227,6 @@ pub const Image = struct {
|
|||||||
const writer = f.writer();
|
const writer = f.writer();
|
||||||
try writer.writeAll(self.data);
|
try writer.writeAll(self.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decompress the image data in-place.
|
|
||||||
fn decompress(self: *Image, alloc: Allocator) !void {
|
|
||||||
return switch (self.compression) {
|
|
||||||
.none => {},
|
|
||||||
.zlib_deflate => self.decompressZlib(alloc),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decompressZlib(self: *Image, alloc: Allocator) !void {
|
|
||||||
// Open our zlib stream
|
|
||||||
var fbs = std.io.fixedBufferStream(self.data);
|
|
||||||
var stream = std.compress.zlib.decompressStream(alloc, fbs.reader()) catch |err| {
|
|
||||||
log.warn("zlib decompression failed: {}", .{err});
|
|
||||||
return error.DecompressionFailed;
|
|
||||||
};
|
|
||||||
defer stream.deinit();
|
|
||||||
|
|
||||||
// Write it to an array list
|
|
||||||
var list = std.ArrayList(u8).init(alloc);
|
|
||||||
defer list.deinit();
|
|
||||||
stream.reader().readAllArrayList(&list, max_size) catch |err| {
|
|
||||||
log.warn("failed to read decompressed data: {}", .{err});
|
|
||||||
return error.DecompressionFailed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Swap our data out
|
|
||||||
alloc.free(self.data);
|
|
||||||
self.data = "";
|
|
||||||
self.data = try list.toOwnedSlice();
|
|
||||||
self.compression = .none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Complete the image. This must be called after loading and after
|
|
||||||
/// being sure the data is complete (not chunked).
|
|
||||||
pub fn complete(self: *Image, alloc: Allocator) !void {
|
|
||||||
const bpp: u32 = switch (self.format) {
|
|
||||||
.rgb => 3,
|
|
||||||
.rgba => 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Validate our dimensions.
|
|
||||||
if (self.width == 0 or self.height == 0) return error.DimensionsRequired;
|
|
||||||
if (self.width > max_dimension or self.height > max_dimension) return error.DimensionsTooLarge;
|
|
||||||
|
|
||||||
// Decompress the data if it is compressed.
|
|
||||||
try self.decompress(alloc);
|
|
||||||
|
|
||||||
// Data length must be what we expect
|
|
||||||
const expected_len = self.width * self.height * bpp;
|
|
||||||
const actual_len = self.data.len;
|
|
||||||
std.log.debug(
|
|
||||||
"complete image id={} width={} height={} bpp={} expected_len={} actual_len={}",
|
|
||||||
.{ self.id, self.width, self.height, bpp, expected_len, actual_len },
|
|
||||||
);
|
|
||||||
if (actual_len != expected_len) return error.InvalidData;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Image, alloc: Allocator) void {
|
|
||||||
if (self.data.len > 0) alloc.free(self.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mostly for logging
|
|
||||||
pub fn withoutData(self: *const Image) Image {
|
|
||||||
var copy = self.*;
|
|
||||||
copy.data = "";
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper to base64 decode some data. No data is freed.
|
/// Helper to base64 decode some data. No data is freed.
|
||||||
|
Reference in New Issue
Block a user