Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions core/src/main/java/app/photofox/vipsffm/VImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import java.lang.String;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -9886,13 +9885,12 @@ public static VImage newFromBytes(Arena arena, byte[] bytes, VipsOption... optio
/// Creates a new VImage from raw bytes, mapping directly to the `vips_image_new_from_memory` function, with some checks.
///
/// This is included for narrow use cases where you have image bytes representing partially supported image formats from another library (like DICOM), and you need a way to get them in to libvips without using the built-in source loaders.
/// Note that due to Java FFM limitations, a full copy to native memory must still be performed.
/// Note that this uses the Java FFM [MemorySegment] API to avoid an unnecessary copy.
///
/// This is an advanced method - if possible, use [VImage#newFromFile] and friends instead. If you have bytes to load, you could use [VImage#newFromBytes].
public static VImage newFromMemory(Arena arena, byte[] bytes, int width, int height, int bands,
int format) throws VipsError {
var offHeapBytes = arena.allocateFrom(ValueLayout.JAVA_BYTE, bytes);
var imagePointer = VipsHelper.image_new_from_memory(arena, offHeapBytes, offHeapBytes.byteSize(), width, height, bands, format);
public static VImage newFromMemory(Arena arena, MemorySegment memorySegment, int width,
int height, int bands, int format) throws VipsError {
var imagePointer = VipsHelper.image_new_from_memory(arena, memorySegment, memorySegment.byteSize(), width, height, bands, format);
return new VImage(arena, imagePointer);
}

Expand Down Expand Up @@ -9952,6 +9950,22 @@ public void writeToStream(OutputStream stream, String suffix, VipsOption... opti
this.writeToTarget(target, suffix, options);
}

/// Writes this VImage's raw pixel values to a [MemorySegment], in the following pixel order: RGBRGBRGB etc.
/// It performs a full memory copy of the image, and so provides an image copying option that is thread-safe
/// and independent of other VImage operations.
///
/// In performance-critical scenarios where you need to avoid memory copies, and you are sure about the image's
/// state and lifetime, prefer [VipsHelper#image_get_data] instead.
public MemorySegment writeToMemory() throws VipsError {
var outLengthPointer = arena.allocate(VipsRaw.C_LONG);
var imageMemory = VipsHelper.image_write_to_memory(this.address, outLengthPointer);
var sizeOfImage = outLengthPointer.get(VipsRaw.C_LONG, 0);
if (sizeOfImage < 0) {
throw new VipsError("unexpected image size after write");
}
return imageMemory.reinterpret(arena, VipsRaw::g_free).asSlice(0, sizeOfImage);
}

public static VImage newImage(Arena arena) throws VipsError {
var newImagePointer = VipsHelper.image_new(arena);
return new VImage(arena, newImagePointer);
Expand Down
48 changes: 30 additions & 18 deletions core/src/main/java/app/photofox/vipsffm/VipsHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public static int object_set_from_string(Arena arena, MemorySegment object, Stri
/// ```c
/// void *vips_type_map(GType base, VipsTypeMap2Fn fn, void *a, void *b)
/// ```
public static MemorySegment type_map(Arena arena, long base, MemorySegment fn, MemorySegment a,
public static MemorySegment type_map(long base, MemorySegment fn, MemorySegment a,
MemorySegment b) throws VipsError {
if(!VipsValidation.isValidPointer(a)) {
VipsValidation.throwInvalidInputError("vips_type_map", "a");
Expand All @@ -215,24 +215,22 @@ public static MemorySegment type_map(Arena arena, long base, MemorySegment fn, M
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_type_map", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

/// Binding for:
/// ```c
/// void *vips_type_map_all(GType base, VipsTypeMapFn fn, void *a)
/// ```
public static MemorySegment type_map_all(Arena arena, long base, MemorySegment fn,
MemorySegment a) throws VipsError {
public static MemorySegment type_map_all(long base, MemorySegment fn, MemorySegment a) throws
VipsError {
if(!VipsValidation.isValidPointer(a)) {
VipsValidation.throwInvalidInputError("vips_type_map_all", "a");
}
var result = VipsRaw.vips_type_map_all(base, fn, a);
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_type_map_all", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down Expand Up @@ -326,7 +324,7 @@ public static void area_unref(MemorySegment area) throws VipsError {
/// ```c
/// void *vips_area_get_data(VipsArea *area, size_t *length, int *n, GType *type, size_t *sizeof_type)
/// ```
public static MemorySegment area_get_data(Arena arena, MemorySegment area, MemorySegment length,
public static MemorySegment area_get_data(MemorySegment area, MemorySegment length,
MemorySegment n, MemorySegment type, MemorySegment sizeof_type) throws VipsError {
if(!VipsValidation.isValidPointer(area)) {
VipsValidation.throwInvalidInputError("vips_area_get_data", "area");
Expand All @@ -347,7 +345,6 @@ public static MemorySegment area_get_data(Arena arena, MemorySegment area, Memor
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_area_get_data", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down Expand Up @@ -484,8 +481,8 @@ public static void value_set_area(MemorySegment value, MemorySegment free_fn, Me
/// ```c
/// void *vips_value_get_area(const GValue *value, size_t *length)
/// ```
public static MemorySegment value_get_area(Arena arena, MemorySegment value, MemorySegment length)
throws VipsError {
public static MemorySegment value_get_area(MemorySegment value, MemorySegment length) throws
VipsError {
if(!VipsValidation.isValidPointer(value)) {
VipsValidation.throwInvalidInputError("vips_value_get_area", "value");
}
Expand All @@ -496,7 +493,6 @@ public static MemorySegment value_get_area(Arena arena, MemorySegment value, Mem
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_value_get_area", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down Expand Up @@ -564,8 +560,8 @@ public static void value_set_ref_string(Arena arena, MemorySegment value, String
/// ```c
/// void *vips_value_get_blob(const GValue *value, size_t *length)
/// ```
public static MemorySegment value_get_blob(Arena arena, MemorySegment value, MemorySegment length)
throws VipsError {
public static MemorySegment value_get_blob(MemorySegment value, MemorySegment length) throws
VipsError {
if(!VipsValidation.isValidPointer(value)) {
VipsValidation.throwInvalidInputError("vips_value_get_blob", "value");
}
Expand All @@ -576,7 +572,6 @@ public static MemorySegment value_get_blob(Arena arena, MemorySegment value, Mem
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_value_get_blob", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down Expand Up @@ -626,7 +621,7 @@ public static void value_set_array(MemorySegment value, int n, long type, long s
/// ```c
/// void *vips_value_get_array(const GValue *value, int *n, GType *type, size_t *sizeof_type)
/// ```
public static MemorySegment value_get_array(Arena arena, MemorySegment value, MemorySegment n,
public static MemorySegment value_get_array(MemorySegment value, MemorySegment n,
MemorySegment type, MemorySegment sizeof_type) throws VipsError {
if(!VipsValidation.isValidPointer(value)) {
VipsValidation.throwInvalidInputError("vips_value_get_array", "value");
Expand All @@ -644,7 +639,6 @@ public static MemorySegment value_get_array(Arena arena, MemorySegment value, Me
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_value_get_array", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down Expand Up @@ -1083,6 +1077,25 @@ public static int image_write(MemorySegment image, MemorySegment out) throws Vip
return result;
}

/// Binding for:
/// ```c
/// void *vips_image_write_to_memory(VipsImage *in, size_t *size)
/// ```
public static MemorySegment image_write_to_memory(MemorySegment in, MemorySegment size) throws
VipsError {
if(!VipsValidation.isValidPointer(in)) {
VipsValidation.throwInvalidInputError("vips_image_write_to_memory", "in");
}
if(!VipsValidation.isValidPointer(size)) {
VipsValidation.throwInvalidInputError("vips_image_write_to_memory", "size");
}
var result = VipsRaw.vips_image_write_to_memory(in, size);
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_image_write_to_memory", "result");
}
return result;
}

/// Binding for:
/// ```c
/// gboolean vips_image_hasalpha(VipsImage *image)
Expand Down Expand Up @@ -1597,8 +1610,8 @@ public static long image_get_typeof(Arena arena, MemorySegment image, String nam
/// ```c
/// void *vips_image_map(VipsImage *image, VipsImageMapFn fn, void *a)
/// ```
public static MemorySegment image_map(Arena arena, MemorySegment image, MemorySegment fn,
MemorySegment a) throws VipsError {
public static MemorySegment image_map(MemorySegment image, MemorySegment fn, MemorySegment a)
throws VipsError {
if(!VipsValidation.isValidPointer(image)) {
VipsValidation.throwInvalidInputError("vips_image_map", "image");
}
Expand All @@ -1609,7 +1622,6 @@ public static MemorySegment image_map(Arena arena, MemorySegment image, MemorySe
if(!VipsValidation.isValidPointer(result)) {
VipsValidation.throwInvalidOutputError("vips_image_map", "result");
}
result = result.reinterpret(arena, VipsRaw::g_object_unref);
return result;
}

Expand Down
59 changes: 59 additions & 0 deletions core/src/main/java/app/photofox/vipsffm/jextract/VipsRaw.java
Original file line number Diff line number Diff line change
Expand Up @@ -7238,6 +7238,65 @@ public static int vips_image_write(MemorySegment image, MemorySegment out) {
}
}

private static class vips_image_write_to_memory {
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
VipsRaw.C_POINTER,
VipsRaw.C_POINTER,
VipsRaw.C_POINTER
);

public static final MemorySegment ADDR = VipsRaw.findOrThrow("vips_image_write_to_memory");

public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
}

/**
* Function descriptor for:
* {@snippet lang=c :
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
* }
*/
public static FunctionDescriptor vips_image_write_to_memory$descriptor() {
return vips_image_write_to_memory.DESC;
}

/**
* Downcall method handle for:
* {@snippet lang=c :
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
* }
*/
public static MethodHandle vips_image_write_to_memory$handle() {
return vips_image_write_to_memory.HANDLE;
}

/**
* Address for:
* {@snippet lang=c :
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
* }
*/
public static MemorySegment vips_image_write_to_memory$address() {
return vips_image_write_to_memory.ADDR;
}

/**
* {@snippet lang=c :
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
* }
*/
public static MemorySegment vips_image_write_to_memory(MemorySegment in, MemorySegment size) {
var mh$ = vips_image_write_to_memory.HANDLE;
try {
if (TRACE_DOWNCALLS) {
traceDowncall("vips_image_write_to_memory", in, size);
}
return (MemorySegment)mh$.invokeExact(in, size);
} catch (Throwable ex$) {
throw new AssertionError("should not reach here", ex$);
}
}

private static class vips_image_hasalpha {
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
VipsRaw.C_INT,
Expand Down
Loading
Loading