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

Commit 3da3a9c

Browse files
committed
[Impeller] Started throwing errors if dart:ui/Image.toByteData fails
1 parent 1bb228d commit 3da3a9c

File tree

6 files changed

+140
-46
lines changed

6 files changed

+140
-46
lines changed

fml/status_or.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_FML_STATUS_OR_H_
6+
#define FLUTTER_FML_STATUS_OR_H_
7+
8+
#include <optional>
9+
10+
#include "flutter/fml/status.h"
11+
12+
namespace fml {
13+
14+
/// TODO(): Replace with absl::StatusOr.
15+
template <typename T>
16+
class StatusOr {
17+
public:
18+
StatusOr(const T& value) : status_(), value_(value) {}
19+
StatusOr(const Status& status) : status_(status), value_() {}
20+
21+
StatusOr(const StatusOr&) = default;
22+
StatusOr(StatusOr&&) = default;
23+
24+
StatusOr& operator=(const StatusOr&) = default;
25+
StatusOr& operator=(StatusOr&&) = default;
26+
27+
StatusOr& operator=(const T& value) {
28+
status_ = Status();
29+
value_ = value;
30+
return *this;
31+
}
32+
33+
StatusOr& operator=(const T&& value) {
34+
status_ = Status();
35+
value_ = std::move(value);
36+
return *this;
37+
}
38+
39+
StatusOr& operator=(const Status& value) {
40+
status_ = value;
41+
value_ = std::nullopt;
42+
return *this;
43+
}
44+
45+
const Status& status() const { return status_; }
46+
47+
bool ok() const { return status_.ok(); }
48+
49+
const T& value() const {
50+
FML_CHECK(status_.ok());
51+
return value_.value();
52+
}
53+
54+
T& value() {
55+
FML_CHECK(status_.ok());
56+
return value_.value();
57+
}
58+
59+
private:
60+
Status status_;
61+
std::optional<T> value_;
62+
};
63+
64+
} // namespace fml
65+
66+
#endif

lib/ui/painting.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,16 +1956,23 @@ base class _Image extends NativeFieldWrapperClass1 {
19561956
external int get height;
19571957

19581958
Future<ByteData?> toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) {
1959-
return _futurize((_Callback<ByteData> callback) {
1960-
return _toByteData(format.index, (Uint8List? encoded) {
1961-
callback(encoded!.buffer.asByteData());
1962-
});
1959+
final Completer<ByteData?> completer = Completer<ByteData?>();
1960+
final String? syncError = _toByteData(format.index, (Uint8List? encoded, String? error) {
1961+
if (encoded != null) {
1962+
completer.complete(encoded.buffer.asByteData());
1963+
} else {
1964+
completer.completeError(Exception(error ?? 'unknown error'));
1965+
}
19631966
});
1967+
if (syncError != null) {
1968+
completer.completeError(Exception(syncError));
1969+
}
1970+
return completer.future;
19641971
}
19651972

19661973
/// Returns an error message on failure, null on success.
19671974
@Native<Handle Function(Pointer<Void>, Int32, Handle)>(symbol: 'Image::toByteData')
1968-
external String? _toByteData(int format, _Callback<Uint8List?> callback);
1975+
external String? _toByteData(int format, void Function(Uint8List?, String?) callback);
19691976

19701977
bool _disposed = false;
19711978
void dispose() {

lib/ui/painting/image_encoding.cc

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,27 @@ void FinalizeSkData(void* isolate_callback_data, void* peer) {
4040
}
4141

4242
void InvokeDataCallback(std::unique_ptr<DartPersistentValue> callback,
43-
sk_sp<SkData> buffer) {
43+
fml::StatusOr<sk_sp<SkData>>&& buffer) {
4444
std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
4545
if (!dart_state) {
4646
return;
4747
}
4848
tonic::DartState::Scope scope(dart_state);
49-
if (!buffer) {
50-
DartInvoke(callback->value(), {Dart_Null()});
49+
if (!buffer.ok()) {
50+
std::string error_copy(buffer.status().message());
51+
Dart_Handle dart_string = ToDart(error_copy);
52+
DartInvoke(callback->value(), {Dart_Null(), dart_string});
5153
return;
5254
}
5355
// Skia will not modify the buffer, and it is backed by memory that is
5456
// read/write, so Dart can be given direct access to the buffer through an
5557
// external Uint8List.
56-
void* bytes = const_cast<void*>(buffer->data());
57-
const intptr_t length = buffer->size();
58-
void* peer = reinterpret_cast<void*>(buffer.release());
58+
void* bytes = const_cast<void*>(buffer.value()->data());
59+
const intptr_t length = buffer.value()->size();
60+
void* peer = reinterpret_cast<void*>(buffer.value().release());
5961
Dart_Handle dart_data = Dart_NewExternalTypedDataWithFinalizer(
6062
Dart_TypedData_kUint8, bytes, length, peer, length, FinalizeSkData);
61-
DartInvoke(callback->value(), {dart_data});
63+
DartInvoke(callback->value(), {dart_data, Dart_Null()});
6264
}
6365

6466
sk_sp<SkData> CopyImageByteData(const sk_sp<SkImage>& raster_image,
@@ -110,21 +112,30 @@ void EncodeImageAndInvokeDataCallback(
110112
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
111113
const std::shared_ptr<impeller::Context>& impeller_context,
112114
bool is_impeller_enabled) {
113-
auto callback_task = fml::MakeCopyable(
114-
[callback = std::move(callback)](sk_sp<SkData> encoded) mutable {
115+
auto callback_task =
116+
fml::MakeCopyable([callback = std::move(callback)](
117+
fml::StatusOr<sk_sp<SkData>>&& encoded) mutable {
115118
InvokeDataCallback(std::move(callback), std::move(encoded));
116119
});
117120
// The static leak checker gets confused by the use of fml::MakeCopyable in
118121
// EncodeImage.
119122
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
120-
auto encode_task = [callback_task = std::move(callback_task), format,
121-
ui_task_runner](const sk_sp<SkImage>& raster_image) {
122-
sk_sp<SkData> encoded = EncodeImage(raster_image, format);
123-
ui_task_runner->PostTask([callback_task = callback_task,
124-
encoded = std::move(encoded)]() mutable {
125-
callback_task(std::move(encoded));
126-
});
127-
};
123+
auto encode_task =
124+
[callback_task = std::move(callback_task), format,
125+
ui_task_runner](const fml::StatusOr<sk_sp<SkImage>>& raster_image) {
126+
if (raster_image.ok()) {
127+
sk_sp<SkData> encoded = EncodeImage(raster_image.value(), format);
128+
ui_task_runner->PostTask([callback_task = callback_task,
129+
encoded = std::move(encoded)]() mutable {
130+
callback_task(std::move(encoded));
131+
});
132+
} else {
133+
ui_task_runner->PostTask([callback_task = callback_task,
134+
raster_image = raster_image]() mutable {
135+
callback_task(raster_image.status());
136+
});
137+
}
138+
};
128139

129140
FML_DCHECK(image);
130141
#if IMPELLER_SUPPORTS_RENDERING

lib/ui/painting/image_encoding_impeller.cc

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,15 @@ sk_sp<SkImage> ConvertBufferToSkImage(
5858

5959
void DoConvertImageToRasterImpeller(
6060
const sk_sp<DlImage>& dl_image,
61-
std::function<void(sk_sp<SkImage>)> encode_task,
61+
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
6262
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
6363
const std::shared_ptr<impeller::Context>& impeller_context) {
6464
is_gpu_disabled_sync_switch->Execute(
6565
fml::SyncSwitch::Handlers()
66-
.SetIfTrue([&encode_task] { encode_task(nullptr); })
66+
.SetIfTrue([&encode_task] {
67+
encode_task(
68+
fml::Status(fml::StatusCode::kUnavailable, "gpu unavailable"));
69+
})
6770
.SetIfFalse([&dl_image, &encode_task, &impeller_context] {
6871
ImageEncodingImpeller::ConvertDlImageToSkImage(
6972
dl_image, std::move(encode_task), impeller_context);
@@ -74,19 +77,21 @@ void DoConvertImageToRasterImpeller(
7477

7578
void ImageEncodingImpeller::ConvertDlImageToSkImage(
7679
const sk_sp<DlImage>& dl_image,
77-
std::function<void(sk_sp<SkImage>)> encode_task,
80+
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
7881
const std::shared_ptr<impeller::Context>& impeller_context) {
7982
auto texture = dl_image->impeller_texture();
8083

8184
if (impeller_context == nullptr) {
8285
FML_LOG(ERROR) << "Impeller context was null.";
83-
encode_task(nullptr);
86+
encode_task(fml::Status(fml::StatusCode::kFailedPrecondition,
87+
"Impeller context was null."));
8488
return;
8589
}
8690

8791
if (texture == nullptr) {
8892
FML_LOG(ERROR) << "Image was null.";
89-
encode_task(nullptr);
93+
encode_task(
94+
fml::Status(fml::StatusCode::kFailedPrecondition, "Image was null."));
9095
return;
9196
}
9297

@@ -95,13 +100,15 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage(
95100

96101
if (dimensions.isEmpty()) {
97102
FML_LOG(ERROR) << "Image dimensions were empty.";
98-
encode_task(nullptr);
103+
encode_task(fml::Status(fml::StatusCode::kFailedPrecondition,
104+
"Image dimensions were empty."));
99105
return;
100106
}
101107

102108
if (!color_type.has_value()) {
103109
FML_LOG(ERROR) << "Failed to get color type from pixel format.";
104-
encode_task(nullptr);
110+
encode_task(fml::Status(fml::StatusCode::kUnimplemented,
111+
"Failed to get color type from pixel format."));
105112
return;
106113
}
107114

@@ -121,7 +128,7 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage(
121128
encode_task = std::move(encode_task)](
122129
impeller::CommandBuffer::Status status) {
123130
if (status != impeller::CommandBuffer::Status::kCompleted) {
124-
encode_task(nullptr);
131+
encode_task(fml::Status(fml::StatusCode::kUnknown, ""));
125132
return;
126133
}
127134
auto sk_image = ConvertBufferToSkImage(buffer, color_type, dimensions);
@@ -135,14 +142,14 @@ void ImageEncodingImpeller::ConvertDlImageToSkImage(
135142

136143
void ImageEncodingImpeller::ConvertImageToRaster(
137144
const sk_sp<DlImage>& dl_image,
138-
std::function<void(sk_sp<SkImage>)> encode_task,
145+
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
139146
const fml::RefPtr<fml::TaskRunner>& raster_task_runner,
140147
const fml::RefPtr<fml::TaskRunner>& io_task_runner,
141148
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
142149
const std::shared_ptr<impeller::Context>& impeller_context) {
143150
auto original_encode_task = std::move(encode_task);
144151
encode_task = [original_encode_task = std::move(original_encode_task),
145-
io_task_runner](sk_sp<SkImage> image) mutable {
152+
io_task_runner](fml::StatusOr<sk_sp<SkImage>> image) mutable {
146153
fml::TaskRunner::RunNowOrPostTask(
147154
io_task_runner,
148155
[original_encode_task = std::move(original_encode_task),

lib/ui/painting/image_encoding_impeller.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "flutter/common/task_runners.h"
99
#include "flutter/display_list/image/dl_image.h"
10+
#include "flutter/fml/status_or.h"
1011
#include "flutter/fml/synchronization/sync_switch.h"
1112

1213
namespace impeller {
@@ -26,14 +27,14 @@ class ImageEncodingImpeller {
2627
/// Visible for testing.
2728
static void ConvertDlImageToSkImage(
2829
const sk_sp<DlImage>& dl_image,
29-
std::function<void(sk_sp<SkImage>)> encode_task,
30+
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
3031
const std::shared_ptr<impeller::Context>& impeller_context);
3132

3233
/// Converts a DlImage to a SkImage.
3334
/// `encode_task` is executed with the resulting `SkImage`.
3435
static void ConvertImageToRaster(
3536
const sk_sp<DlImage>& dl_image,
36-
std::function<void(sk_sp<SkImage>)> encode_task,
37+
std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
3738
const fml::RefPtr<fml::TaskRunner>& raster_task_runner,
3839
const fml::RefPtr<fml::TaskRunner>& io_task_runner,
3940
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,

lib/ui/painting/image_encoding_unittests.cc

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,14 @@ TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage16Float) {
238238
bool did_call = false;
239239
ImageEncodingImpeller::ConvertDlImageToSkImage(
240240
image,
241-
[&did_call](const sk_sp<SkImage>& image) {
241+
[&did_call](const fml::StatusOr<sk_sp<SkImage>>& image) {
242242
did_call = true;
243-
ASSERT_TRUE(image);
244-
EXPECT_EQ(100, image->width());
245-
EXPECT_EQ(100, image->height());
246-
EXPECT_EQ(kRGBA_F16_SkColorType, image->colorType());
247-
EXPECT_EQ(nullptr, image->colorSpace());
243+
ASSERT_TRUE(image.ok());
244+
ASSERT_TRUE(image.value());
245+
EXPECT_EQ(100, image.value()->width());
246+
EXPECT_EQ(100, image.value()->height());
247+
EXPECT_EQ(kRGBA_F16_SkColorType, image.value()->colorType());
248+
EXPECT_EQ(nullptr, image.value()->colorSpace());
248249
},
249250
context);
250251
EXPECT_TRUE(did_call);
@@ -264,13 +265,14 @@ TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage10XR) {
264265
bool did_call = false;
265266
ImageEncodingImpeller::ConvertDlImageToSkImage(
266267
image,
267-
[&did_call](const sk_sp<SkImage>& image) {
268+
[&did_call](const fml::StatusOr<sk_sp<SkImage>>& image) {
268269
did_call = true;
269-
ASSERT_TRUE(image);
270-
EXPECT_EQ(100, image->width());
271-
EXPECT_EQ(100, image->height());
272-
EXPECT_EQ(kBGR_101010x_XR_SkColorType, image->colorType());
273-
EXPECT_EQ(nullptr, image->colorSpace());
270+
ASSERT_TRUE(image.ok());
271+
ASSERT_TRUE(image.value());
272+
EXPECT_EQ(100, image.value()->width());
273+
EXPECT_EQ(100, image.value()->height());
274+
EXPECT_EQ(kBGR_101010x_XR_SkColorType, image.value()->colorType());
275+
EXPECT_EQ(nullptr, image.value()->colorSpace());
274276
},
275277
context);
276278
EXPECT_TRUE(did_call);

0 commit comments

Comments
 (0)