Skip to content

Commit feaec27

Browse files
authored
Implements decodeImageFromPixelsSync (#179519)
fixes #178488 This doesn't implement the following. They can be implemented in later PRs. - a skia implementation (maybe won't implement?) - a web implementation - resizing - target pixel format ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent ca27d3c commit feaec27

23 files changed

Lines changed: 585 additions & 14 deletions

engine/src/flutter/lib/gpu/texture.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
#include "flutter/lib/gpu/formats.h"
88
#include "flutter/lib/ui/painting/image.h"
9+
#include "flutter/lib/ui/ui_dart_state.h"
910
#include "fml/make_copyable.h"
1011
#include "fml/mapping.h"
1112
#include "impeller/core/allocator.h"
1213
#include "impeller/core/formats.h"
1314
#include "impeller/core/texture.h"
15+
1416
#if IMPELLER_SUPPORTS_RENDERING
1517
#include "impeller/display_list/dl_image_impeller.h" // nogncheck
1618
#endif

engine/src/flutter/lib/ui/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ source_set("ui") {
200200
"painting/image_decoder_impeller.h",
201201
"painting/image_encoding_impeller.cc",
202202
"painting/image_encoding_impeller.h",
203+
"painting/pixel_deferred_image_gpu_impeller.cc",
204+
"painting/pixel_deferred_image_gpu_impeller.h",
203205
]
204206

205207
deps += [

engine/src/flutter/lib/ui/dart_ui.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ typedef CanvasPath Path;
8585
/* Other */ \
8686
V(FontCollection::LoadFontFromList) \
8787
V(ImageDescriptor::initEncoded) \
88+
V(Image::decodeImageFromPixelsSync) \
8889
V(ImageFilter::equals) \
8990
V(ImmutableBuffer::init) \
9091
V(ImmutableBuffer::initFromAsset) \

engine/src/flutter/lib/ui/painting.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2741,6 +2741,31 @@ void decodeImageFromPixels(
27412741
});
27422742
}
27432743

2744+
/// Decodes the given [pixels] into an [Image] synchronously.
2745+
///
2746+
/// The [pixels] are expected to be in the format specified by [format].
2747+
///
2748+
/// The [width] and [height] arguments specify the dimensions of the image.
2749+
///
2750+
/// This function returns an [Image] immediately. The image might not be
2751+
/// fully decoded yet, but it can be drawn to a [Canvas].
2752+
Image decodeImageFromPixelsSync(Uint8List pixels, int width, int height, PixelFormat format) {
2753+
final image = Image._(_Image._(), width, height);
2754+
_decodeImageFromPixelsSync(pixels, width, height, format.index, image._image);
2755+
return image;
2756+
}
2757+
2758+
@Native<Void Function(Handle, Int32, Int32, Int32, Handle)>(
2759+
symbol: 'Image::decodeImageFromPixelsSync',
2760+
)
2761+
external void _decodeImageFromPixelsSync(
2762+
Uint8List pixels,
2763+
int width,
2764+
int height,
2765+
int format,
2766+
_Image outImage,
2767+
);
2768+
27442769
/// Determines the winding rule that decides how the interior of a [Path] is
27452770
/// calculated.
27462771
///

engine/src/flutter/lib/ui/painting/canvas.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,14 @@ void Canvas::drawArc(double left,
378378
void Canvas::drawPath(const CanvasPath* path,
379379
Dart_Handle paint_objects,
380380
Dart_Handle paint_data) {
381-
Paint paint(paint_objects, paint_data);
382-
383-
FML_DCHECK(paint.isNotNull());
384381
if (!path) {
385382
Dart_ThrowException(
386383
ToDart("Canvas.drawPath called with non-genuine Path."));
387384
return;
388385
}
389386
if (display_list_builder_) {
387+
Paint paint(paint_objects, paint_data);
388+
FML_DCHECK(paint.isNotNull());
390389
DlPaint dl_paint;
391390
paint.paint(dl_paint, kDrawPathFlags, DlTileMode::kDecal);
392391
builder()->DrawPath(path->path(), dl_paint);

engine/src/flutter/lib/ui/painting/display_list_deferred_image_gpu_impeller.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class DlDeferredImageGPUImpeller final : public DlImage {
8888
bool isTextureBacked() const;
8989

9090
const std::shared_ptr<impeller::Texture> texture() const {
91+
FML_DCHECK(raster_task_runner_->RunsTasksOnCurrentThread());
9192
return texture_;
9293
}
9394

engine/src/flutter/lib/ui/painting/display_list_deferred_image_gpu_impeller_unittests.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,18 @@ TEST(DlDeferredImageGPUImpeller, TrashesDisplayList) {
7171
snapshot_delegate_weak_ptr = snapshot_delegate->GetWeakPtr();
7272
});
7373

74+
sk_sp<DlDeferredImageGPUImpeller> image;
7475
// Pause raster thread.
7576
fml::AutoResetWaitableEvent latch;
76-
task_runner->PostTask([&latch]() { latch.Wait(); });
77+
task_runner->PostTask([&latch, &image]() {
78+
latch.Wait();
79+
EXPECT_FALSE(image->impeller_texture());
80+
});
7781

78-
auto image = DlDeferredImageGPUImpeller::Make(
82+
image = DlDeferredImageGPUImpeller::Make(
7983
builder.Build(), size, SnapshotPixelFormat::kDontCare,
8084
snapshot_delegate_weak_ptr, task_runner);
8185

82-
EXPECT_FALSE(image->impeller_texture());
83-
8486
// Unpause raster thread.
8587
latch.Signal();
8688

engine/src/flutter/lib/ui/painting/image.cc

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@
44

55
#include "flutter/lib/ui/painting/image.h"
66

7-
#include <algorithm>
8-
#include <limits>
97
#include "tonic/logging/dart_invoke.h"
108

119
#if IMPELLER_SUPPORTS_RENDERING
1210
#include "flutter/lib/ui/painting/image_encoding_impeller.h"
11+
#include "flutter/lib/ui/painting/pixel_deferred_image_gpu_impeller.h"
1312
#endif
13+
#include "flutter/display_list/image/dl_image.h"
1414
#include "flutter/lib/ui/painting/image_encoding.h"
15+
#include "flutter/lib/ui/ui_dart_state.h"
1516
#include "third_party/tonic/converter/dart_converter.h"
16-
#include "third_party/tonic/dart_args.h"
17-
#include "third_party/tonic/dart_binding_macros.h"
18-
#include "third_party/tonic/dart_library_natives.h"
1917

2018
namespace flutter {
2119

@@ -56,3 +54,119 @@ int CanvasImage::colorSpace() {
5654
}
5755

5856
} // namespace flutter
57+
58+
namespace flutter {
59+
60+
namespace {
61+
62+
int BytesPerPixel(PixelFormat pixel_format) {
63+
switch (pixel_format) {
64+
case PixelFormat::kRgba8888:
65+
case PixelFormat::kBgra8888:
66+
case PixelFormat::kRFloat32:
67+
return 4;
68+
case PixelFormat::kRgbaFloat32:
69+
return 16;
70+
}
71+
return 4;
72+
}
73+
74+
SkColorType PixelFormatToSkColorType(PixelFormat pixel_format) {
75+
switch (pixel_format) {
76+
case PixelFormat::kRgba8888:
77+
return kRGBA_8888_SkColorType;
78+
case PixelFormat::kBgra8888:
79+
return kBGRA_8888_SkColorType;
80+
case PixelFormat::kRgbaFloat32:
81+
return kRGBA_F32_SkColorType;
82+
case PixelFormat::kRFloat32:
83+
return kUnknown_SkColorType;
84+
}
85+
return kUnknown_SkColorType;
86+
}
87+
88+
// Returns only static strings.
89+
const char* DoDecodeImageFromPixelsSync(Dart_Handle pixels_handle,
90+
uint32_t width,
91+
uint32_t height,
92+
int32_t pixel_format,
93+
Dart_Handle raw_image_handle) {
94+
auto* dart_state = UIDartState::Current();
95+
if (!dart_state) {
96+
return "Dart state is null.";
97+
}
98+
99+
if (!dart_state->IsImpellerEnabled()) {
100+
return "decodeImageFromPixelsSync is not implemented on Skia.";
101+
}
102+
103+
if (width == 0 || height == 0) {
104+
return "Image dimensions must be greater than zero.";
105+
}
106+
107+
if (pixel_format < 0 ||
108+
pixel_format > static_cast<int32_t>(kLastPixelFormat)) {
109+
return "Invalid pixel format.";
110+
}
111+
PixelFormat format = static_cast<PixelFormat>(pixel_format);
112+
113+
sk_sp<SkData> sk_data;
114+
sk_sp<SkImage> sk_image;
115+
{
116+
tonic::Uint8List pixels(pixels_handle);
117+
if (!pixels.data()) {
118+
return "Pixels must not be null.";
119+
}
120+
121+
int32_t row_bytes = width * BytesPerPixel(format);
122+
SkColorType color_type = PixelFormatToSkColorType(format);
123+
if (color_type == kUnknown_SkColorType) {
124+
return "Unsupported pixel format.";
125+
}
126+
127+
SkImageInfo image_info =
128+
SkImageInfo::Make(width, height, color_type, kUnpremul_SkAlphaType);
129+
if (pixel_format == 2) { // rgbaFloat32
130+
image_info = image_info.makeAlphaType(kUnpremul_SkAlphaType);
131+
} else {
132+
image_info = image_info.makeAlphaType(kPremul_SkAlphaType);
133+
}
134+
135+
sk_data = SkData::MakeWithCopy(pixels.data(), pixels.num_elements());
136+
sk_image = SkImages::RasterFromData(image_info, sk_data, row_bytes);
137+
if (!sk_image) {
138+
return "Failed to create image from pixels.";
139+
}
140+
}
141+
142+
auto snapshot_delegate = dart_state->GetSnapshotDelegate();
143+
auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner();
144+
145+
auto result_image = CanvasImage::Create();
146+
sk_sp<DlImage> deferred_image;
147+
148+
#if IMPELLER_SUPPORTS_RENDERING
149+
deferred_image = PixelDeferredImageGPUImpeller::Make(
150+
sk_image, std::move(snapshot_delegate), std::move(raster_task_runner));
151+
#endif // IMPELLER_SUPPORTS_RENDERING
152+
153+
result_image->set_image(deferred_image);
154+
result_image->AssociateWithDartWrapper(raw_image_handle);
155+
156+
return nullptr;
157+
}
158+
} // namespace
159+
160+
void CanvasImage::decodeImageFromPixelsSync(Dart_Handle pixels_handle,
161+
uint32_t width,
162+
uint32_t height,
163+
int32_t pixel_format,
164+
Dart_Handle raw_image_handle) {
165+
const char* error = DoDecodeImageFromPixelsSync(
166+
pixels_handle, width, height, pixel_format, raw_image_handle);
167+
if (error) {
168+
Dart_ThrowException(tonic::ToDart(error));
169+
}
170+
}
171+
172+
} // namespace flutter

engine/src/flutter/lib/ui/painting/image.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@
77

88
#include "flutter/display_list/image/dl_image.h"
99
#include "flutter/lib/ui/dart_wrapper.h"
10-
#include "flutter/lib/ui/ui_dart_state.h"
11-
#include "third_party/skia/include/core/SkImage.h"
1210

1311
namespace flutter {
1412

13+
// Must be kept in sync with painting.dart.
14+
enum class PixelFormat {
15+
kRgba8888,
16+
kBgra8888,
17+
kRgbaFloat32,
18+
kRFloat32, // kLastPixelFormat
19+
};
20+
21+
constexpr PixelFormat kLastPixelFormat = PixelFormat::kRFloat32;
22+
1523
// Must be kept in sync with painting.dart.
1624
enum ColorSpace {
1725
kSRGB,
@@ -35,6 +43,12 @@ class CanvasImage final : public RefCountedDartWrappable<CanvasImage> {
3543

3644
int height() { return image_ ? image_->height() : 0; }
3745

46+
static void decodeImageFromPixelsSync(Dart_Handle pixels_handle,
47+
uint32_t width,
48+
uint32_t height,
49+
int32_t pixel_format,
50+
Dart_Handle raw_image_handle);
51+
3852
Dart_Handle toByteData(int format, Dart_Handle callback);
3953

4054
void dispose();

0 commit comments

Comments
 (0)