Add ImmutableBuffer.fromFilePath (flutter/engine#36623)

This commit is contained in:
Jonah Williams
2022-10-05 15:18:03 -07:00
committed by GitHub
parent 36da633c2d
commit aadc2d3094
10 changed files with 139 additions and 1 deletions

View File

@@ -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) \

View File

@@ -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;

View File

@@ -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

View File

@@ -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_);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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_, //

View File

@@ -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
});
}

View File

@@ -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');