Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
android_image_generator.cc
Go to the documentation of this file.
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
6
7#include <memory>
8#include <utility>
9
10#include <android/bitmap.h>
12
14
15#include "third_party/skia/include/codec/SkCodecAnimation.h"
16
17namespace flutter {
18
20static jmethodID g_decode_image_method = nullptr;
21
23
24AndroidImageGenerator::AndroidImageGenerator(sk_sp<SkData> data)
25 : data_(std::move(data)), image_info_(SkImageInfo::MakeUnknown(-1, -1)) {}
26
27const SkImageInfo& AndroidImageGenerator::GetInfo() {
28 header_decoded_latch_.Wait();
29 return image_info_;
30}
31
32unsigned int AndroidImageGenerator::GetFrameCount() const {
33 return 1;
34}
35
36unsigned int AndroidImageGenerator::GetPlayCount() const {
37 return 1;
38}
39
40const ImageGenerator::FrameInfo AndroidImageGenerator::GetFrameInfo(
41 unsigned int frame_index) {
42 return {.required_frame = std::nullopt,
43 .duration = 0,
44 .disposal_method = SkCodecAnimation::DisposalMethod::kKeep};
45}
46
47SkISize AndroidImageGenerator::GetScaledDimensions(float desired_scale) {
48 return GetInfo().dimensions();
49}
50
51bool AndroidImageGenerator::GetPixels(const SkImageInfo& info,
52 void* pixels,
53 size_t row_bytes,
54 unsigned int frame_index,
55 std::optional<unsigned int> prior_frame) {
56 fully_decoded_latch_.Wait();
57
58 if (!software_decoded_data_) {
59 return false;
60 }
61
62 if (kRGBA_8888_SkColorType != info.colorType()) {
63 return false;
64 }
65
66 switch (info.alphaType()) {
67 case kOpaque_SkAlphaType:
68 if (kOpaque_SkAlphaType != GetInfo().alphaType()) {
69 return false;
70 }
71 break;
72 case kPremul_SkAlphaType:
73 break;
74 default:
75 return false;
76 }
77
78 // TODO(bdero): Override `GetImage()` to use `SkImage::FromAHardwareBuffer` on
79 // API level 30+ once it's updated to do symbol lookups and not get
80 // preprocessed out in Skia. This will allow for avoiding this copy in
81 // cases where the result image doesn't need to be resized.
82 size_t pixels_size = info.computeByteSize(row_bytes);
83 if (pixels_size == SIZE_MAX || pixels_size < software_decoded_data_->size()) {
84 return false;
85 }
86 memcpy(pixels, software_decoded_data_->data(),
87 software_decoded_data_->size());
88 return true;
89}
90
91void AndroidImageGenerator::DecodeImage() {
92 DoDecodeImage();
93
94 header_decoded_latch_.Signal();
95 fully_decoded_latch_.Signal();
96}
97
98void AndroidImageGenerator::DoDecodeImage() {
99 FML_DCHECK(g_flutter_jni_class);
100 FML_DCHECK(g_decode_image_method);
101
102 // Call FlutterJNI.decodeImage
103
104 JNIEnv* env = fml::jni::AttachCurrentThread();
105
106 // This task is run on the IO thread. Create a frame to ensure that all
107 // local JNI references used here are freed.
108 fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
109
110 jobject direct_buffer =
111 env->NewDirectByteBuffer(const_cast<void*>(data_->data()), data_->size());
112
113 auto bitmap = std::make_unique<fml::jni::ScopedJavaGlobalRef<jobject>>(
114 env, env->CallStaticObjectMethod(g_flutter_jni_class->obj(),
115 g_decode_image_method, direct_buffer,
116 reinterpret_cast<jlong>(this)));
118
119 if (bitmap->is_null()) {
120 return;
121 }
122
123 AndroidBitmapInfo info;
124 [[maybe_unused]] int status;
125 if ((status = AndroidBitmap_getInfo(env, bitmap->obj(), &info)) < 0) {
126 FML_DLOG(ERROR) << "Failed to get bitmap info, status=" << status;
127 return;
128 }
129 FML_DCHECK(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
130
131 // Lock the android buffer in a shared pointer
132
133 void* pixel_lock;
134 if ((status = AndroidBitmap_lockPixels(env, bitmap->obj(), &pixel_lock)) <
135 0) {
136 FML_DLOG(ERROR) << "Failed to lock pixels, error=" << status;
137 return;
138 }
139
140 SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
144 AndroidBitmap_unlockPixels(env, bitmap->obj());
145 delete bitmap;
146 };
147
148 software_decoded_data_ = SkData::MakeWithProc(
149 pixel_lock, info.width * info.height * sizeof(uint32_t), on_release,
150 bitmap.release());
151}
152
153bool AndroidImageGenerator::Register(JNIEnv* env) {
155 env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
156 FML_DCHECK(!g_flutter_jni_class->is_null());
157
158 g_decode_image_method = env->GetStaticMethodID(
159 g_flutter_jni_class->obj(), "decodeImage",
160 "(Ljava/nio/ByteBuffer;J)Landroid/graphics/Bitmap;");
162
163 static const JNINativeMethod header_decoded_method = {
164 .name = "nativeImageHeaderCallback",
165 .signature = "(JII)V",
166 .fnPtr = reinterpret_cast<void*>(
167 &AndroidImageGenerator::NativeImageHeaderCallback),
168 };
169 if (env->RegisterNatives(g_flutter_jni_class->obj(), &header_decoded_method,
170 1) != 0) {
171 FML_LOG(ERROR)
172 << "Failed to register FlutterJNI.nativeImageHeaderCallback method";
173 return false;
174 }
175
176 return true;
177}
178
179std::shared_ptr<ImageGenerator> AndroidImageGenerator::MakeFromData(
180 sk_sp<SkData> data,
181 const fml::RefPtr<fml::TaskRunner>& task_runner) {
182 std::shared_ptr<AndroidImageGenerator> generator(
183 new AndroidImageGenerator(std::move(data)));
184
186 task_runner, [generator]() { generator->DecodeImage(); });
187
188 if (generator->IsValidImageData()) {
189 return generator;
190 }
191
192 return nullptr;
193}
194
195std::shared_ptr<AndroidImageGenerator> AndroidImageGenerator::MakeForTesting(
196 const SkImageInfo& header_info,
197 sk_sp<SkData> decoded_data) {
198 std::shared_ptr<AndroidImageGenerator> generator(
199 new AndroidImageGenerator(SkData::MakeEmpty()));
200 generator->image_info_ = header_info;
201 generator->software_decoded_data_ = std::move(decoded_data);
202 generator->header_decoded_latch_.Signal();
203 generator->fully_decoded_latch_.Signal();
204 return generator;
205}
206
207void AndroidImageGenerator::NativeImageHeaderCallback(JNIEnv* env,
208 jclass jcaller,
209 jlong generator_address,
210 int width,
211 int height) {
212 AndroidImageGenerator* generator =
213 reinterpret_cast<AndroidImageGenerator*>(generator_address);
214
215 generator->image_info_ = SkImageInfo::Make(
216 width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
217 generator->header_decoded_latch_.Signal();
218}
219
220bool AndroidImageGenerator::IsValidImageData() {
221 // The generator kicks off an IO task to decode everything, and calls to
222 // "GetInfo()" block until either the header has been decoded or decoding has
223 // failed, whichever is sooner. The decoder is initialized with a width and
224 // height of -1 and will update the dimensions if the image is able to be
225 // decoded.
226 return GetInfo().height() != -1;
227}
228
229} // namespace flutter
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
#define SIZE_MAX
Definition comments.cc:91
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
std::shared_ptr< SkBitmap > bitmap
static jmethodID g_decode_image_method
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
static fml::jni::ScopedJavaGlobalRef< jclass > * g_flutter_jni_class
JNIEnv * AttachCurrentThread()
Definition jni_util.cc:34
bool CheckException(JNIEnv *env)
Definition jni_util.cc:199
Definition ref_ptr.h:261
std::shared_ptr< ContextGLES > context
int32_t height
int32_t width
Info about a single frame in the context of a multi-frame image, useful for animation and blending.
std::optional< unsigned int > required_frame