Flutter Engine
The Flutter Engine
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
5#include "flutter/shell/platform/android/android_image_generator.h"
6
7#include <memory>
8#include <utility>
9
10#include <android/bitmap.h>
12
13#include "flutter/fml/platform/android/jni_util.h"
14
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,
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()) {
68 if (kOpaque_SkAlphaType != GetInfo().alphaType()) {
69 return false;
70 }
71 break;
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 memcpy(pixels, software_decoded_data_->data(),
83 software_decoded_data_->size());
84 return true;
85}
86
87void AndroidImageGenerator::DecodeImage() {
88 DoDecodeImage();
89
90 header_decoded_latch_.Signal();
91 fully_decoded_latch_.Signal();
92}
93
94void AndroidImageGenerator::DoDecodeImage() {
97
98 // Call FlutterJNI.decodeImage
99
101
102 // This task is run on the IO thread. Create a frame to ensure that all
103 // local JNI references used here are freed.
104 fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
105
106 jobject direct_buffer =
107 env->NewDirectByteBuffer(const_cast<void*>(data_->data()), data_->size());
108
109 auto bitmap = std::make_unique<fml::jni::ScopedJavaGlobalRef<jobject>>(
110 env, env->CallStaticObjectMethod(g_flutter_jni_class->obj(),
111 g_decode_image_method, direct_buffer,
112 reinterpret_cast<jlong>(this)));
114
115 if (bitmap->is_null()) {
116 return;
117 }
118
119 AndroidBitmapInfo info;
120 [[maybe_unused]] int status;
121 if ((status = AndroidBitmap_getInfo(env, bitmap->obj(), &info)) < 0) {
122 FML_DLOG(ERROR) << "Failed to get bitmap info, status=" << status;
123 return;
124 }
125 FML_DCHECK(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
126
127 // Lock the android buffer in a shared pointer
128
129 void* pixel_lock;
130 if ((status = AndroidBitmap_lockPixels(env, bitmap->obj(), &pixel_lock)) <
131 0) {
132 FML_DLOG(ERROR) << "Failed to lock pixels, error=" << status;
133 return;
134 }
135
136 SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void {
138 reinterpret_cast<fml::jni::ScopedJavaGlobalRef<jobject>*>(context);
140 AndroidBitmap_unlockPixels(env, bitmap->obj());
141 delete bitmap;
142 };
143
144 software_decoded_data_ = SkData::MakeWithProc(
145 pixel_lock, info.width * info.height * sizeof(uint32_t), on_release,
146 bitmap.release());
147}
148
151 env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
152 FML_DCHECK(!g_flutter_jni_class->is_null());
153
154 g_decode_image_method = env->GetStaticMethodID(
155 g_flutter_jni_class->obj(), "decodeImage",
156 "(Ljava/nio/ByteBuffer;J)Landroid/graphics/Bitmap;");
158
159 static const JNINativeMethod header_decoded_method = {
160 .name = "nativeImageHeaderCallback",
161 .signature = "(JII)V",
162 .fnPtr = reinterpret_cast<void*>(
163 &AndroidImageGenerator::NativeImageHeaderCallback),
164 };
165 if (env->RegisterNatives(g_flutter_jni_class->obj(), &header_decoded_method,
166 1) != 0) {
168 << "Failed to register FlutterJNI.nativeImageHeaderCallback method";
169 return false;
170 }
171
172 return true;
173}
174
175std::shared_ptr<ImageGenerator> AndroidImageGenerator::MakeFromData(
177 const fml::RefPtr<fml::TaskRunner>& task_runner) {
178 std::shared_ptr<AndroidImageGenerator> generator(
179 new AndroidImageGenerator(std::move(data)));
180
182 task_runner, [generator]() { generator->DecodeImage(); });
183
184 if (generator->IsValidImageData()) {
185 return generator;
186 }
187
188 return nullptr;
189}
190
191void AndroidImageGenerator::NativeImageHeaderCallback(JNIEnv* env,
192 jclass jcaller,
193 jlong generator_address,
194 int width,
195 int height) {
196 AndroidImageGenerator* generator =
197 reinterpret_cast<AndroidImageGenerator*>(generator_address);
198
199 generator->image_info_ = SkImageInfo::Make(
201 generator->header_decoded_latch_.Signal();
202}
203
204bool AndroidImageGenerator::IsValidImageData() {
205 // The generator kicks off an IO task to decode everything, and calls to
206 // "GetInfo()" block until either the header has been decoded or decoding has
207 // failed, whichever is sooner. The decoder is initialized with a width and
208 // height of -1 and will update the dimensions if the image is able to be
209 // decoded.
210 return GetInfo().height() != -1;
211}
212
213} // namespace flutter
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
void(* ReleaseProc)(const void *ptr, void *context)
Definition: SkData.h:78
static sk_sp< SkData > MakeWithProc(const void *ptr, size_t length, ReleaseProc proc, void *ctx)
Definition: SkData.cpp:128
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
Definition: task_runner.cc:55
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
Definition: bitmap.py:1
Definition: __init__.py:1
static jmethodID g_decode_image_method
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: switches.h:41
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:256
int32_t height
int32_t width
Definition: SkSize.h:16
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
Info about a single frame in the context of a multi-frame image, useful for animation and blending.
std::optional< unsigned int > required_frame
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define ERROR(message)
Definition: elf_loader.cc:260