Skip to content

Commit c1ff1d2

Browse files
authored
Add sample for arrayjoin (#70)
1 parent a09eeef commit c1ff1d2

File tree

4 files changed

+97
-23
lines changed

4 files changed

+97
-23
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ repositories {
2020
}
2121

2222
dependencies {
23-
implementation("app.photofox.vips-ffm:vips-ffm-core:0.5.5")
23+
implementation("app.photofox.vips-ffm:vips-ffm-core:0.5.6")
2424
}
2525
```
2626

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

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package app.photofox.vipsffm;
22

3-
import app.photofox.vipsffm.jextract.GValue;
4-
import app.photofox.vipsffm.jextract.VipsRaw;
3+
import app.photofox.vipsffm.jextract.*;
54

65
import java.lang.foreign.Arena;
76
import java.lang.foreign.MemoryLayout;
@@ -30,27 +29,37 @@ public static void invokeOperation(
3029
List<? extends VipsOption> args
3130
) throws VipsError {
3231
// https://www.libvips.org/API/current/binding.html
33-
var rawOperation = VipsRaw.vips_operation_new(arena.allocateFrom(nickname));
34-
if (!VipsValidation.isValidPointer(rawOperation)) {
32+
var operation = VipsRaw.vips_operation_new(arena.allocateFrom(nickname));
33+
if (!VipsValidation.isValidPointer(operation)) {
3534
VipsValidation.throwVipsError(String.format("failed to create operation for %s", nickname));
3635
}
37-
final var operation = rawOperation.reinterpret(arena, VipsRaw::g_object_unref);
36+
3837
if (stringOptions != null && !stringOptions.isBlank()) {
39-
VipsHelper.object_set_from_string(arena, operation, stringOptions);
38+
var result = VipsHelper.object_set_from_string(arena, operation, stringOptions);
39+
if (!VipsValidation.isValidResult(result)) {
40+
VipsHelper.object_unref_outputs(operation);
41+
VipsRaw.g_object_unref(operation);
42+
VipsValidation.throwVipsError(String.format("failed to create cached operation for %s", nickname));
43+
}
4044
}
4145

4246
setInputOptions(arena, args, operation);
4347

44-
var operationResult = VipsRaw.vips_cache_operation_build(operation);
45-
if (!VipsValidation.isValidPointer(operationResult)) {
48+
var cachedOperation = VipsRaw.vips_cache_operation_build(operation);
49+
if (!VipsValidation.isValidPointer(cachedOperation)) {
50+
VipsHelper.object_unref_outputs(operation);
51+
VipsRaw.g_object_unref(operation);
4652
VipsValidation.throwVipsError(String.format("failed to create cached operation for %s", nickname));
4753
}
48-
operationResult.reinterpret(arena, (it) -> {
49-
VipsHelper.object_unref_outputs(it);
50-
VipsRaw.g_object_unref(it);
51-
});
54+
VipsRaw.g_object_unref(operation);
55+
operation = cachedOperation;
5256

53-
readOutputOptions(arena, args, operationResult);
57+
try {
58+
readOutputOptions(arena, args, operation);
59+
} finally {
60+
VipsHelper.object_unref_outputs(operation);
61+
VipsRaw.g_object_unref(operation);
62+
}
5463
}
5564

5665
private static void readOutputOptions(
@@ -66,7 +75,21 @@ private static void readOutputOptions(
6675
private static void readOutputOption(Arena arena, MemorySegment operation, VipsOption option) {
6776
var optionKey = option.key();
6877
var keyCString = arena.allocateFrom(optionKey);
69-
var gvaluePointer = GValue.allocate(arena).reinterpret(arena, VipsRaw::g_value_unset);
78+
var gvaluePointer = GValue.allocate(arena);
79+
try {
80+
readGValueAndSetOptionValue(arena, operation, option, gvaluePointer, keyCString);
81+
} finally {
82+
VipsRaw.g_value_unset(gvaluePointer);
83+
}
84+
}
85+
86+
private static void readGValueAndSetOptionValue(
87+
Arena arena,
88+
MemorySegment operation,
89+
VipsOption option,
90+
MemorySegment gvaluePointer,
91+
MemorySegment keyCString
92+
) {
7093
switch (option) {
7194
case VipsOption.Int o -> {
7295
VipsRaw.g_value_init(gvaluePointer, G_TYPE_INT());
@@ -210,7 +233,20 @@ private static void setInputOption(
210233
) {
211234
var optionKey = option.key();
212235
var keyCString = arena.allocateFrom(optionKey);
213-
var gvaluePointer = GValue.allocate(arena).reinterpret(arena, VipsRaw::g_value_unset);
236+
var gvaluePointer = GValue.allocate(arena);
237+
try {
238+
setGValueFromOption(arena, option, gvaluePointer);
239+
VipsRaw.g_object_set_property(operation, keyCString, gvaluePointer);
240+
} finally {
241+
VipsRaw.g_value_unset(gvaluePointer);
242+
}
243+
}
244+
245+
private static void setGValueFromOption(
246+
Arena arena,
247+
VipsOption option,
248+
MemorySegment gvaluePointer
249+
) {
214250
switch (option) {
215251
case VipsOption.Int o -> {
216252
var value = o.valueOrThrow();
@@ -281,14 +317,15 @@ private static void setInputOption(
281317
var value = o.valueOrThrow();
282318
var valueSize = value.size();
283319
VipsRaw.g_value_init(gvaluePointer, vips_array_image_get_type());
284-
var arrayPointer = arena.allocate(C_POINTER, valueSize);
320+
VipsRaw.vips_value_set_array_image(gvaluePointer, valueSize);
321+
var vipsArrayPointer = VipsRaw.vips_value_get_array_image(gvaluePointer, MemorySegment.NULL);
285322
for (int i = 0; i < valueSize; i++) {
286323
var imageAddress = value.get(i).address;
287-
arrayPointer.setAtIndex(C_POINTER, i, imageAddress);
324+
// we must ref inputs to vips_array_image_get_type
325+
// it should not be unreffed here - it will get unreffed above
326+
VipsRaw.g_object_ref(imageAddress);
327+
vipsArrayPointer.setAtIndex(C_POINTER, i, imageAddress);
288328
}
289-
VipsRaw.vips_value_set_array_image(gvaluePointer, valueSize);
290-
var vipsArrayPointer = VipsRaw.vips_value_get_array_image(gvaluePointer, MemorySegment.NULL);
291-
vipsArrayPointer.set(C_POINTER, 0, arrayPointer);
292329
}
293330
case VipsOption.Interpolate o -> {
294331
var value = o.valueOrThrow();
@@ -301,7 +338,6 @@ private static void setInputOption(
301338
VipsRaw.g_value_set_int(gvaluePointer, value.getRawValue());
302339
}
303340
}
304-
VipsRaw.g_object_set_property(operation, keyCString, gvaluePointer);
305341
}
306342

307343
public static MemorySegment makeCharStarArray(Arena arena, List<String> strings) {

sample/src/main/kotlin/vipsffm/SampleRunner.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ object SampleRunner {
2222
VImageCopyWriteSample,
2323
VOptionHyphenSample,
2424
VImageCachingSample,
25-
VImageBlobSample
25+
VImageBlobSample,
26+
VImageArrayJoinSample
2627
)
2728
val sampleParentRunPath = Paths.get("sample_run")
2829
if (Files.exists(sampleParentRunPath)) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package vipsffm
2+
3+
import app.photofox.vipsffm.VImage
4+
import app.photofox.vipsffm.VSource
5+
import app.photofox.vipsffm.VipsHelper
6+
import app.photofox.vipsffm.VipsOption
7+
import app.photofox.vipsffm.enums.VipsAlign
8+
import java.lang.foreign.Arena
9+
import java.nio.file.Path
10+
import kotlin.io.path.absolutePathString
11+
12+
object VImageArrayJoinSample: RunnableSample {
13+
14+
override fun run(arena: Arena, workingDirectory: Path): Result<Unit> {
15+
val outputPath = workingDirectory.resolve("rabbit_two.jpg")
16+
val image = VImage.newFromFile(
17+
arena,
18+
"sample/src/main/resources/sample_images/rabbit.jpg"
19+
)
20+
.thumbnailImage(400)
21+
22+
image
23+
.arrayjoin(
24+
listOf(image, image),
25+
VipsOption.Enum("valign", VipsAlign.ALIGN_CENTRE),
26+
VipsOption.Enum("halign", VipsAlign.ALIGN_CENTRE),
27+
VipsOption.Int("across", 2),
28+
VipsOption.Int("shim", 10)
29+
)
30+
.writeToFile(outputPath.absolutePathString())
31+
32+
return SampleHelper.validate(
33+
outputPath,
34+
expectedSizeBoundsKb = 50L..200L
35+
)
36+
}
37+
}

0 commit comments

Comments
 (0)