Skip to content

Commit 0d498ab

Browse files
authored
Add support for vips_image_write_to_memory (#166)
1 parent a2019bd commit 0d498ab

File tree

15 files changed

+660
-332
lines changed

15 files changed

+660
-332
lines changed

core/src/main/java/app/photofox/vipsffm/VImage.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import java.lang.String;
5555
import java.lang.foreign.Arena;
5656
import java.lang.foreign.MemorySegment;
57-
import java.lang.foreign.ValueLayout;
5857
import java.util.ArrayList;
5958
import java.util.Arrays;
6059
import java.util.List;
@@ -9886,13 +9885,12 @@ public static VImage newFromBytes(Arena arena, byte[] bytes, VipsOption... optio
98869885
/// Creates a new VImage from raw bytes, mapping directly to the `vips_image_new_from_memory` function, with some checks.
98879886
///
98889887
/// 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.
9889-
/// Note that due to Java FFM limitations, a full copy to native memory must still be performed.
9888+
/// Note that this uses the Java FFM [MemorySegment] API to avoid an unnecessary copy.
98909889
///
98919890
/// This is an advanced method - if possible, use [VImage#newFromFile] and friends instead. If you have bytes to load, you could use [VImage#newFromBytes].
9892-
public static VImage newFromMemory(Arena arena, byte[] bytes, int width, int height, int bands,
9893-
int format) throws VipsError {
9894-
var offHeapBytes = arena.allocateFrom(ValueLayout.JAVA_BYTE, bytes);
9895-
var imagePointer = VipsHelper.image_new_from_memory(arena, offHeapBytes, offHeapBytes.byteSize(), width, height, bands, format);
9891+
public static VImage newFromMemory(Arena arena, MemorySegment memorySegment, int width,
9892+
int height, int bands, int format) throws VipsError {
9893+
var imagePointer = VipsHelper.image_new_from_memory(arena, memorySegment, memorySegment.byteSize(), width, height, bands, format);
98969894
return new VImage(arena, imagePointer);
98979895
}
98989896

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

9953+
/// Writes this VImage's raw pixel values to a [MemorySegment], in the following pixel order: RGBRGBRGB etc.
9954+
/// It performs a full memory copy of the image, and so provides an image copying option that is thread-safe
9955+
/// and independent of other VImage operations.
9956+
///
9957+
/// In performance-critical scenarios where you need to avoid memory copies, and you are sure about the image's
9958+
/// state and lifetime, prefer [VipsHelper#image_get_data] instead.
9959+
public MemorySegment writeToMemory() throws VipsError {
9960+
var outLengthPointer = arena.allocate(VipsRaw.C_LONG);
9961+
var imageMemory = VipsHelper.image_write_to_memory(this.address, outLengthPointer);
9962+
var sizeOfImage = outLengthPointer.get(VipsRaw.C_LONG, 0);
9963+
if (sizeOfImage < 0) {
9964+
throw new VipsError("unexpected image size after write");
9965+
}
9966+
return imageMemory.reinterpret(arena, VipsRaw::g_free).asSlice(0, sizeOfImage);
9967+
}
9968+
99559969
public static VImage newImage(Arena arena) throws VipsError {
99569970
var newImagePointer = VipsHelper.image_new(arena);
99579971
return new VImage(arena, newImagePointer);

core/src/main/java/app/photofox/vipsffm/VipsHelper.java

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public static int object_set_from_string(Arena arena, MemorySegment object, Stri
203203
/// ```c
204204
/// void *vips_type_map(GType base, VipsTypeMap2Fn fn, void *a, void *b)
205205
/// ```
206-
public static MemorySegment type_map(Arena arena, long base, MemorySegment fn, MemorySegment a,
206+
public static MemorySegment type_map(long base, MemorySegment fn, MemorySegment a,
207207
MemorySegment b) throws VipsError {
208208
if(!VipsValidation.isValidPointer(a)) {
209209
VipsValidation.throwInvalidInputError("vips_type_map", "a");
@@ -215,24 +215,22 @@ public static MemorySegment type_map(Arena arena, long base, MemorySegment fn, M
215215
if(!VipsValidation.isValidPointer(result)) {
216216
VipsValidation.throwInvalidOutputError("vips_type_map", "result");
217217
}
218-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
219218
return result;
220219
}
221220

222221
/// Binding for:
223222
/// ```c
224223
/// void *vips_type_map_all(GType base, VipsTypeMapFn fn, void *a)
225224
/// ```
226-
public static MemorySegment type_map_all(Arena arena, long base, MemorySegment fn,
227-
MemorySegment a) throws VipsError {
225+
public static MemorySegment type_map_all(long base, MemorySegment fn, MemorySegment a) throws
226+
VipsError {
228227
if(!VipsValidation.isValidPointer(a)) {
229228
VipsValidation.throwInvalidInputError("vips_type_map_all", "a");
230229
}
231230
var result = VipsRaw.vips_type_map_all(base, fn, a);
232231
if(!VipsValidation.isValidPointer(result)) {
233232
VipsValidation.throwInvalidOutputError("vips_type_map_all", "result");
234233
}
235-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
236234
return result;
237235
}
238236

@@ -326,7 +324,7 @@ public static void area_unref(MemorySegment area) throws VipsError {
326324
/// ```c
327325
/// void *vips_area_get_data(VipsArea *area, size_t *length, int *n, GType *type, size_t *sizeof_type)
328326
/// ```
329-
public static MemorySegment area_get_data(Arena arena, MemorySegment area, MemorySegment length,
327+
public static MemorySegment area_get_data(MemorySegment area, MemorySegment length,
330328
MemorySegment n, MemorySegment type, MemorySegment sizeof_type) throws VipsError {
331329
if(!VipsValidation.isValidPointer(area)) {
332330
VipsValidation.throwInvalidInputError("vips_area_get_data", "area");
@@ -347,7 +345,6 @@ public static MemorySegment area_get_data(Arena arena, MemorySegment area, Memor
347345
if(!VipsValidation.isValidPointer(result)) {
348346
VipsValidation.throwInvalidOutputError("vips_area_get_data", "result");
349347
}
350-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
351348
return result;
352349
}
353350

@@ -484,8 +481,8 @@ public static void value_set_area(MemorySegment value, MemorySegment free_fn, Me
484481
/// ```c
485482
/// void *vips_value_get_area(const GValue *value, size_t *length)
486483
/// ```
487-
public static MemorySegment value_get_area(Arena arena, MemorySegment value, MemorySegment length)
488-
throws VipsError {
484+
public static MemorySegment value_get_area(MemorySegment value, MemorySegment length) throws
485+
VipsError {
489486
if(!VipsValidation.isValidPointer(value)) {
490487
VipsValidation.throwInvalidInputError("vips_value_get_area", "value");
491488
}
@@ -496,7 +493,6 @@ public static MemorySegment value_get_area(Arena arena, MemorySegment value, Mem
496493
if(!VipsValidation.isValidPointer(result)) {
497494
VipsValidation.throwInvalidOutputError("vips_value_get_area", "result");
498495
}
499-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
500496
return result;
501497
}
502498

@@ -564,8 +560,8 @@ public static void value_set_ref_string(Arena arena, MemorySegment value, String
564560
/// ```c
565561
/// void *vips_value_get_blob(const GValue *value, size_t *length)
566562
/// ```
567-
public static MemorySegment value_get_blob(Arena arena, MemorySegment value, MemorySegment length)
568-
throws VipsError {
563+
public static MemorySegment value_get_blob(MemorySegment value, MemorySegment length) throws
564+
VipsError {
569565
if(!VipsValidation.isValidPointer(value)) {
570566
VipsValidation.throwInvalidInputError("vips_value_get_blob", "value");
571567
}
@@ -576,7 +572,6 @@ public static MemorySegment value_get_blob(Arena arena, MemorySegment value, Mem
576572
if(!VipsValidation.isValidPointer(result)) {
577573
VipsValidation.throwInvalidOutputError("vips_value_get_blob", "result");
578574
}
579-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
580575
return result;
581576
}
582577

@@ -626,7 +621,7 @@ public static void value_set_array(MemorySegment value, int n, long type, long s
626621
/// ```c
627622
/// void *vips_value_get_array(const GValue *value, int *n, GType *type, size_t *sizeof_type)
628623
/// ```
629-
public static MemorySegment value_get_array(Arena arena, MemorySegment value, MemorySegment n,
624+
public static MemorySegment value_get_array(MemorySegment value, MemorySegment n,
630625
MemorySegment type, MemorySegment sizeof_type) throws VipsError {
631626
if(!VipsValidation.isValidPointer(value)) {
632627
VipsValidation.throwInvalidInputError("vips_value_get_array", "value");
@@ -644,7 +639,6 @@ public static MemorySegment value_get_array(Arena arena, MemorySegment value, Me
644639
if(!VipsValidation.isValidPointer(result)) {
645640
VipsValidation.throwInvalidOutputError("vips_value_get_array", "result");
646641
}
647-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
648642
return result;
649643
}
650644

@@ -1083,6 +1077,25 @@ public static int image_write(MemorySegment image, MemorySegment out) throws Vip
10831077
return result;
10841078
}
10851079

1080+
/// Binding for:
1081+
/// ```c
1082+
/// void *vips_image_write_to_memory(VipsImage *in, size_t *size)
1083+
/// ```
1084+
public static MemorySegment image_write_to_memory(MemorySegment in, MemorySegment size) throws
1085+
VipsError {
1086+
if(!VipsValidation.isValidPointer(in)) {
1087+
VipsValidation.throwInvalidInputError("vips_image_write_to_memory", "in");
1088+
}
1089+
if(!VipsValidation.isValidPointer(size)) {
1090+
VipsValidation.throwInvalidInputError("vips_image_write_to_memory", "size");
1091+
}
1092+
var result = VipsRaw.vips_image_write_to_memory(in, size);
1093+
if(!VipsValidation.isValidPointer(result)) {
1094+
VipsValidation.throwInvalidOutputError("vips_image_write_to_memory", "result");
1095+
}
1096+
return result;
1097+
}
1098+
10861099
/// Binding for:
10871100
/// ```c
10881101
/// gboolean vips_image_hasalpha(VipsImage *image)
@@ -1597,8 +1610,8 @@ public static long image_get_typeof(Arena arena, MemorySegment image, String nam
15971610
/// ```c
15981611
/// void *vips_image_map(VipsImage *image, VipsImageMapFn fn, void *a)
15991612
/// ```
1600-
public static MemorySegment image_map(Arena arena, MemorySegment image, MemorySegment fn,
1601-
MemorySegment a) throws VipsError {
1613+
public static MemorySegment image_map(MemorySegment image, MemorySegment fn, MemorySegment a)
1614+
throws VipsError {
16021615
if(!VipsValidation.isValidPointer(image)) {
16031616
VipsValidation.throwInvalidInputError("vips_image_map", "image");
16041617
}
@@ -1609,7 +1622,6 @@ public static MemorySegment image_map(Arena arena, MemorySegment image, MemorySe
16091622
if(!VipsValidation.isValidPointer(result)) {
16101623
VipsValidation.throwInvalidOutputError("vips_image_map", "result");
16111624
}
1612-
result = result.reinterpret(arena, VipsRaw::g_object_unref);
16131625
return result;
16141626
}
16151627

core/src/main/java/app/photofox/vipsffm/jextract/VipsRaw.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7238,6 +7238,65 @@ public static int vips_image_write(MemorySegment image, MemorySegment out) {
72387238
}
72397239
}
72407240

7241+
private static class vips_image_write_to_memory {
7242+
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
7243+
VipsRaw.C_POINTER,
7244+
VipsRaw.C_POINTER,
7245+
VipsRaw.C_POINTER
7246+
);
7247+
7248+
public static final MemorySegment ADDR = VipsRaw.findOrThrow("vips_image_write_to_memory");
7249+
7250+
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
7251+
}
7252+
7253+
/**
7254+
* Function descriptor for:
7255+
* {@snippet lang=c :
7256+
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
7257+
* }
7258+
*/
7259+
public static FunctionDescriptor vips_image_write_to_memory$descriptor() {
7260+
return vips_image_write_to_memory.DESC;
7261+
}
7262+
7263+
/**
7264+
* Downcall method handle for:
7265+
* {@snippet lang=c :
7266+
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
7267+
* }
7268+
*/
7269+
public static MethodHandle vips_image_write_to_memory$handle() {
7270+
return vips_image_write_to_memory.HANDLE;
7271+
}
7272+
7273+
/**
7274+
* Address for:
7275+
* {@snippet lang=c :
7276+
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
7277+
* }
7278+
*/
7279+
public static MemorySegment vips_image_write_to_memory$address() {
7280+
return vips_image_write_to_memory.ADDR;
7281+
}
7282+
7283+
/**
7284+
* {@snippet lang=c :
7285+
* extern void *vips_image_write_to_memory(VipsImage *in, size_t *size)
7286+
* }
7287+
*/
7288+
public static MemorySegment vips_image_write_to_memory(MemorySegment in, MemorySegment size) {
7289+
var mh$ = vips_image_write_to_memory.HANDLE;
7290+
try {
7291+
if (TRACE_DOWNCALLS) {
7292+
traceDowncall("vips_image_write_to_memory", in, size);
7293+
}
7294+
return (MemorySegment)mh$.invokeExact(in, size);
7295+
} catch (Throwable ex$) {
7296+
throw new AssertionError("should not reach here", ex$);
7297+
}
7298+
}
7299+
72417300
private static class vips_image_hasalpha {
72427301
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
72437302
VipsRaw.C_INT,

0 commit comments

Comments
 (0)