Add ImmutableBuffer.fromFilePath (flutter/engine#36623)
This commit is contained in:
@@ -83,6 +83,7 @@ typedef CanvasPath Path;
|
||||
V(ImageDescriptor::initEncoded, 3) \
|
||||
V(ImmutableBuffer::init, 3) \
|
||||
V(ImmutableBuffer::initFromAsset, 3) \
|
||||
V(ImmutableBuffer::initFromFile, 3) \
|
||||
V(ImageDescriptor::initRaw, 6) \
|
||||
V(IsolateNameServerNatives::LookupPortByName, 1) \
|
||||
V(IsolateNameServerNatives::RegisterPortWithName, 2) \
|
||||
|
||||
@@ -6097,12 +6097,30 @@ class ImmutableBuffer extends NativeFieldWrapperClass1 {
|
||||
}).then((int length) => instance.._length = length);
|
||||
}
|
||||
|
||||
/// Create a buffer from the file with [path].
|
||||
///
|
||||
/// Throws an [Exception] if the asset does not exist.
|
||||
static Future<ImmutableBuffer> fromFilePath(String path) {
|
||||
final ImmutableBuffer instance = ImmutableBuffer._(0);
|
||||
return _futurize((_Callback<int> callback) {
|
||||
return instance._initFromFile(path, callback);
|
||||
}).then((int length) {
|
||||
if (length == -1) {
|
||||
throw Exception('Could not load file at $path.');
|
||||
}
|
||||
return instance.._length = length;
|
||||
});
|
||||
}
|
||||
|
||||
@FfiNative<Handle Function(Handle, Handle, Handle)>('ImmutableBuffer::init')
|
||||
external String? _init(Uint8List list, _Callback<void> callback);
|
||||
|
||||
@FfiNative<Handle Function(Handle, Handle, Handle)>('ImmutableBuffer::initFromAsset')
|
||||
external String? _initFromAsset(String assetKey, _Callback<int> callback);
|
||||
|
||||
@FfiNative<Handle Function(Handle, Handle, Handle)>('ImmutableBuffer::initFromFile')
|
||||
external String? _initFromFile(String assetKey, _Callback<int> callback);
|
||||
|
||||
/// The length, in bytes, of the underlying data.
|
||||
int get length => _length;
|
||||
int _length;
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "flutter/fml/file.h"
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/lib/ui/ui_dart_state.h"
|
||||
#include "flutter/lib/ui/window/platform_configuration.h"
|
||||
#include "third_party/tonic/converter/dart_converter.h"
|
||||
#include "third_party/tonic/dart_args.h"
|
||||
#include "third_party/tonic/dart_binding_macros.h"
|
||||
#include "third_party/tonic/dart_persistent_value.h"
|
||||
|
||||
#if FML_OS_ANDROID
|
||||
#include <sys/mman.h>
|
||||
@@ -77,6 +80,71 @@ Dart_Handle ImmutableBuffer::initFromAsset(Dart_Handle buffer_handle,
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
Dart_Handle ImmutableBuffer::initFromFile(Dart_Handle raw_buffer_handle,
|
||||
Dart_Handle file_path_handle,
|
||||
Dart_Handle callback_handle) {
|
||||
UIDartState::ThrowIfUIOperationsProhibited();
|
||||
if (!Dart_IsClosure(callback_handle)) {
|
||||
return tonic::ToDart("Callback must be a function");
|
||||
}
|
||||
|
||||
uint8_t* chars = nullptr;
|
||||
intptr_t file_path_length = 0;
|
||||
Dart_Handle result =
|
||||
Dart_StringToUTF8(file_path_handle, &chars, &file_path_length);
|
||||
if (Dart_IsError(result)) {
|
||||
return tonic::ToDart("File path must be valid UTF8");
|
||||
}
|
||||
|
||||
std::string file_path = std::string{reinterpret_cast<const char*>(chars),
|
||||
static_cast<size_t>(file_path_length)};
|
||||
|
||||
auto* dart_state = UIDartState::Current();
|
||||
auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner();
|
||||
auto buffer_callback =
|
||||
std::make_unique<tonic::DartPersistentValue>(dart_state, callback_handle);
|
||||
auto buffer_handle = std::make_unique<tonic::DartPersistentValue>(
|
||||
dart_state, raw_buffer_handle);
|
||||
|
||||
auto ui_task = fml::MakeCopyable(
|
||||
[buffer_callback = std::move(buffer_callback),
|
||||
buffer_handle = std::move(buffer_handle)](const sk_sp<SkData>& sk_data,
|
||||
size_t buffer_size) mutable {
|
||||
auto dart_state = buffer_callback->dart_state().lock();
|
||||
if (!dart_state) {
|
||||
return;
|
||||
}
|
||||
tonic::DartState::Scope scope(dart_state);
|
||||
if (!sk_data) {
|
||||
// -1 is used as a sentinel that the file could not be opened.
|
||||
tonic::DartInvoke(buffer_callback->Get(), {tonic::ToDart(-1)});
|
||||
return;
|
||||
}
|
||||
auto buffer = fml::MakeRefCounted<ImmutableBuffer>(sk_data);
|
||||
buffer->AssociateWithDartWrapper(buffer_handle->Get());
|
||||
tonic::DartInvoke(buffer_callback->Get(), {tonic::ToDart(buffer_size)});
|
||||
});
|
||||
|
||||
dart_state->GetConcurrentTaskRunner()->PostTask(
|
||||
[file_path = std::move(file_path),
|
||||
ui_task_runner = std::move(ui_task_runner), ui_task] {
|
||||
auto mapping = std::make_unique<fml::FileMapping>(fml::OpenFile(
|
||||
file_path.c_str(), false, fml::FilePermission::kRead));
|
||||
|
||||
sk_sp<SkData> sk_data;
|
||||
size_t buffer_size = 0;
|
||||
if (mapping->IsValid()) {
|
||||
buffer_size = mapping->GetSize();
|
||||
const void* bytes = static_cast<const void*>(mapping->GetMapping());
|
||||
sk_data = MakeSkDataWithCopy(bytes, buffer_size);
|
||||
}
|
||||
ui_task_runner->PostTask(
|
||||
[sk_data = std::move(sk_data), ui_task = std::move(ui_task),
|
||||
buffer_size]() { ui_task(sk_data, buffer_size); });
|
||||
});
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
#if FML_OS_ANDROID
|
||||
|
||||
// Compressed image buffers are allocated on the UI thread but are deleted on a
|
||||
|
||||
@@ -56,6 +56,20 @@ class ImmutableBuffer : public RefCountedDartWrappable<ImmutableBuffer> {
|
||||
Dart_Handle asset_name_handle,
|
||||
Dart_Handle callback_handle);
|
||||
|
||||
/// Initializes a new ImmutableData from an File path.
|
||||
///
|
||||
/// The zero indexed argument is the caller that will be registered as the
|
||||
/// Dart peer of the native ImmutableBuffer object.
|
||||
///
|
||||
/// The first indexed argumented is a String corresponding to the file path
|
||||
/// to load.
|
||||
///
|
||||
/// The second indexed argument is expected to be a void callback to signal
|
||||
/// when the copy has completed.
|
||||
static Dart_Handle initFromFile(Dart_Handle buffer_handle,
|
||||
Dart_Handle file_path_handle,
|
||||
Dart_Handle callback_handle);
|
||||
|
||||
/// The length of the data in bytes.
|
||||
size_t length() const {
|
||||
FML_DCHECK(data_);
|
||||
|
||||
@@ -38,6 +38,7 @@ UIDartState::Context::Context(
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
|
||||
bool enable_impeller)
|
||||
: task_runners(task_runners),
|
||||
snapshot_delegate(std::move(snapshot_delegate)),
|
||||
@@ -48,6 +49,7 @@ UIDartState::Context::Context(
|
||||
advisory_script_uri(std::move(advisory_script_uri)),
|
||||
advisory_script_entrypoint(std::move(advisory_script_entrypoint)),
|
||||
volatile_path_tracker(std::move(volatile_path_tracker)),
|
||||
concurrent_task_runner(concurrent_task_runner),
|
||||
enable_impeller(enable_impeller) {}
|
||||
|
||||
UIDartState::UIDartState(
|
||||
@@ -145,6 +147,11 @@ std::shared_ptr<VolatilePathTracker> UIDartState::GetVolatilePathTracker()
|
||||
return context_.volatile_path_tracker;
|
||||
}
|
||||
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner>
|
||||
UIDartState::GetConcurrentTaskRunner() const {
|
||||
return context_.concurrent_task_runner;
|
||||
}
|
||||
|
||||
void UIDartState::ScheduleMicrotask(Dart_Handle closure) {
|
||||
if (tonic::CheckAndHandleError(closure) || !Dart_IsClosure(closure)) {
|
||||
return;
|
||||
|
||||
@@ -54,6 +54,7 @@ class UIDartState : public tonic::DartState {
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
|
||||
bool enable_impeller);
|
||||
|
||||
/// The task runners used by the shell hosting this runtime controller. This
|
||||
@@ -93,6 +94,10 @@ class UIDartState : public tonic::DartState {
|
||||
/// Cache for tracking path volatility.
|
||||
std::shared_ptr<VolatilePathTracker> volatile_path_tracker;
|
||||
|
||||
/// The task runner whose tasks may be executed concurrently on a pool
|
||||
/// of shared worker threads.
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner;
|
||||
|
||||
/// Whether Impeller is enabled or not.
|
||||
bool enable_impeller = false;
|
||||
};
|
||||
@@ -128,6 +133,8 @@ class UIDartState : public tonic::DartState {
|
||||
|
||||
std::shared_ptr<VolatilePathTracker> GetVolatilePathTracker() const;
|
||||
|
||||
std::shared_ptr<fml::ConcurrentTaskRunner> GetConcurrentTaskRunner() const;
|
||||
|
||||
fml::WeakPtr<SnapshotDelegate> GetSnapshotDelegate() const;
|
||||
|
||||
fml::WeakPtr<ImageDecoder> GetImageDecoder() const;
|
||||
|
||||
@@ -728,6 +728,10 @@ class ImmutableBuffer {
|
||||
throw UnsupportedError('ImmutableBuffer.fromAsset is not supported on the web.');
|
||||
}
|
||||
|
||||
static Future<ImmutableBuffer> fromFilePath(String path) async {
|
||||
throw UnsupportedError('ImmutableBuffer.fromFilePath is not supported on the web.');
|
||||
}
|
||||
|
||||
Uint8List? _list;
|
||||
|
||||
int get length => _length;
|
||||
|
||||
@@ -61,7 +61,8 @@ std::unique_ptr<RuntimeController> RuntimeController::Spawn(
|
||||
std::move(io_manager), context_.unref_queue,
|
||||
std::move(image_decoder), std::move(image_generator_registry),
|
||||
std::move(advisory_script_uri), std::move(advisory_script_entrypoint),
|
||||
context_.volatile_path_tracker, context_.enable_impeller};
|
||||
context_.volatile_path_tracker, context_.concurrent_task_runner,
|
||||
context_.enable_impeller};
|
||||
auto result =
|
||||
std::make_unique<RuntimeController>(p_client, //
|
||||
vm_, //
|
||||
|
||||
@@ -100,6 +100,7 @@ Engine::Engine(Delegate& delegate,
|
||||
settings_.advisory_script_uri, // advisory script uri
|
||||
settings_.advisory_script_entrypoint, // advisory script entrypoint
|
||||
std::move(volatile_path_tracker), // volatile path tracker
|
||||
vm.GetConcurrentWorkerTaskRunner(), // concurrent task runner
|
||||
settings_.enable_impeller, // enable impeller
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,12 +21,29 @@ void main() {
|
||||
expect(error is Exception, true);
|
||||
});
|
||||
|
||||
test('Loading a file that does not exist returns null', () async {
|
||||
Object? error;
|
||||
try {
|
||||
await ImmutableBuffer.fromFilePath('ThisDoesNotExist');
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
expect(error, isNotNull);
|
||||
expect(error is Exception, true);
|
||||
});
|
||||
|
||||
test('returns the bytes of a bundled asset', () async {
|
||||
final ImmutableBuffer buffer = await ImmutableBuffer.fromAsset('DashInNooglerHat.jpg');
|
||||
|
||||
expect(buffer.length == 354679, true);
|
||||
});
|
||||
|
||||
test('returns the bytes of a file', () async {
|
||||
final ImmutableBuffer buffer = await ImmutableBuffer.fromFilePath('flutter/lib/ui/fixtures/DashInNooglerHat.jpg');
|
||||
|
||||
expect(buffer.length == 354679, true);
|
||||
});
|
||||
|
||||
test('Can load an asset with a space in the key', () async {
|
||||
// This assets actual path is "fixtures/DashInNooglerHat%20WithSpace.jpg"
|
||||
final ImmutableBuffer buffer = await ImmutableBuffer.fromAsset('DashInNooglerHat WithSpace.jpg');
|
||||
|
||||
Reference in New Issue
Block a user