Skip to content
This repository has been archived by the owner on Feb 25, 2025. It is now read-only.

[dart:ui] add an explicit API for loading assets #21153

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import("//flutter/testing/testing.gni")

source_set("ui") {
sources = [
"assets.cc",
"assets.h",
"compositing/scene.cc",
"compositing/scene.h",
"compositing/scene_builder.cc",
Expand Down
74 changes: 74 additions & 0 deletions lib/ui/assets.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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.

#include "flutter/lib/ui/assets.h"

#include "flutter/assets/asset_manager.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/platform_configuration.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/logging/dart_invoke.h"
#include "third_party/tonic/typed_data/dart_byte_data.h"
#include "third_party/tonic/typed_data/typed_list.h"

using tonic::DartInvoke;
using tonic::DartPersistentValue;
using tonic::ToDart;

namespace flutter {

void FinalizeSkData(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {

}

void Assets::loadAssetBytes(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle callback = Dart_GetNativeArgument(args, 1);
if (!Dart_IsClosure(callback)) {
Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function"));
return;
}
Dart_Handle asset_name_handle = Dart_GetNativeArgument(args, 0);
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_PropagateError(result);
return;
}
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) {
return;
}
const void* bytes_ = static_cast<const void*>(data->GetMapping());
void* bytes = const_cast<void*>(bytes_);
const intptr_t length = data->GetSize();
void* peer = reinterpret_cast<void*>(data.release());
Dart_Handle byte_buffer = Dart_NewExternalTypedDataWithFinalizer(
Dart_TypedData_kUint8, bytes, length, peer, length, FinalizeSkData);

// Dart_Handle byte_buffer =
// tonic::DartByteData::Create(data->GetMapping(), data->GetSize());
tonic::DartInvoke(callback, {ToDart(byte_buffer)});
}

void Assets::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"loadAssetBytes", loadAssetBytes, 2, true},
});
}

} // namespace flutter
23 changes: 23 additions & 0 deletions lib/ui/assets.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.

// @dart = 2.10

part of dart.ui;

/// Load the asset bytes specified by [assetKey].
///
/// The [assetKey] is generally the filepath of the asset which is bundled
/// into the flutter application. This API is not supported on the Web.
///
/// If the [assetKey] does not correspond to a real asset, returns `null`.
ByteData? loadAsset(String assetKey) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use ImmutableBuffer here, we could have a nice fast path for loading these without relying on Dart.

But right now, that class is only useful when creating images. We'd want to consider extending it in some way so you could get a ByteData from it, or a Uint8List or something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today, we ahve ImmutableBuffer.fromUint8List, but this could allow for creating ImmutableBuffers directly, which would definitely be useful for e.g. Image.asset.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm not remembering all of the context around ImmutableBuffer. If we had a ReadonlyUint8List from the core Dart SDK, would ImmutableBuffer still be needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately we need a way to somewhat compatible with the existing API and expose the bytes to users applications. Assets are used for more than just images today, we have APIs to load strings/bytedata/json

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's not to say we couldn't add a special API for images though, they are one of the most common cases

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

ImmutableBuffer was added to avoid having to do multiple copies of image data from a Uint8List to an SkData buffer if it ends up being reused.

If the Dart SDK has an appropriate type that's useful outside of dart:ui, that's probably better. But if not we could expand ImmutableBuffer to have access as ByteData (which, when Dart added some type appropriate for it, could just become a pass through to that type).

ByteData? result;
_loadAsset(assetKey, (Uint8List bytes) {
result = bytes.buffer.asByteData();
});
return result;
}

void _loadAsset(String asseyKey, void Function(Uint8List) onData) native 'loadAssetBytes';
26 changes: 26 additions & 0 deletions lib/ui/assets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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.

#ifndef FLUTTER_LIB_UI_ASSETS_H_
#define FLUTTER_LIB_UI_ASSETS_H_

#include "flutter/assets/asset_manager.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/platform_configuration.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/logging/dart_invoke.h"
#include "third_party/tonic/typed_data/dart_byte_data.h"
#include "third_party/tonic/typed_data/typed_list.h"

namespace flutter {

class Assets {
public:
static void loadAssetBytes(Dart_NativeArguments args);

static void RegisterNatives(tonic::DartLibraryNatives* natives);
};
} // namespace flutter
#endif // FLUTTER_LIB_UI_ASSETS_H_
2 changes: 2 additions & 0 deletions lib/ui/dart_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "flutter/lib/ui/dart_ui.h"

#include "flutter/fml/build_config.h"
#include "flutter/lib/ui/assets.h"
#include "flutter/lib/ui/compositing/scene.h"
#include "flutter/lib/ui/compositing/scene_builder.h"
#include "flutter/lib/ui/dart_runtime_hooks.h"
Expand Down Expand Up @@ -86,6 +87,7 @@ void DartUI::InitForGlobal() {
SemanticsUpdateBuilder::RegisterNatives(g_natives);
Vertices::RegisterNatives(g_natives);
PlatformConfiguration::RegisterNatives(g_natives);
Assets::RegisterNatives(g_natives);
#if defined(LEGACY_FUCHSIA_EMBEDDER)
SceneHost::RegisterNatives(g_natives);
#endif
Expand Down
1 change: 1 addition & 0 deletions lib/ui/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'dart:nativewrappers';
import 'dart:typed_data';

part 'annotations.dart';
part 'assets.dart';
part 'channel_buffers.dart';
part 'compositing.dart';
part 'geometry.dart';
Expand Down
3 changes: 3 additions & 0 deletions lib/ui/window/platform_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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/platform_message.h"
Expand Down Expand Up @@ -111,6 +112,8 @@ class PlatformConfigurationClient {
/// creation.
virtual FontCollection& GetFontCollection() = 0;

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
Expand Down
17 changes: 17 additions & 0 deletions lib/web_ui/lib/src/ui/assets.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.

// @dart = 2.10
part of ui;

/// Load the asset bytes specified by [assetKey].
///
/// The [assetKey] is generally the filepath of the asset which is bundled
/// into the flutter application. This API is not supported on the
/// Web.
///
/// If the [assetKey] does not correspond to a real asset, returns `null`.
ByteData? loadAsset(String assetKey) {
return null;
}
1 change: 1 addition & 0 deletions lib/web_ui/lib/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ part 'src/ui/test_embedding.dart';
part 'src/ui/text.dart';
part 'src/ui/tile_mode.dart';
part 'src/ui/window.dart';
part 'src/ui/assets.dart';

/// Provides a compile time constant to customize flutter framework and other
/// users of ui engine for web runtime.
Expand Down
6 changes: 6 additions & 0 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "flutter/runtime/runtime_controller.h"
#include "flutter/assets/asset_manager.h"

#include "flutter/fml/message_loop.h"
#include "flutter/fml/trace_event.h"
Expand Down Expand Up @@ -328,6 +329,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) {
Expand Down
4 changes: 4 additions & 0 deletions runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -499,6 +500,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;
Expand Down
3 changes: 3 additions & 0 deletions runtime/runtime_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -32,6 +33,8 @@ class RuntimeDelegate {

virtual FontCollection& GetFontCollection() = 0;

virtual std::shared_ptr<AssetManager> GetAssetManager() = 0;

virtual void UpdateIsolateDescription(const std::string isolate_name,
int64_t isolate_port) = 0;

Expand Down
4 changes: 4 additions & 0 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,10 @@ FontCollection& Engine::GetFontCollection() {
return font_collection_;
}

std::shared_ptr<AssetManager> Engine::GetAssetManager() {
return asset_manager_;
}

void Engine::DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet,
uint64_t trace_flow_id) {
animator_->EnqueueTraceFlowId(trace_flow_id);
Expand Down
3 changes: 3 additions & 0 deletions shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,9 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
// |RuntimeDelegate|
FontCollection& GetFontCollection() override;

// |RuntimeDelegate|
std::shared_ptr<AssetManager> GetAssetManager() override;

// |PointerDataDispatcher::Delegate|
void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet,
uint64_t trace_flow_id) override;
Expand Down
1 change: 1 addition & 0 deletions shell/common/engine_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates));
MOCK_METHOD1(HandlePlatformMessage, void(fml::RefPtr<PlatformMessage>));
MOCK_METHOD0(GetFontCollection, FontCollection&());
MOCK_METHOD0(GetAssetManager, std::shared_ptr<AssetManager>());
MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
MOCK_METHOD1(SetNeedsReportTimings, void(bool));
MOCK_METHOD1(ComputePlatformResolvedLocale,
Expand Down
18 changes: 18 additions & 0 deletions testing/dart/assets_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.

// @dart = 2.6
import 'dart:ui' as ui;

import 'package:test/test.dart';

void main() {
test('Loading an asset that does not exist returns null', () {
expect(ui.loadAsset('ThisDoesNotExist'), null);
});

test('returns the bytes of a bundled asset', () {
expect(ui.loadAsset('FontManifest.json'), isNotNull);
});
}