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#include "flutter/lib/ui/painting/image_descriptor.h"
6
7#include "flutter/fml/build_config.h"
8#include "flutter/fml/logging.h"
9#include "flutter/fml/trace_event.h"
10#include "flutter/lib/ui/painting/codec.h"
11#include "flutter/lib/ui/painting/image_decoder.h"
12#include "flutter/lib/ui/painting/multi_frame_codec.h"
13#include "flutter/lib/ui/painting/single_frame_codec.h"
14#include "flutter/lib/ui/ui_dart_state.h"
15#include "third_party/tonic/dart_binding_macros.h"
16#include "third_party/tonic/logging/dart_invoke.h"
17
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
31namespace flutter {
32
33IMPLEMENT_WRAPPERTYPEINFO(ui, ImageDescriptor);
34
35#define FOR_EACH_BINDING(V) \
36 V(ImageDescriptor, initRaw) \
37 V(ImageDescriptor, instantiateCodec) \
38 V(ImageDescriptor, width) \
39 V(ImageDescriptor, height) \
40 V(ImageDescriptor, bytesPerPixel)
41
42FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
43
44void ImageDescriptor::RegisterNatives(tonic::DartLibraryNatives* natives) {
45 natives->Register(
46 {{"ImageDescriptor_initEncoded", ImageDescriptor::initEncoded, 3, true},
47 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
48}
49
50const SkImageInfo ImageDescriptor::CreateImageInfo() const {
51 if (generator_) {
52 return generator_->getInfo();
53 }
54 if (platform_image_generator_) {
55 return platform_image_generator_->getInfo();
56 }
57 return SkImageInfo::MakeUnknown();
58}
59
60ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
61 const SkImageInfo& image_info,
62 std::optional<size_t> row_bytes)
63 : buffer_(std::move(buffer)),
64 generator_(nullptr),
65 platform_image_generator_(nullptr),
66 image_info_(std::move(image_info)),
67 row_bytes_(row_bytes) {}
68
69ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
70 std::unique_ptr<SkCodec> codec)
71 : buffer_(std::move(buffer)),
72 generator_(std::shared_ptr<SkCodecImageGenerator>(
73 static_cast<SkCodecImageGenerator*>(
74 SkCodecImageGenerator::MakeFromCodec(std::move(codec))
75 .release()))),
76 platform_image_generator_(nullptr),
77 image_info_(CreateImageInfo()),
78 row_bytes_(std::nullopt) {}
79
80ImageDescriptor::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)),
85 image_info_(CreateImageInfo()),
86 row_bytes_(std::nullopt) {}
87
88void ImageDescriptor::initEncoded(Dart_NativeArguments args) {
89 Dart_Handle callback_handle = Dart_GetNativeArgument(args, 2);
90 if (!Dart_IsClosure(callback_handle)) {
91 Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function"));
92 return;
93 }
94
95 Dart_Handle descriptor_handle = Dart_GetNativeArgument(args, 0);
96 ImmutableBuffer* immutable_buffer =
97 tonic::DartConverter<ImmutableBuffer*>::FromDart(
98 Dart_GetNativeArgument(args, 1));
99
100 if (!immutable_buffer) {
101 Dart_SetReturnValue(args,
102 tonic::ToDart("Buffer parameter must not be null"));
103 return;
104 }
105
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.
108 std::unique_ptr<SkCodec> codec =
109 SkCodec::MakeFromData(immutable_buffer->data());
110 fml::RefPtr<ImageDescriptor> descriptor;
111 if (!codec) {
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));
125 }
126
127 FML_DCHECK(descriptor);
128
129 descriptor->AssociateWithDartWrapper(descriptor_handle);
130 tonic::DartInvoke(callback_handle, {Dart_TypeVoid()});
131}
132
133void ImageDescriptor::initRaw(Dart_Handle descriptor_handle,
134 fml::RefPtr<ImmutableBuffer> data,
135 int width,
136 int height,
137 int row_bytes,
138 PixelFormat pixel_format) {
139 SkColorType color_type = kUnknown_SkColorType;
140 switch (pixel_format) {
141 case PixelFormat::kRGBA8888:
142 color_type = kRGBA_8888_SkColorType;
143 break;
144 case PixelFormat::kBGRA8888:
145 color_type = kBGRA_8888_SkColorType;
146 break;
147 }
148 FML_DCHECK(color_type != kUnknown_SkColorType);
149 auto image_info =
150 SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
151 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
152 data->data(), std::move(image_info),
153 row_bytes == -1 ? std::nullopt : std::optional<size_t>(row_bytes));
154 descriptor->AssociateWithDartWrapper(descriptor_handle);
155}
156
157void ImageDescriptor::instantiateCodec(Dart_Handle codec_handle,
158 int target_width,
159 int target_height) {
160 fml::RefPtr<Codec> ui_codec;
161 if (!generator_ || generator_->getFrameCount() == 1) {
162 ui_codec = fml::MakeRefCounted<SingleFrameCodec>(
163 static_cast<fml::RefPtr<ImageDescriptor>>(this), target_width,
164 target_height);
165 } else {
166 ui_codec = fml::MakeRefCounted<MultiFrameCodec>(generator_);
167 }
168 ui_codec->AssociateWithDartWrapper(codec_handle);
169}
170
171sk_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
188bool 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
197} // namespace flutter
198