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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ repositories {
}

dependencies {
implementation("app.photofox.vips-ffm:vips-ffm-core:1.9.7")
implementation("app.photofox.vips-ffm:vips-ffm-core:1.9.8")
}
```

Expand Down
31 changes: 24 additions & 7 deletions core/src/main/java/app/photofox/vipsffm/VBlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ public final class VBlob {
/// @param bytes The bytes to wrap
public static VBlob newFromBytes(Arena arena, byte[] bytes) throws VipsError {
var offHeapSegment = arena.allocateFrom(ValueLayout.JAVA_BYTE, bytes);
var blobAddress = VipsRaw.vips_blob_new(MemorySegment.NULL, offHeapSegment, offHeapSegment.byteSize());
return newFromDataSegment(arena, offHeapSegment);
}

static VBlob newFromDataSegment(Arena arena, MemorySegment dataSegment) throws VipsError {
var blobAddress = VipsRaw.vips_blob_new(MemorySegment.NULL, dataSegment, dataSegment.byteSize());
if (!VipsValidation.isValidPointer(blobAddress)) {
throw new VipsError("invalid blob returned from libvips");
}
Expand All @@ -59,20 +63,20 @@ public MemorySegment getUnsafeStructAddress() throws VipsError {
return this.address;
}

/// Not recommended for use, use [#asByteBuffer()] instead
/// Not recommended for use, use [#asArenaScopedByteBuffer()] or [#asClonedByteBuffer()] instead
///
/// Gets the raw [MemorySegment] (C pointer) for the data in this blob
///
/// Sliced to the length of the data, which isn't always null terminated
public MemorySegment getUnsafeDataAddress() throws VipsError {
var lengthOutPointer = arena.allocate(C_LONG);
var dataPointer = VipsRaw.vips_area_get_data(
var dataPointer = VipsRaw.vips_blob_get(
this.address,
lengthOutPointer,
MemorySegment.NULL,
MemorySegment.NULL,
MemorySegment.NULL
lengthOutPointer
);
if (!VipsValidation.isValidPointer(dataPointer)) {
throw new VipsError("unexpected vblob bad data pointer");
}
var length = lengthOutPointer.get(C_LONG, 0);
if (length < 0) {
throw new VipsError("unexpected length of vblob data " + length);
Expand All @@ -89,6 +93,10 @@ public long byteSize() {
///
/// Mapped to native memory via DirectByteBuffer, hence does not make a copy, so the data has the
/// same data lifetime as [#arena]
///
/// Note that because this is backed by native memory, you cannot use the [ByteBuffer#array()]
/// method on the resulting buffer. Use [#asClonedByteBuffer()] or [#getBytes()] if you're
/// looking for a full copy of the data.
public ByteBuffer asArenaScopedByteBuffer() {
return this.getUnsafeDataAddress().asByteBuffer();
}
Expand All @@ -107,4 +115,13 @@ public ByteBuffer asClonedByteBuffer() {
newBuffer.rewind();
return newBuffer;
}

/// byte[] representation of the data in this VipsBlob
///
/// Useful if you're working with metadata stored on an image, like an ICC profile
///
/// Note that a full copy of the data is taken
public byte[] getBytes() {
return this.getUnsafeDataAddress().toArray(ValueLayout.JAVA_BYTE);
}
}
6 changes: 3 additions & 3 deletions core/src/main/java/app/photofox/vipsffm/VImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -10465,15 +10465,15 @@ public VBlob getBlob(String name) {
if (blobLength <= 0) {
throw new VipsError("failed to read length of type blob from field: " + name);
}
var blobAddress = outPointer.get(VipsRaw.C_POINTER, 0).reinterpret(blobLength);
return new VBlob(arena, blobAddress);
var dataSegment = outPointer.get(VipsRaw.C_POINTER, 0).reinterpret(blobLength);
return VBlob.newFromDataSegment(arena, dataSegment);
}

/// Helper function to set the metadata stored at `name` on this image, of type `blob`
///
/// See also: [libvips header docs](https://www.libvips.org/API/current/libvips-header.html)
public VImage set(String name, VBlob value) {
VipsHelper.image_set_blob(arena, this.address, name, MemorySegment.NULL, value.address, value.byteSize());
VipsHelper.image_set_blob(arena, this.address, name, MemorySegment.NULL, value.getUnsafeDataAddress(), value.byteSize());
return this;
}

Expand Down
48 changes: 29 additions & 19 deletions docs/app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<li><a href="#byteSize()" tabindex="0">byteSize()</a></li>
<li><a href="#asArenaScopedByteBuffer()" tabindex="0">asArenaScopedByteBuffer()</a></li>
<li><a href="#asClonedByteBuffer()" tabindex="0">asClonedByteBuffer()</a></li>
<li><a href="#getBytes()" tabindex="0">getBytes()</a></li>
</ol>
</li>
</ol>
Expand Down Expand Up @@ -122,26 +123,25 @@ <h2>Method Summary</h2>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4">
<div class="block">Size of the data in this blob</div>
</div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link">MemorySegment</a></code></div>
<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#getUnsafeDataAddress()" class="member-name-link">getUnsafeDataAddress</a>()</code></div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code>byte[]</code></div>
<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#getBytes()" class="member-name-link">getBytes</a>()</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4">
<div class="block"><p>Not recommended for use, use</p>
<details class="invalid-tag">
<summary>invalid reference</summary>
<pre><code>#asByteBuffer()</code></pre>
</details>
instead
</div>
<div class="block">byte[] representation of the data in this VipsBlob</div>
</div>
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link">MemorySegment</a></code></div>
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#getUnsafeStructAddress()" class="member-name-link">getUnsafeStructAddress</a>()</code></div>
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#getUnsafeDataAddress()" class="member-name-link">getUnsafeDataAddress</a>()</code></div>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4">
<div class="block">Not recommended for use, use <a href="#asArenaScopedByteBuffer()"><code>asArenaScopedByteBuffer()</code></a> or <a href="#asClonedByteBuffer()"><code>asClonedByteBuffer()</code></a> instead</div>
</div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link">MemorySegment</a></code></div>
<div class="col-second odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4"><code><a href="#getUnsafeStructAddress()" class="member-name-link">getUnsafeStructAddress</a>()</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab2 method-summary-table-tab4">
<div class="block">Gets the raw <a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link"><code>MemorySegment</code></a> (C pointer) for this VipsBlob struct</div>
</div>
<div class="col-first odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code>static <a href="VBlob.html" title="class in app.photofox.vipsffm">VBlob</a></code></div>
<div class="col-second odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code><a href="#newFromBytes(java.lang.foreign.Arena,byte%5B%5D)" class="member-name-link">newFromBytes</a><wbr>(<a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/Arena.html" title="class or interface in java.lang.foreign" class="external-link">Arena</a>&nbsp;arena,
<div class="col-first even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code>static <a href="VBlob.html" title="class in app.photofox.vipsffm">VBlob</a></code></div>
<div class="col-second even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4"><code><a href="#newFromBytes(java.lang.foreign.Arena,byte%5B%5D)" class="member-name-link">newFromBytes</a><wbr>(<a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/Arena.html" title="class or interface in java.lang.foreign" class="external-link">Arena</a>&nbsp;arena,
byte[]&nbsp;bytes)</code></div>
<div class="col-last odd-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4">
<div class="col-last even-row-color method-summary-table method-summary-table-tab1 method-summary-table-tab4">
<div class="block">Creates a new VBlob from a fixed array of bytes</div>
</div>
</div>
Expand Down Expand Up @@ -206,12 +206,7 @@ <h3>getUnsafeDataAddress</h3>
<div class="horizontal-scroll">
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type"><a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link">MemorySegment</a></span>&nbsp;<span class="element-name">getUnsafeDataAddress</span>()
throws <span class="exceptions"><a href="VipsError.html" title="class in app.photofox.vipsffm">VipsError</a></span></div>
<div class="block"><p>Not recommended for use, use</p>
<details class="invalid-tag">
<summary>invalid reference</summary>
<pre><code>#asByteBuffer()</code></pre>
</details>
instead
<div class="block"><p>Not recommended for use, use <a href="#asArenaScopedByteBuffer()"><code>asArenaScopedByteBuffer()</code></a> or <a href="#asClonedByteBuffer()"><code>asClonedByteBuffer()</code></a> instead</p>
<p>Gets the raw <a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/foreign/MemorySegment.html" title="class or interface in java.lang.foreign" class="external-link"><code>MemorySegment</code></a> (C pointer) for the data in this blob</p>
<p>Sliced to the length of the data, which isn't always null terminated</p>
</div>
Expand Down Expand Up @@ -239,6 +234,9 @@ <h3>asArenaScopedByteBuffer</h3>
<div class="block"><p>ByteBuffer representation of the data in this blob</p>
<p>Mapped to native memory via DirectByteBuffer, hence does not make a copy, so the data has the
same data lifetime as <code>arena</code></p>
<p>Note that because this is backed by native memory, you cannot use the <a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/nio/ByteBuffer.html#array()" title="class or interface in java.nio" class="external-link"><code>ByteBuffer.array()</code></a>
method on the resulting buffer. Use <a href="#asClonedByteBuffer()"><code>asClonedByteBuffer()</code></a> or <a href="#getBytes()"><code>getBytes()</code></a> if you're
looking for a full copy of the data.</p>
</div>
</div>
</section>
Expand All @@ -254,6 +252,18 @@ <h3>asClonedByteBuffer</h3>
</div>
</section>
</li>
<li>
<section class="detail" id="getBytes()">
<h3>getBytes</h3>
<div class="horizontal-scroll">
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type">byte[]</span>&nbsp;<span class="element-name">getBytes</span>()</div>
<div class="block"><p>byte[] representation of the data in this VipsBlob</p>
<p>Useful if you're working with metadata stored on an image, like an ICC profile</p>
<p>Note that a full copy of the data is taken</p>
</div>
</div>
</section>
</li>
</ul>
</section>
</li>
Expand Down
12 changes: 5 additions & 7 deletions docs/index-all.html
Original file line number Diff line number Diff line change
Expand Up @@ -5397,6 +5397,10 @@ <h2 class="title" id="I:G">G</h2>
<div class="block">Helper function to get the metadata stored at <code>name</code> on this image, of type <code>blob</code>, or <code>null</code>
if not present</div>
</dd>
<dt><a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html#getBytes()" class="member-name-link">getBytes()</a> - Method in class app.photofox.vipsffm.<a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html" title="class in app.photofox.vipsffm">VBlob</a></dt>
<dd>
<div class="block">byte[] representation of the data in this VipsBlob</div>
</dd>
<dt><a href="app.photofox.vipsffm/app/photofox/vipsffm/VImage.html#getDouble(java.lang.String)" class="member-name-link">getDouble(String)</a> - Method in class app.photofox.vipsffm.<a href="app.photofox.vipsffm/app/photofox/vipsffm/VImage.html" title="class in app.photofox.vipsffm">VImage</a></dt>
<dd>
<div class="block">Helper function to get the metadata stored at <code>name</code> on this image, of type <code>double</code>, or <code>null</code>
Expand Down Expand Up @@ -5701,13 +5705,7 @@ <h2 class="title" id="I:G">G</h2>
</dd>
<dt><a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html#getUnsafeDataAddress()" class="member-name-link">getUnsafeDataAddress()</a> - Method in class app.photofox.vipsffm.<a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html" title="class in app.photofox.vipsffm">VBlob</a></dt>
<dd>
<div class="block"><p>Not recommended for use, use</p>
<details class="invalid-tag">
<summary>invalid reference</summary>
<pre><code>#asByteBuffer()</code></pre>
</details>
instead
</div>
<div class="block">Not recommended for use, use <a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html#asArenaScopedByteBuffer()"><code>VBlob.asArenaScopedByteBuffer()</code></a> or <a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html#asClonedByteBuffer()"><code>VBlob.asClonedByteBuffer()</code></a> instead</div>
</dd>
<dt><a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html#getUnsafeStructAddress()" class="member-name-link">getUnsafeStructAddress()</a> - Method in class app.photofox.vipsffm.<a href="app.photofox.vipsffm/app/photofox/vipsffm/VBlob.html" title="class in app.photofox.vipsffm">VBlob</a></dt>
<dd>
Expand Down
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ <h2 id="usage-heading">Usage</h2>
}

dependencies {
implementation(&quot;app.photofox.vips-ffm:vips-ffm-core:1.9.7&quot;)
implementation(&quot;app.photofox.vips-ffm:vips-ffm-core:1.9.8&quot;)
}
</code></pre>
<p>Figure out what you're trying to do by looking at the <a href="https://www.libvips.org/API/current/">libvips documentation</a>
Expand Down
2 changes: 1 addition & 1 deletion docs/member-search-index.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions generator/src/main/kotlin/vipsffm/GenerateVClasses.kt
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ object GenerateVClasses {
when (poetValueType) {
vblobType -> {
this.addStatement(
"\$T.image_set_$typeName(arena, this.address, name, \$T.NULL, value.address, value.byteSize())",
"\$T.image_set_$typeName(arena, this.address, name, \$T.NULL, value.getUnsafeDataAddress(), value.byteSize())",
vipsHelperType,
memorySegmentType
)
Expand Down Expand Up @@ -1087,8 +1087,8 @@ object GenerateVClasses {
.endControlFlow()
.build()
)
this.addStatement("var blobAddress = outPointer.get(\$T.C_POINTER, 0).reinterpret(blobLength)", vipsRawType)
this.addStatement("return new VBlob(arena, blobAddress)")
this.addStatement("var dataSegment = outPointer.get(\$T.C_POINTER, 0).reinterpret(blobLength)", vipsRawType)
this.addStatement("return VBlob.newFromDataSegment(arena, dataSegment)")
}

vimageType -> {
Expand Down
15 changes: 13 additions & 2 deletions sample/src/main/kotlin/vipsffm/sample/VImageGetSetSample.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import app.photofox.vipsffm.VBlob
import app.photofox.vipsffm.VImage
import org.slf4j.LoggerFactory
import vipsffm.RunnableSample
import java.awt.color.ColorSpace
import java.awt.color.ICC_Profile
import java.lang.foreign.Arena
import java.lang.foreign.ValueLayout
import java.nio.file.Path

/**
Expand Down Expand Up @@ -46,13 +49,21 @@ object VImageGetSetSample: RunnableSample {
)
}

val bytes = sourceImage.getBlob("test-name-blob").asClonedByteBuffer()
if (!bytes.array().contentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04))) {
val bytes = sourceImage.getBlob("test-name-blob").bytes
if (!bytes.contentEquals(byteArrayOf(0x01, 0x02, 0x03, 0x04))) {
return Result.failure(
RuntimeException("unexpected value in metadata")
)
}

val iccBytes = sourceImage.getBlob("icc-profile-data").asClonedByteBuffer()
val profile = ICC_Profile.getInstance(iccBytes.array())
if (profile.majorVersion != 2 || profile.colorSpaceType != ColorSpace.TYPE_RGB) {
return Result.failure(
RuntimeException("unexpected icc profile values")
)
}

val imageFromMetadata = sourceImage.getImage("test-name-image")
if (imageFromMetadata.width != 100 && imageFromMetadata.height != 100) {
return Result.failure(
Expand Down
Loading