mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add support for loading asset directly from ImmutableBuffer (flutter/engine#32999)
This commit is contained in:
parent
61ce899097
commit
8aef5df7b1
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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_);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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",
|
||||
|
||||
32
engine/src/flutter/testing/dart/assets_test.dart
Normal file
32
engine/src/flutter/testing/dart/assets_test.dart
Normal 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();
|
||||
});
|
||||
}
|
||||
@ -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,
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user