add alpha/requireAlpha argument when decoding images
remove size checks from setImage (we are assuming the caller knows the correct size) This also adds some monkey-patch methods to js_interop to convert Float32List to a UInt8List (but use this with care, because it will only work for emscripten-allocated objects)
This commit is contained in:
@@ -215,3 +215,10 @@ extension DartBigIntExtension on int {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Float32ListExtension on Float32List {
|
||||||
|
|
||||||
|
Uint8List asUint8List() {
|
||||||
|
return this.buffer.asUint8List(this.offsetInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,22 @@ const FILAMENT_SINGLE_THREADED = true;
|
|||||||
const FILAMENT_WASM = true;
|
const FILAMENT_WASM = true;
|
||||||
const IS_WINDOWS = false;
|
const IS_WINDOWS = false;
|
||||||
|
|
||||||
|
final _allocations = <TypedData>{};
|
||||||
|
|
||||||
Int32List makeInt32List(int length) {
|
Int32List makeInt32List(int length) {
|
||||||
var ptr = malloc<Int32>(length * 4);
|
var ptr = stackAlloc<Int32>(length * 4);
|
||||||
var buf = _NativeLibrary.instance._emscripten_make_int32_buffer(ptr, length);
|
var buf = _NativeLibrary.instance._emscripten_make_int32_buffer(ptr, length);
|
||||||
return buf.toDart;
|
var int32List = buf.toDart;
|
||||||
|
_allocations.add(int32List);
|
||||||
|
return int32List;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float32List makeFloat32List(int length) {
|
||||||
|
var ptr = stackAlloc<Float32>(length * 4);
|
||||||
|
var buf = _NativeLibrary.instance._emscripten_make_f32_buffer(ptr, length);
|
||||||
|
var f32List = buf.toDart;
|
||||||
|
_allocations.add(f32List);
|
||||||
|
return f32List;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension type _NativeLibrary(JSObject _) implements JSObject {
|
extension type _NativeLibrary(JSObject _) implements JSObject {
|
||||||
@@ -34,6 +46,8 @@ extension type _NativeLibrary(JSObject _) implements JSObject {
|
|||||||
Pointer<Float64> ptr, int length);
|
Pointer<Float64> ptr, int length);
|
||||||
external Pointer _emscripten_get_byte_offset(JSObject obj);
|
external Pointer _emscripten_get_byte_offset(JSObject obj);
|
||||||
|
|
||||||
|
external int _emscripten_stack_get_base();
|
||||||
|
external Pointer _emscripten_stack_get_current();
|
||||||
external int _emscripten_stack_get_free();
|
external int _emscripten_stack_get_free();
|
||||||
|
|
||||||
external void _execute_queue();
|
external void _execute_queue();
|
||||||
@@ -47,8 +61,8 @@ extension type _NativeLibrary(JSObject _) implements JSObject {
|
|||||||
|
|
||||||
extension FreeTypedData<T> on TypedData {
|
extension FreeTypedData<T> on TypedData {
|
||||||
void free() {
|
void free() {
|
||||||
final ptr = Pointer<Void>(this.offsetInBytes);
|
Pointer<Void>(this.offsetInBytes).free();
|
||||||
ptr.free();
|
_allocations.remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +78,12 @@ Pointer<T> getPointer<T extends NativeType>(TypedData data, JSObject obj) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension JSBackingBuffer on JSUint8Array {
|
extension JSUint8BackingBuffer on JSUint8Array {
|
||||||
|
@JS('buffer')
|
||||||
|
external JSObject buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension JSFloat32BackingBuffer on JSFloat32Array {
|
||||||
@JS('buffer')
|
@JS('buffer')
|
||||||
external JSObject buffer;
|
external JSObject buffer;
|
||||||
}
|
}
|
||||||
@@ -117,7 +136,11 @@ extension Uint8ListExtension on Uint8List {
|
|||||||
final bar =
|
final bar =
|
||||||
Uint8ArrayWrapper(NativeLibrary.instance.HEAPU8.buffer, ptr, length)
|
Uint8ArrayWrapper(NativeLibrary.instance.HEAPU8.buffer, ptr, length)
|
||||||
as JSUint8Array;
|
as JSUint8Array;
|
||||||
|
var now = DateTime.now();
|
||||||
bar.toDart.setRange(0, length, this);
|
bar.toDart.setRange(0, length, this);
|
||||||
|
var finished = DateTime.now();
|
||||||
|
print(
|
||||||
|
"uint8list copy finished in ${finished.millisecondsSinceEpoch - now.millisecondsSinceEpoch}ms");
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,6 +154,11 @@ extension Float32ListExtension on Float32List {
|
|||||||
bar.toDart.setRange(0, length, this);
|
bar.toDart.setRange(0, length, this);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint8List asUint8List() {
|
||||||
|
var ptr = Pointer<Uint8>(_NativeLibrary.instance._emscripten_get_byte_offset(this.toJS));
|
||||||
|
return ptr.asTypedList(length * 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int16ListExtension on Int16List {
|
extension Int16ListExtension on Int16List {
|
||||||
@@ -176,6 +204,10 @@ extension Int32ListExtension on Int32List {
|
|||||||
if (this.lengthInBytes == 0) {
|
if (this.lengthInBytes == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (_allocations.contains(this)) {
|
||||||
|
return Pointer<Int32>(
|
||||||
|
_NativeLibrary.instance._emscripten_get_byte_offset(this.toJS));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this.buffer.asUint8List(this.offsetInBytes);
|
this.buffer.asUint8List(this.offsetInBytes);
|
||||||
final ptr = getPointer<Int32>(this, this.toJS);
|
final ptr = getPointer<Int32>(this, this.toJS);
|
||||||
@@ -214,12 +246,21 @@ extension Float64ListExtension on Float64List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AsUint8List on Pointer<Uint8> {
|
||||||
|
Uint8List asTypedList(int length) {
|
||||||
|
final start = addr;
|
||||||
|
final wrapper =
|
||||||
|
Uint8ArrayWrapper(NativeLibrary.instance.HEAPU8.buffer, start, length)
|
||||||
|
as JSUint8Array;
|
||||||
|
return wrapper.toDart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension AsFloat32List on Pointer<Float> {
|
extension AsFloat32List on Pointer<Float> {
|
||||||
Float32List asTypedList(int length) {
|
Float32List asTypedList(int length) {
|
||||||
final start = addr;
|
final start = addr;
|
||||||
final wrapper =
|
final wrapper = Float32ArrayWrapper(
|
||||||
Float32ArrayWrapper(NativeLibrary.instance.HEAPU8.buffer, start, length)
|
NativeLibrary.instance.HEAPF32.buffer, start, length) as JSFloat32Array;
|
||||||
as JSFloat32Array;
|
|
||||||
return wrapper.toDart;
|
return wrapper.toDart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -738,11 +738,12 @@ external ffi.Pointer<TLinearImage> Image_createEmpty(
|
|||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Pointer<TLinearImage> Function(
|
ffi.Pointer<TLinearImage> Function(
|
||||||
ffi.Pointer<ffi.Uint8>, ffi.Size, ffi.Pointer<ffi.Char>)>(isLeaf: true)
|
ffi.Pointer<ffi.Uint8>, ffi.Size, ffi.Pointer<ffi.Char>, ffi.Bool alpha)>(isLeaf: true)
|
||||||
external ffi.Pointer<TLinearImage> Image_decode(
|
external ffi.Pointer<TLinearImage> Image_decode(
|
||||||
ffi.Pointer<ffi.Uint8> data,
|
ffi.Pointer<ffi.Uint8> data,
|
||||||
int length,
|
int length,
|
||||||
ffi.Pointer<ffi.Char> name,
|
ffi.Pointer<ffi.Char> name,
|
||||||
|
bool alpha
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Pointer<ffi.Float> Function(ffi.Pointer<TLinearImage>)>(
|
@ffi.Native<ffi.Pointer<ffi.Float> Function(ffi.Pointer<TLinearImage>)>(
|
||||||
@@ -2367,6 +2368,7 @@ external void Image_createEmptyRenderThread(
|
|||||||
ffi.Pointer<ffi.Uint8>,
|
ffi.Pointer<ffi.Uint8>,
|
||||||
ffi.Size,
|
ffi.Size,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Char>,
|
||||||
|
ffi.Bool,
|
||||||
ffi.Pointer<
|
ffi.Pointer<
|
||||||
ffi.NativeFunction<
|
ffi.NativeFunction<
|
||||||
ffi.Void Function(ffi.Pointer<TLinearImage>)>>)>(isLeaf: true)
|
ffi.Void Function(ffi.Pointer<TLinearImage>)>>)>(isLeaf: true)
|
||||||
@@ -2374,6 +2376,7 @@ external void Image_decodeRenderThread(
|
|||||||
ffi.Pointer<ffi.Uint8> data,
|
ffi.Pointer<ffi.Uint8> data,
|
||||||
int length,
|
int length,
|
||||||
ffi.Pointer<ffi.Char> name,
|
ffi.Pointer<ffi.Char> name,
|
||||||
|
bool alpha,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<TLinearImage>)>>
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<TLinearImage>)>>
|
||||||
onComplete,
|
onComplete,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -393,6 +393,7 @@ extension type NativeLibrary(JSObject _) implements JSObject {
|
|||||||
JSFunction f, String signature);
|
JSFunction f, String signature);
|
||||||
external void removeFunction<T>(Pointer<NativeFunction<T>> f);
|
external void removeFunction<T>(Pointer<NativeFunction<T>> f);
|
||||||
external JSUint8Array get HEAPU8;
|
external JSUint8Array get HEAPU8;
|
||||||
|
external JSFloat32Array get HEAPF32;
|
||||||
|
|
||||||
external void _Thermion_resizeCanvas(
|
external void _Thermion_resizeCanvas(
|
||||||
int width,
|
int width,
|
||||||
@@ -812,6 +813,7 @@ extension type NativeLibrary(JSObject _) implements JSObject {
|
|||||||
Pointer<Uint8> data,
|
Pointer<Uint8> data,
|
||||||
size_t length,
|
size_t length,
|
||||||
Pointer<Char> name,
|
Pointer<Char> name,
|
||||||
|
bool alpha
|
||||||
);
|
);
|
||||||
external Pointer<Float32> _Image_getBytes(
|
external Pointer<Float32> _Image_getBytes(
|
||||||
Pointer<TLinearImage> tLinearImage,
|
Pointer<TLinearImage> tLinearImage,
|
||||||
@@ -1670,6 +1672,7 @@ extension type NativeLibrary(JSObject _) implements JSObject {
|
|||||||
Pointer<Uint8> data,
|
Pointer<Uint8> data,
|
||||||
size_t length,
|
size_t length,
|
||||||
Pointer<Char> name,
|
Pointer<Char> name,
|
||||||
|
bool alpha,
|
||||||
Pointer<self.NativeFunction<void Function(PointerClass<TLinearImage>)>>
|
Pointer<self.NativeFunction<void Function(PointerClass<TLinearImage>)>>
|
||||||
onComplete,
|
onComplete,
|
||||||
);
|
);
|
||||||
@@ -3071,8 +3074,9 @@ self.Pointer<TLinearImage> Image_decode(
|
|||||||
self.Pointer<Uint8> data,
|
self.Pointer<Uint8> data,
|
||||||
Dart__darwin_size_t length,
|
Dart__darwin_size_t length,
|
||||||
self.Pointer<Char> name,
|
self.Pointer<Char> name,
|
||||||
|
bool alpha
|
||||||
) {
|
) {
|
||||||
final result = _lib._Image_decode(data, length, name);
|
final result = _lib._Image_decode(data, length, name, alpha);
|
||||||
return self.Pointer<TLinearImage>(result);
|
return self.Pointer<TLinearImage>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4839,11 +4843,12 @@ void Image_decodeRenderThread(
|
|||||||
self.Pointer<Uint8> data,
|
self.Pointer<Uint8> data,
|
||||||
Dart__darwin_size_t length,
|
Dart__darwin_size_t length,
|
||||||
self.Pointer<Char> name,
|
self.Pointer<Char> name,
|
||||||
|
bool alpha,
|
||||||
self.Pointer<self.NativeFunction<void Function(Pointer<TLinearImage>)>>
|
self.Pointer<self.NativeFunction<void Function(Pointer<TLinearImage>)>>
|
||||||
onComplete,
|
onComplete,
|
||||||
) {
|
) {
|
||||||
final result =
|
final result =
|
||||||
_lib._Image_decodeRenderThread(data, length, name, onComplete.cast());
|
_lib._Image_decodeRenderThread(data, length, name, alpha, onComplete.cast());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
|
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
|
||||||
TextureFormat textureFormat = TextureFormat.RGBA16F,
|
TextureFormat textureFormat = TextureFormat.RGBA16F,
|
||||||
int? importedTextureHandle}) async {
|
int? importedTextureHandle}) async {
|
||||||
|
|
||||||
var bitmask = flags.fold(0, (a, b) => a | b.value);
|
var bitmask = flags.fold(0, (a, b) => a | b.value);
|
||||||
|
|
||||||
final texturePtr = await withPointerCallback<TTexture>((cb) {
|
final texturePtr = await withPointerCallback<TTexture>((cb) {
|
||||||
@@ -362,19 +363,28 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
return FFITextureSampler(samplerPtr);
|
return FFITextureSampler(samplerPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the image data into a native LinearImage (floating point).
|
||||||
|
/// If [requireAlpha] is true, the decoded image will always contain an
|
||||||
|
/// alpha channel (even if the original image did not contain one).
|
||||||
///
|
///
|
||||||
///
|
Future<LinearImage> decodeImage(Uint8List data, { String name = "image", bool requireAlpha = false}) async {
|
||||||
///
|
|
||||||
Future<LinearImage> decodeImage(Uint8List data) async {
|
|
||||||
final name = "image";
|
|
||||||
late Pointer stackPtr;
|
late Pointer stackPtr;
|
||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
//stackPtr = stackSave();
|
//stackPtr = stackSave();
|
||||||
}
|
}
|
||||||
|
var now = DateTime.now();
|
||||||
|
|
||||||
var ptr = Image_decode(
|
var ptr = Image_decode(
|
||||||
data.address,
|
data.address,
|
||||||
data.length,
|
data.length,
|
||||||
name.toNativeUtf8().cast<Char>(),
|
name.toNativeUtf8().cast<Char>(),
|
||||||
|
requireAlpha
|
||||||
|
);
|
||||||
|
|
||||||
|
var finished = DateTime.now();
|
||||||
|
print(
|
||||||
|
"Image_decode (render thread) finished in ${finished.millisecondsSinceEpoch - now.millisecondsSinceEpoch}ms",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
@@ -1114,11 +1124,11 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
stackPtr = stackSave();
|
stackPtr = stackSave();
|
||||||
}
|
}
|
||||||
TransformManager_setTransform(transformManager, entity, matrix4ToDouble4x4(transform));
|
TransformManager_setTransform(
|
||||||
|
transformManager, entity, matrix4ToDouble4x4(transform));
|
||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
stackRestore(stackPtr);
|
stackRestore(stackPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -1129,7 +1139,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
stackPtr = stackSave();
|
stackPtr = stackSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
var transform = double4x4ToMatrix4(
|
var transform = double4x4ToMatrix4(
|
||||||
TransformManager_getWorldTransform(transformManager, entity));
|
TransformManager_getWorldTransform(transformManager, entity));
|
||||||
if (FILAMENT_WASM) {
|
if (FILAMENT_WASM) {
|
||||||
@@ -1137,5 +1147,4 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
}
|
}
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,15 +165,10 @@ class FFILinearImage extends LinearImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<FFILinearImage> decode(Uint8List data,
|
static Future<FFILinearImage> decode(Uint8List data,
|
||||||
[String name = "image"]) async {
|
{String name = "image", bool requireAlpha = false}) async {
|
||||||
final namePtr = name.toNativeUtf8();
|
final image = await FilamentApp.instance!
|
||||||
|
.decodeImage(data, name: name, requireAlpha: requireAlpha);
|
||||||
final imagePtr = await withPointerCallback<TLinearImage>((cb) {
|
return image as FFILinearImage;
|
||||||
Image_decodeRenderThread(
|
|
||||||
data.address, data.lengthInBytes, namePtr.cast(), cb);
|
|
||||||
});
|
|
||||||
|
|
||||||
return FFILinearImage(imagePtr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> destroy() async {
|
Future<void> destroy() async {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ abstract class FilamentApp<T> {
|
|||||||
///
|
///
|
||||||
/// Decodes the specified image data.
|
/// Decodes the specified image data.
|
||||||
///
|
///
|
||||||
Future<LinearImage> decodeImage(Uint8List data);
|
Future<LinearImage> decodeImage(Uint8List data, { String name = "image", bool requireAlpha = false});
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Creates an (empty) imge with the given dimensions.
|
/// Creates an (empty) imge with the given dimensions.
|
||||||
|
|||||||
@@ -484,11 +484,17 @@ abstract class LinearImage {
|
|||||||
Future<int> getHeight();
|
Future<int> getHeight();
|
||||||
Future<int> getChannels();
|
Future<int> getChannels();
|
||||||
|
|
||||||
|
/// Decodes the image contained in [data] and returns a texture of
|
||||||
|
/// the corresponding size with the image set as mip-level 0.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
static Future<Texture> decodeToTexture(Uint8List data, {
|
||||||
static Future<Texture> decodeToTexture(Uint8List data, { TextureFormat textureFormat = TextureFormat.RGB32F, PixelDataFormat pixelDataFormat = PixelDataFormat.RGB, PixelDataType pixelDataType = PixelDataType.FLOAT, int levels = 1}) async {
|
TextureFormat textureFormat = TextureFormat.RGB32F,
|
||||||
final decodedImage = await FilamentApp.instance!.decodeImage(data);
|
PixelDataFormat pixelDataFormat = PixelDataFormat.RGB,
|
||||||
|
PixelDataType pixelDataType = PixelDataType.FLOAT,
|
||||||
|
int levels = 1,
|
||||||
|
bool requireAlpha = false}) async {
|
||||||
|
final decodedImage = await FilamentApp.instance!.decodeImage(data, requireAlpha: requireAlpha);
|
||||||
|
|
||||||
final texture = await FilamentApp.instance!.createTexture(
|
final texture = await FilamentApp.instance!.createTexture(
|
||||||
await decodedImage.getWidth(),
|
await decodedImage.getWidth(),
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ EMSCRIPTEN_KEEPALIVE TTextureUsage Texture_getUsage(TTexture *tTexture, uint32_t
|
|||||||
EMSCRIPTEN_KEEPALIVE void Texture_generateMipMaps(TTexture *tTexture, TEngine *tEngine);
|
EMSCRIPTEN_KEEPALIVE void Texture_generateMipMaps(TTexture *tTexture, TEngine *tEngine);
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_createEmpty(uint32_t width,uint32_t height,uint32_t channel);
|
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_createEmpty(uint32_t width,uint32_t height,uint32_t channel);
|
||||||
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_decode(uint8_t* data, size_t length, const char* name);
|
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_decode(uint8_t* data, size_t length, const char* name, bool alpha);
|
||||||
EMSCRIPTEN_KEEPALIVE float *Image_getBytes(TLinearImage *tLinearImage);
|
EMSCRIPTEN_KEEPALIVE float *Image_getBytes(TLinearImage *tLinearImage);
|
||||||
EMSCRIPTEN_KEEPALIVE void Image_destroy(TLinearImage *tLinearImage);
|
EMSCRIPTEN_KEEPALIVE void Image_destroy(TLinearImage *tLinearImage);
|
||||||
EMSCRIPTEN_KEEPALIVE uint32_t Image_getWidth(TLinearImage *tLinearImage);
|
EMSCRIPTEN_KEEPALIVE uint32_t Image_getWidth(TLinearImage *tLinearImage);
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ namespace thermion
|
|||||||
|
|
||||||
// Image methods
|
// Image methods
|
||||||
void Image_createEmptyRenderThread(uint32_t width, uint32_t height, uint32_t channel, void (*onComplete)(TLinearImage *));
|
void Image_createEmptyRenderThread(uint32_t width, uint32_t height, uint32_t channel, void (*onComplete)(TLinearImage *));
|
||||||
void Image_decodeRenderThread(uint8_t* data, size_t length, const char* name, void (*onComplete)(TLinearImage *));
|
void Image_decodeRenderThread(uint8_t* data, size_t length, const char* name, bool alpha, void (*onComplete)(TLinearImage *));
|
||||||
void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *));
|
void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *));
|
||||||
void Image_destroyRenderThread(TLinearImage *tLinearImage, uint32_t requestId, VoidCallback onComplete);
|
void Image_destroyRenderThread(TLinearImage *tLinearImage, uint32_t requestId, VoidCallback onComplete);
|
||||||
void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
|
void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
|
||||||
|
|||||||
@@ -40,14 +40,16 @@ namespace thermion
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_decode(uint8_t *data, size_t length, const char *name = "image")
|
EMSCRIPTEN_KEEPALIVE TLinearImage *Image_decode(uint8_t *data, size_t length, const char *name = "image", bool alpha = true)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto start = std::chrono::high_resolution_clock::now();
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
int width, height, channels;
|
int width, height, channels;
|
||||||
|
|
||||||
|
TRACE("Loading image from buffer of length %lu bytes (alpha : %s)", length, alpha ? "true" : "false");
|
||||||
|
|
||||||
uint8_t *imgData = stbi_load_from_memory(data, length, &width, &height, &channels, 0);
|
uint8_t *imgData = stbi_load_from_memory(data, length, &width, &height, &channels, alpha ? 4 : 3);
|
||||||
|
|
||||||
if (!imgData) {
|
if (!imgData) {
|
||||||
ERROR("Failed to decode image");
|
ERROR("Failed to decode image");
|
||||||
@@ -56,7 +58,7 @@ namespace thermion
|
|||||||
|
|
||||||
LinearImage *linearImage;
|
LinearImage *linearImage;
|
||||||
|
|
||||||
if(channels == 4) {
|
if(alpha) {
|
||||||
linearImage = new LinearImage(toLinearWithAlpha<uint8_t>(
|
linearImage = new LinearImage(toLinearWithAlpha<uint8_t>(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -75,7 +77,7 @@ namespace thermion
|
|||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||||
|
|
||||||
TRACE("Image decoded successfully in %lld ms", duration.count());
|
TRACE("Image decoded successfully in %lld ms (%dx%dx%d)", duration.count(), width, height, channels);
|
||||||
|
|
||||||
if (!linearImage->isValid())
|
if (!linearImage->isValid())
|
||||||
{
|
{
|
||||||
@@ -383,28 +385,14 @@ namespace thermion
|
|||||||
|
|
||||||
switch (bufferFormat)
|
switch (bufferFormat)
|
||||||
{
|
{
|
||||||
case PixelBufferDescriptor::PixelDataFormat::RGB:
|
case PixelBufferDescriptor::PixelDataFormat::RGB:
|
||||||
case PixelBufferDescriptor::PixelDataFormat::RGBA:
|
case PixelBufferDescriptor::PixelDataFormat::RGBA:
|
||||||
{
|
case PixelBufferDescriptor::PixelDataFormat::RGB_INTEGER:
|
||||||
size_t expectedSize = width * height * channels * sizeof(float);
|
case PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER:
|
||||||
if (size != expectedSize)
|
break;
|
||||||
{
|
default:
|
||||||
Log("Size mismatch (expected %lu, got %lu)", expectedSize, size);
|
Log("Unsupported buffer format type : %d", bufferFormat);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PixelBufferDescriptor::PixelDataFormat::RGB_INTEGER:
|
|
||||||
case PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER:
|
|
||||||
if (size != width * height * channels * sizeof(uint8_t))
|
|
||||||
{
|
|
||||||
Log("Size mismatch");
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log("Unsupported buffer format type : %d", bufferFormat);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the texture upload is async, so we need to copy the buffer
|
// the texture upload is async, so we need to copy the buffer
|
||||||
|
|||||||
@@ -755,12 +755,12 @@ extern "C"
|
|||||||
auto fut = _renderThread->add_task(lambda);
|
auto fut = _renderThread->add_task(lambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void Image_decodeRenderThread(uint8_t *data, size_t length, const char *name, void (*onComplete)(TLinearImage *))
|
EMSCRIPTEN_KEEPALIVE void Image_decodeRenderThread(uint8_t *data, size_t length, const char *name, bool alpha, void (*onComplete)(TLinearImage *))
|
||||||
{
|
{
|
||||||
std::packaged_task<void()> lambda(
|
std::packaged_task<void()> lambda(
|
||||||
[=]() mutable
|
[=]() mutable
|
||||||
{
|
{
|
||||||
auto image = Image_decode(data, length, name);
|
auto image = Image_decode(data, length, name, alpha);
|
||||||
PROXY(onComplete(image));
|
PROXY(onComplete(image));
|
||||||
});
|
});
|
||||||
auto fut = _renderThread->add_task(lambda);
|
auto fut = _renderThread->add_task(lambda);
|
||||||
|
|||||||
Reference in New Issue
Block a user