Add support for loading asset directly from ImmutableBuffer (flutter/engine#32999)

This commit is contained in:
Jonah Williams 2022-05-10 19:39:04 -07:00 committed by GitHub
parent 61ce899097
commit 8aef5df7b1
13 changed files with 191 additions and 6 deletions

View File

@ -2053,6 +2053,48 @@ Future<Codec> instantiateImageCodec(
bool allowUpscaling = true,
}) async {
final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(list);
return instantiateImageCodecFromBuffer(
buffer,
targetWidth: targetWidth,
targetHeight: targetHeight,
allowUpscaling: allowUpscaling,
);
}
/// Instantiates an image [Codec].
///
/// This method is a convenience wrapper around the [ImageDescriptor] API, and
/// using [ImageDescriptor] directly is preferred since it allows the caller to
/// make better determinations about how and whether to use the `targetWidth`
/// and `targetHeight` parameters.
///
/// The [buffer] parameter is the binary image data (e.g a PNG or GIF binary data).
/// The data can be for either static or animated images. The following image
/// formats are supported: {@macro dart.ui.imageFormats}
///
/// The [targetWidth] and [targetHeight] arguments specify the size of the
/// output image, in image pixels. If they are not equal to the intrinsic
/// dimensions of the image, then the image will be scaled after being decoded.
/// If the `allowUpscaling` parameter is not set to true, both dimensions will
/// be capped at the intrinsic dimensions of the image, even if only one of
/// them would have exceeded those intrinsic dimensions. If exactly one of these
/// two arguments is specified, then the aspect ratio will be maintained while
/// forcing the image to match the other given dimension. If neither is
/// specified, then the image maintains its intrinsic size.
///
/// Scaling the image to larger than its intrinsic size should usually be
/// avoided, since it causes the image to use more memory than necessary.
/// Instead, prefer scaling the [Canvas] transform. If the image must be scaled
/// up, the `allowUpscaling` parameter must be set to true.
///
/// The returned future can complete with an error if the image decoding has
/// failed.
Future<Codec> instantiateImageCodecFromBuffer(
ImmutableBuffer buffer, {
int? targetWidth,
int? targetHeight,
bool allowUpscaling = true,
}) async {
final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer);
if (!allowUpscaling) {
if (targetWidth != null && targetWidth > descriptor.width) {
@ -5511,7 +5553,7 @@ class Shadow {
/// The creator of this object is responsible for calling [dispose] when it is
/// no longer needed.
class ImmutableBuffer extends NativeFieldWrapperClass1 {
ImmutableBuffer._(this.length);
ImmutableBuffer._(this._length);
/// Creates a copy of the data from a [Uint8List] suitable for internal use
/// in the engine.
@ -5521,10 +5563,24 @@ class ImmutableBuffer extends NativeFieldWrapperClass1 {
instance._init(list, callback);
}).then((_) => instance);
}
/// Create a buffer from the asset with key [assetKey].
///
/// Throws an [Exception] if the asset does not exist.
static Future<ImmutableBuffer> fromAsset(String assetKey) {
final ImmutableBuffer instance = ImmutableBuffer._(0);
return _futurize((_Callback<int> callback) {
return instance._initFromAsset(assetKey, callback);
}).then((int length) => instance.._length = length);
}
void _init(Uint8List list, _Callback<void> callback) native 'ImmutableBuffer_init';
String? _initFromAsset(String assetKey, _Callback<int> callback) native 'ImmutableBuffer_initFromAsset';
/// The length, in bytes, of the underlying data.
final int length;
int get length => _length;
int _length;
bool _debugDisposed = false;

View File

@ -7,6 +7,7 @@
#include <cstring>
#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"
@ -30,6 +31,9 @@ ImmutableBuffer::~ImmutableBuffer() {}
void ImmutableBuffer::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({{"ImmutableBuffer_init", ImmutableBuffer::init, 3, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
natives->Register({{"ImmutableBuffer_initFromAsset",
ImmutableBuffer::initFromAsset, 3, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
void ImmutableBuffer::init(Dart_NativeArguments args) {
@ -49,6 +53,45 @@ void ImmutableBuffer::init(Dart_NativeArguments args) {
tonic::DartInvoke(callback_handle, {Dart_TypeVoid()});
}
void ImmutableBuffer::initFromAsset(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle callback_handle = Dart_GetNativeArgument(args, 2);
if (!Dart_IsClosure(callback_handle)) {
Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function"));
return;
}
Dart_Handle asset_name_handle = Dart_GetNativeArgument(args, 1);
uint8_t* chars = nullptr;
intptr_t asset_length = 0;
Dart_Handle result =
Dart_StringToUTF8(asset_name_handle, &chars, &asset_length);
if (Dart_IsError(result)) {
Dart_SetReturnValue(args, tonic::ToDart("Asset must be valid UTF8"));
return;
}
Dart_Handle immutable_buffer = Dart_GetNativeArgument(args, 0);
std::string asset_name = std::string{reinterpret_cast<const char*>(chars),
static_cast<size_t>(asset_length)};
std::shared_ptr<AssetManager> asset_manager = UIDartState::Current()
->platform_configuration()
->client()
->GetAssetManager();
std::unique_ptr<fml::Mapping> data = asset_manager->GetAsMapping(asset_name);
if (data == nullptr) {
Dart_SetReturnValue(args, tonic::ToDart("Asset not found"));
return;
}
auto size = data->GetSize();
const void* bytes = static_cast<const void*>(data->GetMapping());
auto sk_data = MakeSkDataWithCopy(bytes, size);
auto buffer = fml::MakeRefCounted<ImmutableBuffer>(sk_data);
buffer->AssociateWithDartWrapper(immutable_buffer);
tonic::DartInvoke(callback_handle, {tonic::ToDart(size)});
}
size_t ImmutableBuffer::GetAllocationSize() const {
return sizeof(ImmutableBuffer) + data_->size();
}

View File

@ -39,6 +39,19 @@ class ImmutableBuffer : public RefCountedDartWrappable<ImmutableBuffer> {
/// when the copy has completed.
static void init(Dart_NativeArguments args);
/// Initializes a new ImmutableData from an asset matching a provided
/// asset string.
///
/// 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 asset
/// to load.
///
/// The second indexed argument is expected to be a void callback to signal
/// when the copy has completed.
static void initFromAsset(Dart_NativeArguments args);
/// The length of the data in bytes.
size_t length() const {
FML_DCHECK(data_);

View File

@ -11,6 +11,7 @@
#include <unordered_map>
#include <vector>
#include "flutter/assets/asset_manager.h"
#include "flutter/fml/time/time_point.h"
#include "flutter/lib/ui/semantics/semantics_update.h"
#include "flutter/lib/ui/window/pointer_data_packet.h"
@ -98,6 +99,11 @@ class PlatformConfigurationClient {
/// creation.
virtual FontCollection& GetFontCollection() = 0;
//--------------------------------------------------------------------------
/// @brief Returns the current collection of assets available on the
/// platform.
virtual std::shared_ptr<AssetManager> GetAssetManager() = 0;
//--------------------------------------------------------------------------
/// @brief Notifies this client of the name of the root isolate and its
/// port when that isolate is launched, restarted (in the

View File

@ -484,6 +484,20 @@ Future<Codec> instantiateImageCodec(
}
}
Future<Codec> instantiateImageCodecFromBuffer(
ImmutableBuffer buffer, {
int? targetWidth,
int? targetHeight,
bool allowUpscaling = true,
}) async {
if (engine.useCanvasKit) {
return engine.skiaInstantiateImageCodec(buffer._list!, targetWidth, targetHeight);
} else {
final html.Blob blob = html.Blob(<dynamic>[buffer._list!.buffer]);
return engine.HtmlBlobCodec(blob);
}
}
Future<Codec> webOnlyInstantiateImageCodecFromUrl(Uri uri,
{engine.WebOnlyImageCodecChunkCallback? chunkCallback}) {
if (engine.useCanvasKit) {
@ -735,15 +749,21 @@ class ImageShader extends Shader {
}
class ImmutableBuffer {
ImmutableBuffer._(this.length);
ImmutableBuffer._(this._length);
static Future<ImmutableBuffer> fromUint8List(Uint8List list) async {
final ImmutableBuffer instance = ImmutableBuffer._(list.length);
instance._list = list;
return instance;
}
static Future<ImmutableBuffer> fromAsset(String assetKey) async {
throw UnsupportedError('ImmutableBuffer.fromAsset is not supported on the web.');
}
Uint8List? _list;
final int length;
int get length => _length;
int _length;
bool get debugDisposed {
late bool disposed;

View File

@ -299,6 +299,11 @@ FontCollection& RuntimeController::GetFontCollection() {
return client_.GetFontCollection();
}
// |PlatfromConfigurationClient|
std::shared_ptr<AssetManager> RuntimeController::GetAssetManager() {
return client_.GetAssetManager();
}
// |PlatformConfigurationClient|
void RuntimeController::UpdateIsolateDescription(const std::string isolate_name,
int64_t isolate_port) {

View File

@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "flutter/assets/asset_manager.h"
#include "flutter/common/task_runners.h"
#include "flutter/flow/layers/layer_tree.h"
#include "flutter/fml/macros.h"
@ -614,6 +615,9 @@ class RuntimeController : public PlatformConfigurationClient {
// |PlatformConfigurationClient|
FontCollection& GetFontCollection() override;
// |PlatformConfigurationClient|
std::shared_ptr<AssetManager> GetAssetManager() override;
// |PlatformConfigurationClient|
void UpdateIsolateDescription(const std::string isolate_name,
int64_t isolate_port) override;

View File

@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "flutter/assets/asset_manager.h"
#include "flutter/flow/layers/layer_tree.h"
#include "flutter/lib/ui/semantics/custom_accessibility_action.h"
#include "flutter/lib/ui/semantics/semantics_node.h"
@ -33,6 +34,8 @@ class RuntimeDelegate {
virtual FontCollection& GetFontCollection() = 0;
virtual std::shared_ptr<AssetManager> GetAssetManager() = 0;
virtual void OnRootIsolateCreated() = 0;
virtual void UpdateIsolateDescription(const std::string isolate_name,

View File

@ -755,8 +755,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
// |RuntimeDelegate|
FontCollection& GetFontCollection() override;
// Return the asset manager associated with the current engine, or nullptr.
std::shared_ptr<AssetManager> GetAssetManager();
// |RuntimeDelegate|
std::shared_ptr<AssetManager> GetAssetManager() override;
// Return the weak_ptr of ImageDecoder.
fml::WeakPtr<ImageDecoder> GetImageDecoderWeakPtr();

View File

@ -53,6 +53,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates));
MOCK_METHOD1(HandlePlatformMessage, void(std::unique_ptr<PlatformMessage>));
MOCK_METHOD0(GetFontCollection, FontCollection&());
MOCK_METHOD0(GetAssetManager, std::shared_ptr<AssetManager>());
MOCK_METHOD0(OnRootIsolateCreated, void());
MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
MOCK_METHOD1(SetNeedsReportTimings, void(bool));

View File

@ -5,6 +5,7 @@
import("//flutter/testing/dart/compile_test.gni")
tests = [
"assets_test.dart",
"canvas_test.dart",
"channel_buffers_test.dart",
"codec_test.dart",

View File

@ -0,0 +1,32 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:litetest/litetest.dart';
void main() {
test('Loading an asset that does not exist returns null', () async {
Object? error;
try {
await ui.ImmutableBuffer.fromAsset('ThisDoesNotExist');
} catch (err) {
error = err;
}
expect(error, isNotNull);
expect(error is Exception, true);
});
test('returns the bytes of a bundled asset', () async {
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromAsset('assets/DashInNooglerHat.jpg');
expect(buffer.length == 354679, true);
});
test('can dispose immutable buffer', () async {
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromAsset('assets/DashInNooglerHat.jpg');
buffer.dispose();
});
}

View File

@ -286,6 +286,7 @@ def RunDartTest(build_dir, test_packages, dart_file, verbose_dart_snapshot, mult
command_args += [
'--use-test-fonts',
'--icu-data-file-path=%s' % os.path.join(build_dir, 'icudtl.dat'),
'--flutter-assets-dir=%s' % os.path.join(build_dir, 'gen', 'flutter', 'lib', 'ui'),
kernel_file_output,
]