improve kitty image data handling

This commit is contained in:
2026-06-01 02:14:16 +02:00
parent 6a3d5aa0b0
commit 8ff5ca399a
4 changed files with 54 additions and 11 deletions

View File

@@ -53,7 +53,7 @@ public final class KittyGraphics {
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_COLUMNS), iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_COLUMNS),
iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_ROWS), iterator.getU32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_ROWS),
iterator.getI32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_Z), iterator.getI32(GhosttyLibrary.KITTY_GRAPHICS_PLACEMENT_DATA_Z),
image.map(KittyImage::snapshot), image,
Optional.ofNullable(renderInfo) Optional.ofNullable(renderInfo)
)); ));
} }

View File

@@ -13,16 +13,46 @@ public final class KittyImage {
this.handle = handle; this.handle = handle;
} }
// Cheap metadata accessors: a single native field read each, no pixel-buffer copy. Use these
// to build cache keys; only call data() once you've decided you actually need the bytes.
public long id() {
return library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_ID);
}
public long number() {
return library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_NUMBER);
}
public long width() {
return library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_WIDTH);
}
public long height() {
return library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_HEIGHT);
}
public KittyImageFormat format() {
return KittyImageFormat.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_FORMAT));
}
public KittyImageCompression compression() {
return KittyImageCompression.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_COMPRESSION));
}
// Byte length of the pixel buffer, read without copying it.
public long dataLength() {
return library.kittyImageDataLength(handle);
}
// Copies the pixel buffer out of native memory. Expensive for large images; call only when
// the decoded image isn't already cached.
public byte[] data() {
return library.kittyImageData(handle);
}
public KittyImageSnapshot snapshot() { public KittyImageSnapshot snapshot() {
return new KittyImageSnapshot( return new KittyImageSnapshot(id(), number(), width(), height(), format(), compression(), data());
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_ID),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_NUMBER),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_WIDTH),
library.kittyImageGetU32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_HEIGHT),
KittyImageFormat.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_FORMAT)),
KittyImageCompression.fromNative(library.kittyImageGetI32(handle, GhosttyLibrary.KITTY_IMAGE_DATA_COMPRESSION)),
library.kittyImageData(handle)
);
} }
MemorySegment handle() { MemorySegment handle() {

View File

@@ -15,7 +15,7 @@ public record KittyPlacement(
long columns, long columns,
long rows, long rows,
int z, int z,
Optional<KittyImageSnapshot> image, Optional<KittyImage> image,
Optional<KittyRenderInfo> renderInfo Optional<KittyRenderInfo> renderInfo
) { ) {
} }

View File

@@ -1434,6 +1434,19 @@ public final class GhosttyLibrary {
} }
} }
// Length of the image's pixel buffer without copying it out of native memory. Lets callers
// build a cache key cheaply and only pull the bytes (kittyImageData) on a cache miss.
public long kittyImageDataLength(MemorySegment image) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment outLen = arena.allocate(C_SIZE_T);
int result = (int) kittyGraphicsImageGet.invoke(image, KITTY_IMAGE_DATA_DATA_LEN, outLen);
checkResult("ghostty_kitty_graphics_image_get", result);
return outLen.get(C_SIZE_T, 0);
} catch (Throwable t) {
return rethrow(t);
}
}
public MemorySegment kittyPlacementIteratorNew() { public MemorySegment kittyPlacementIteratorNew() {
try (Arena arena = Arena.ofConfined()) { try (Arena arena = Arena.ofConfined()) {
MemorySegment out = arena.allocate(C_POINTER); MemorySegment out = arena.allocate(C_POINTER);