-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add image loading from an InputStream, and image writing to an Output…
…Stream
- Loading branch information
Showing
64 changed files
with
4,861 additions
and
334 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
core/src/main/java/app/photofox/vipsffm/VCustomSource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package app.photofox.vipsffm; | ||
|
||
import app.photofox.vipsffm.jextract.CustomStreamReadCallback; | ||
import app.photofox.vipsffm.jextract.CustomStreamSeekCallback; | ||
|
||
import java.lang.foreign.Arena; | ||
import java.lang.foreign.MemorySegment; | ||
|
||
/** | ||
* Models a libvips "custom streaming" source | ||
* Provides callbacks for read and seek operations | ||
* See <a href="https://www.libvips.org/2019/11/29/True-streaming-for-libvips.html">true streaming for libvips</a> | ||
*/ | ||
public final class VCustomSource extends VSource { | ||
|
||
@FunctionalInterface | ||
public interface ReadCallback { | ||
|
||
long read(MemorySegment dataPointer, long length); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface SeekCallback { | ||
|
||
long seek(int whence); | ||
} | ||
|
||
private final VCustomSource.ReadCallback readCallback; | ||
private final VCustomSource.SeekCallback seekCallback; | ||
|
||
public VCustomSource( | ||
Arena arena, | ||
VCustomSource.ReadCallback readCallback, | ||
VCustomSource.SeekCallback seekCallback | ||
) throws VipsError { | ||
super(arena, VipsHelper.source_custom_new(arena)); | ||
this.readCallback = readCallback; | ||
this.seekCallback = seekCallback; | ||
|
||
attachReadSignal(arena, this); | ||
if (seekCallback != null) { | ||
attachSeekSignal(arena, this, seekCallback); | ||
} | ||
} | ||
|
||
public VCustomSource( | ||
Arena arena, | ||
VCustomSource.ReadCallback readCallback | ||
) throws VipsError { | ||
this(arena, readCallback, null); | ||
} | ||
|
||
private void attachReadSignal(Arena arena, VSource source) { | ||
var callback = new CustomStreamReadCallback.Function() { | ||
|
||
@Override | ||
public long apply( | ||
MemorySegment source, | ||
MemorySegment data, | ||
long length, | ||
MemorySegment handle | ||
) { | ||
return readCallback.read(data, length); | ||
} | ||
}; | ||
var callbackPointer = CustomStreamReadCallback.allocate(callback, arena); | ||
var result = VipsHelper.g_signal_connect_data( | ||
arena, | ||
source.address, | ||
"read", | ||
callbackPointer, | ||
MemorySegment.NULL, | ||
MemorySegment.NULL, | ||
0 | ||
); | ||
if (result <= 0) { | ||
throw new VipsError("failed to create read signal"); | ||
} | ||
} | ||
|
||
private void attachSeekSignal(Arena arena, VSource source, SeekCallback seekCallback) { | ||
var callback = new CustomStreamSeekCallback.Function() { | ||
|
||
@Override | ||
public long apply( | ||
MemorySegment source, | ||
MemorySegment data, | ||
int whence, | ||
MemorySegment handle | ||
) { | ||
return seekCallback.seek(whence); | ||
} | ||
}; | ||
var callbackPointer = CustomStreamSeekCallback.allocate(callback, arena); | ||
var result = VipsHelper.g_signal_connect_data( | ||
arena, | ||
source.address, | ||
"seek", | ||
callbackPointer, | ||
MemorySegment.NULL, | ||
MemorySegment.NULL, | ||
0 | ||
); | ||
if (result <= 0) { | ||
throw new VipsError("failed to create seek signal"); | ||
} | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
core/src/main/java/app/photofox/vipsffm/VCustomTarget.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package app.photofox.vipsffm; | ||
|
||
import app.photofox.vipsffm.jextract.CustomStreamEndCallback; | ||
import app.photofox.vipsffm.jextract.CustomStreamWriteCallback; | ||
|
||
import java.lang.foreign.Arena; | ||
import java.lang.foreign.MemorySegment; | ||
|
||
/** | ||
* Models a libvips "custom streaming" target | ||
* Provides callbacks for write and end operations | ||
* See <a href="https://www.libvips.org/2019/11/29/True-streaming-for-libvips.html">true streaming for libvips</a> | ||
*/ | ||
public final class VCustomTarget extends VTarget { | ||
|
||
@FunctionalInterface | ||
public interface WriteCallback { | ||
|
||
long write(MemorySegment dataPointer); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface EndCallback { | ||
|
||
int end(); | ||
} | ||
|
||
private final WriteCallback writeCallback; | ||
private final EndCallback endCallback; | ||
|
||
public VCustomTarget( | ||
Arena arena, | ||
WriteCallback writeCallback, | ||
EndCallback endCallback | ||
) throws VipsError { | ||
super(arena, VipsHelper.target_custom_new(arena)); | ||
this.writeCallback = writeCallback; | ||
this.endCallback = endCallback; | ||
|
||
attachWriteSignal(arena, this); | ||
attachEndSignal(arena, this); | ||
} | ||
|
||
private void attachWriteSignal(Arena arena, VTarget target) { | ||
var callback = new CustomStreamWriteCallback.Function() { | ||
|
||
@Override | ||
public long apply( | ||
MemorySegment source, | ||
MemorySegment data, | ||
long length, | ||
MemorySegment handle | ||
) { | ||
var segment = data.asSlice(0, length); | ||
return writeCallback.write(segment); | ||
} | ||
}; | ||
var callbackPointer = CustomStreamWriteCallback.allocate(callback, arena); | ||
var result = VipsHelper.g_signal_connect_data( | ||
arena, | ||
target.address, | ||
"write", | ||
callbackPointer, | ||
MemorySegment.NULL, | ||
MemorySegment.NULL, | ||
0 | ||
); | ||
if (result <= 0) { | ||
throw new VipsError("failed to create write signal"); | ||
} | ||
} | ||
|
||
private void attachEndSignal(Arena arena, VTarget target) { | ||
var callback = new CustomStreamEndCallback.Function() { | ||
|
||
@Override | ||
public int apply( | ||
MemorySegment source, | ||
MemorySegment handle | ||
) { | ||
return endCallback.end(); | ||
} | ||
}; | ||
var callbackPointer = CustomStreamEndCallback.allocate(callback, arena); | ||
var result = VipsHelper.g_signal_connect_data( | ||
arena, | ||
target.address, | ||
"end", | ||
callbackPointer, | ||
MemorySegment.NULL, | ||
MemorySegment.NULL, | ||
0 | ||
); | ||
if (result <= 0) { | ||
throw new VipsError("failed to create end signal"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.