Skip to content

Commit 1ba3100

Browse files
authored
support fallback codecs on Windows, macOS, iOS (flutter#19989)
1 parent b3ca41a commit 1ba3100

File tree

7 files changed

+118
-23
lines changed

7 files changed

+118
-23
lines changed

lib/ui/painting.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ part of dart.ui;
2020

2121
// Update this list when changing the list of supported codecs.
2222
/// {@template flutter.dart:ui.imageFormats}
23-
/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP
23+
/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional
24+
/// formats may be supported by the underlying platform. Flutter will
25+
/// attempt to call platform API to decode unrecognized formats, and if the
26+
/// platform API supports decoding the image Flutter will be able to render it.
2427
/// {@endtemplate}
2528
2629
bool _rectIsValid(Rect rect) {
@@ -1557,14 +1560,17 @@ enum PixelFormat {
15571560

15581561
/// Opaque handle to raw decoded image data (pixels).
15591562
///
1560-
/// To obtain an [Image] object, use [instantiateImageCodec].
1563+
/// To obtain an [Image] object, use the [ImageDescriptor] API.
15611564
///
15621565
/// To draw an [Image], use one of the methods on the [Canvas] class, such as
15631566
/// [Canvas.drawImage].
15641567
///
15651568
/// See also:
15661569
///
15671570
/// * [Image](https://api.flutter.dev/flutter/widgets/Image-class.html), the class in the [widgets] library.
1571+
/// * [ImageDescriptor], which allows reading information about the image and
1572+
/// creating a codec to decode it.
1573+
/// * [instantiateImageCodec], a utility method that wraps [ImageDescriptor].
15681574
///
15691575
@pragma('vm:entry-point')
15701576
class Image extends NativeFieldWrapperClass2 {
@@ -1678,6 +1684,11 @@ class Codec extends NativeFieldWrapperClass2 {
16781684

16791685
/// Instantiates an image [Codec].
16801686
///
1687+
/// This method is a convenience wrapper around the [ImageDescriptor] API, and
1688+
/// using [ImageDescriptor] directly is preferred since it allows the caller to
1689+
/// make better determinations about how and whether to use the `targetWidth`
1690+
/// and `targetHeight` parameters.
1691+
///
16811692
/// The `list` parameter is the binary image data (e.g a PNG or GIF binary data).
16821693
/// The data can be for either static or animated images. The following image
16831694
/// formats are supported: {@macro flutter.dart:ui.imageFormats}

lib/ui/painting/image_decoder.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ sk_sp<SkImage> ImageFromCompressedData(fml::RefPtr<ImageDescriptor> descriptor,
105105

106106
if (!descriptor->should_resize(target_width, target_height)) {
107107
// No resizing requested. Just decode & rasterize the image.
108-
return SkImage::MakeFromEncoded(descriptor->data())->makeRasterImage();
108+
return descriptor->image()->makeRasterImage();
109109
}
110110

111111
const SkISize source_dimensions = descriptor->image_info().dimensions();
@@ -149,7 +149,7 @@ sk_sp<SkImage> ImageFromCompressedData(fml::RefPtr<ImageDescriptor> descriptor,
149149
}
150150
}
151151

152-
auto image = SkImage::MakeFromEncoded(descriptor->data());
152+
auto image = descriptor->image();
153153
if (!image) {
154154
return nullptr;
155155
}

lib/ui/painting/image_decoder_unittests.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) {
165165
ASSERT_FALSE(data);
166166

167167
fml::RefPtr<ImageDescriptor> image_descriptor =
168-
fml::MakeRefCounted<ImageDescriptor>(std::move(data), nullptr);
168+
fml::MakeRefCounted<ImageDescriptor>(std::move(data),
169+
std::unique_ptr<SkCodec>(nullptr));
169170

170171
ImageDecoder::ImageResult callback = [&](SkiaGPUObject<SkImage> image) {
171172
ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread());

lib/ui/painting/image_descriptor.cc

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,30 @@
44

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

7+
#include "flutter/fml/build_config.h"
78
#include "flutter/fml/logging.h"
89
#include "flutter/fml/trace_event.h"
910
#include "flutter/lib/ui/painting/codec.h"
1011
#include "flutter/lib/ui/painting/image_decoder.h"
1112
#include "flutter/lib/ui/painting/multi_frame_codec.h"
1213
#include "flutter/lib/ui/painting/single_frame_codec.h"
1314
#include "flutter/lib/ui/ui_dart_state.h"
14-
#include "third_party/skia/src/codec/SkCodecImageGenerator.h"
1515
#include "third_party/tonic/dart_binding_macros.h"
1616
#include "third_party/tonic/logging/dart_invoke.h"
1717

18+
#ifdef OS_MACOSX
19+
#include "third_party/skia/include/ports/SkImageGeneratorCG.h"
20+
#define PLATFORM_IMAGE_GENERATOR(data) \
21+
SkImageGeneratorCG::MakeFromEncodedCG(data)
22+
#elif OS_WIN
23+
#include "third_party/skia/include/ports/SkImageGeneratorWIC.h"
24+
#define PLATFORM_IMAGE_GENERATOR(data) \
25+
SkImageGeneratorWIC::MakeFromEncodedWIC(data)
26+
#else
27+
#define PLATFORM_IMAGE_GENERATOR(data) \
28+
std::unique_ptr<SkImageGenerator>(nullptr)
29+
#endif
30+
1831
namespace flutter {
1932

2033
IMPLEMENT_WRAPPERTYPEINFO(ui, ImageDescriptor);
@@ -35,17 +48,21 @@ void ImageDescriptor::RegisterNatives(tonic::DartLibraryNatives* natives) {
3548
}
3649

3750
const SkImageInfo ImageDescriptor::CreateImageInfo() const {
38-
if (!generator_) {
39-
return SkImageInfo::MakeUnknown();
51+
if (generator_) {
52+
return generator_->getInfo();
53+
}
54+
if (platform_image_generator_) {
55+
return platform_image_generator_->getInfo();
4056
}
41-
return generator_->getInfo();
57+
return SkImageInfo::MakeUnknown();
4258
}
4359

4460
ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
4561
const SkImageInfo& image_info,
4662
std::optional<size_t> row_bytes)
4763
: buffer_(std::move(buffer)),
4864
generator_(nullptr),
65+
platform_image_generator_(nullptr),
4966
image_info_(std::move(image_info)),
5067
row_bytes_(row_bytes) {}
5168

@@ -56,6 +73,15 @@ ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
5673
static_cast<SkCodecImageGenerator*>(
5774
SkCodecImageGenerator::MakeFromCodec(std::move(codec))
5875
.release()))),
76+
platform_image_generator_(nullptr),
77+
image_info_(CreateImageInfo()),
78+
row_bytes_(std::nullopt) {}
79+
80+
ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
81+
std::unique_ptr<SkImageGenerator> generator)
82+
: buffer_(std::move(buffer)),
83+
generator_(nullptr),
84+
platform_image_generator_(std::move(generator)),
5985
image_info_(CreateImageInfo()),
6086
row_bytes_(std::nullopt) {}
6187

@@ -77,15 +103,28 @@ void ImageDescriptor::initEncoded(Dart_NativeArguments args) {
77103
return;
78104
}
79105

106+
// This call will succeed if Skia has a built-in codec for this.
107+
// If it fails, we will check if the platform knows how to decode this image.
80108
std::unique_ptr<SkCodec> codec =
81109
SkCodec::MakeFromData(immutable_buffer->data());
110+
fml::RefPtr<ImageDescriptor> descriptor;
82111
if (!codec) {
83-
Dart_SetReturnValue(args, tonic::ToDart("Invalid image data"));
84-
return;
112+
std::unique_ptr<SkImageGenerator> generator =
113+
PLATFORM_IMAGE_GENERATOR(immutable_buffer->data());
114+
if (!generator) {
115+
// We don't have a Skia codec for this image, and the platform doesn't
116+
// know how to decode it.
117+
Dart_SetReturnValue(args, tonic::ToDart("Invalid image data"));
118+
return;
119+
}
120+
descriptor = fml::MakeRefCounted<ImageDescriptor>(immutable_buffer->data(),
121+
std::move(generator));
122+
} else {
123+
descriptor = fml::MakeRefCounted<ImageDescriptor>(immutable_buffer->data(),
124+
std::move(codec));
85125
}
86126

87-
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
88-
immutable_buffer->data(), std::move(codec));
127+
FML_DCHECK(descriptor);
89128

90129
descriptor->AssociateWithDartWrapper(descriptor_handle);
91130
tonic::DartInvoke(callback_handle, {Dart_TypeVoid()});
@@ -128,4 +167,31 @@ void ImageDescriptor::instantiateCodec(Dart_Handle codec_handle,
128167
}
129168
ui_codec->AssociateWithDartWrapper(codec_handle);
130169
}
170+
171+
sk_sp<SkImage> ImageDescriptor::image() const {
172+
SkBitmap bitmap;
173+
if (!bitmap.tryAllocPixels(image_info_)) {
174+
FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
175+
<< image_info_.computeMinByteSize() << "B";
176+
return nullptr;
177+
}
178+
179+
const auto& pixmap = bitmap.pixmap();
180+
if (!get_pixels(pixmap)) {
181+
FML_LOG(ERROR) << "Failed to get pixels for image.";
182+
return nullptr;
183+
}
184+
bitmap.setImmutable();
185+
return SkImage::MakeFromBitmap(bitmap);
186+
}
187+
188+
bool ImageDescriptor::get_pixels(const SkPixmap& pixmap) const {
189+
if (generator_) {
190+
return generator_->getPixels(pixmap.info(), pixmap.writable_addr(),
191+
pixmap.rowBytes());
192+
}
193+
FML_DCHECK(platform_image_generator_);
194+
return platform_image_generator_->getPixels(pixmap);
195+
}
196+
131197
} // namespace flutter

lib/ui/painting/image_descriptor.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "flutter/lib/ui/dart_wrapper.h"
1414
#include "flutter/lib/ui/painting/immutable_buffer.h"
1515
#include "third_party/skia/include/codec/SkCodec.h"
16+
#include "third_party/skia/include/core/SkImageGenerator.h"
1617
#include "third_party/skia/include/core/SkImageInfo.h"
1718
#include "third_party/skia/src/codec/SkCodecImageGenerator.h"
1819
#include "third_party/tonic/dart_library_natives.h"
@@ -81,31 +82,31 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
8182
/// The underlying buffer for this image.
8283
sk_sp<SkData> data() const { return buffer_; }
8384

85+
sk_sp<SkImage> image() const;
86+
8487
/// Whether this descriptor represents compressed (encoded) data or not.
85-
bool is_compressed() const { return !!generator_; }
88+
bool is_compressed() const { return generator_ || platform_image_generator_; }
8689

8790
/// The orientation corrected image info for this image.
8891
const SkImageInfo& image_info() const { return image_info_; }
8992

93+
/// Gets the scaled dimensions of this image, if backed by a codec that can
94+
/// perform efficient subpixel scaling.
9095
SkISize get_scaled_dimensions(float scale) {
91-
if (!generator_) {
92-
FML_DCHECK(false);
93-
return image_info_.dimensions();
96+
if (generator_) {
97+
return generator_->getScaledDimensions(scale);
9498
}
95-
return generator_->getScaledDimensions(scale);
99+
return image_info_.dimensions();
96100
}
97101

98102
/// Gets pixels for this image transformed based on the EXIF orientation tag,
99103
/// if applicable.
100-
bool get_pixels(const SkPixmap& pixmap) const {
101-
FML_DCHECK(generator_);
102-
return generator_->getPixels(pixmap.info(), pixmap.writable_addr(),
103-
pixmap.rowBytes());
104-
}
104+
bool get_pixels(const SkPixmap& pixmap) const;
105105

106106
void dispose() {
107107
ClearDartWrapper();
108108
generator_.reset();
109+
platform_image_generator_.reset();
109110
}
110111

111112
size_t GetAllocationSize() const override {
@@ -119,9 +120,12 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
119120
const SkImageInfo& image_info,
120121
std::optional<size_t> row_bytes);
121122
ImageDescriptor(sk_sp<SkData> buffer, std::unique_ptr<SkCodec> codec);
123+
ImageDescriptor(sk_sp<SkData> buffer,
124+
std::unique_ptr<SkImageGenerator> generator);
122125

123126
sk_sp<SkData> buffer_;
124127
std::shared_ptr<SkCodecImageGenerator> generator_;
128+
std::unique_ptr<SkImageGenerator> platform_image_generator_;
125129
const SkImageInfo image_info_;
126130
std::optional<size_t> row_bytes_;
127131

testing/dart/image_descriptor_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ void main() {
7070
final Codec codec = await descriptor.instantiateCodec();
7171
expect(codec.frameCount, 1);
7272
});
73+
74+
test('HEIC image', () async {
75+
final Uint8List bytes = await readFile('grill_chicken.heic');
76+
final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(bytes);
77+
final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer);
78+
79+
expect(descriptor.width, 300);
80+
expect(descriptor.height, 400);
81+
expect(descriptor.bytesPerPixel, 4);
82+
83+
final Codec codec = await descriptor.instantiateCodec();
84+
expect(codec.frameCount, 1);
85+
}, skip: !(Platform.isIOS || Platform.isMacOS || Platform.isWindows));
7386
}
7487

7588
Future<Uint8List> readFile(String fileName, ) async {
61 KB
Binary file not shown.

0 commit comments

Comments
 (0)