diff --git a/sky/shell/platform/android/io/flutter/view/FlutterView.java b/sky/shell/platform/android/io/flutter/view/FlutterView.java index b1ab4db6fcee1d28da8e62c85bdef7f698ba172a..0924b55d8cd9e9cbccf86a8b7580c8a19d1a7fa1 100644 --- a/sky/shell/platform/android/io/flutter/view/FlutterView.java +++ b/sky/shell/platform/android/io/flutter/view/FlutterView.java @@ -12,6 +12,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.opengl.Matrix; +import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; @@ -501,6 +502,11 @@ public class FlutterView extends SurfaceView } } + /** Return the most recent frame as a bitmap. */ + public Bitmap getBitmap() { + return nativeGetBitmap(mNativePlatformView); + } + private static native long nativeAttach(int skyEngineHandle, FlutterView view); private static native int nativeGetObservatoryPort(); @@ -508,6 +514,7 @@ public class FlutterView extends SurfaceView private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); // ACCESSIBILITY diff --git a/sky/shell/platform/android/platform_view_android.cc b/sky/shell/platform/android/platform_view_android.cc index fcbfcc1ce171def6a19428476e7bebe22ece012d..41e810f88bcb8f9f59ca187be12f2460cf5b501e 100644 --- a/sky/shell/platform/android/platform_view_android.cc +++ b/sky/shell/platform/android/platform_view_android.cc @@ -18,9 +18,11 @@ #include "base/location.h" #include "base/trace_event/trace_event.h" #include "flutter/common/threads.h" +#include "flutter/flow/compositor_context.h" #include "flutter/runtime/dart_service_isolate.h" #include "flutter/sky/shell/shell.h" #include "jni/FlutterView_jni.h" +#include "third_party/skia/include/core/SkSurface.h" namespace sky { namespace shell { @@ -501,5 +503,107 @@ void PlatformViewAndroid::RunFromSource(const std::string& main, base::android::DetachFromVM(); } +base::android::ScopedJavaLocalRef PlatformViewAndroid::GetBitmap( + JNIEnv* env, jobject obj) { + // Render the last frame to an array of pixels on the GPU thread. + // The pixels will be returned as a global JNI reference to an int array. + ftl::AutoResetWaitableEvent latch; + jobject pixels_ref = nullptr; + SkISize frame_size; + blink::Threads::Gpu()->PostTask([this, &latch, &pixels_ref, &frame_size]() { + GetBitmapGpuTask(&latch, &pixels_ref, &frame_size); + }); + + latch.Wait(); + + // Convert the pixel array to an Android bitmap. + if (pixels_ref == nullptr) + return base::android::ScopedJavaLocalRef(); + + base::android::ScopedJavaGlobalRef pixels(env, pixels_ref); + + jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); + FTL_CHECK(bitmap_class); + + jmethodID create_bitmap = env->GetStaticMethodID( + bitmap_class, "createBitmap", + "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + FTL_CHECK(create_bitmap); + + jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); + FTL_CHECK(bitmap_config_class); + + jmethodID bitmap_config_value_of = env->GetStaticMethodID( + bitmap_config_class, "valueOf", + "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); + FTL_CHECK(bitmap_config_value_of); + + jstring argb = env->NewStringUTF("ARGB_8888"); + FTL_CHECK(argb); + + jobject bitmap_config = env->CallStaticObjectMethod( + bitmap_config_class, bitmap_config_value_of, argb); + FTL_CHECK(bitmap_config); + + jobject bitmap = env->CallStaticObjectMethod( + bitmap_class, create_bitmap, + pixels.obj(), frame_size.width(), frame_size.height(), bitmap_config); + + return base::android::ScopedJavaLocalRef(env, bitmap); +} + +void PlatformViewAndroid::GetBitmapGpuTask( + ftl::AutoResetWaitableEvent* latch, + jobject* pixels_out, + SkISize* size_out) { + flow::LayerTree* layer_tree = rasterizer_->GetLastLayerTree(); + if (layer_tree == nullptr) + return; + + JNIEnv* env = base::android::AttachCurrentThread(); + FTL_CHECK(env); + + const SkISize& frame_size = layer_tree->frame_size(); + jsize pixels_size = frame_size.width() * frame_size.height(); + jintArray pixels_array = env->NewIntArray(pixels_size); + FTL_CHECK(pixels_array); + + jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); + FTL_CHECK(pixels); + + SkImageInfo image_info = SkImageInfo::Make( + frame_size.width(), frame_size.height(), kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + sk_sp surface = SkSurface::MakeRasterDirect( + image_info, pixels, frame_size.width() * sizeof(jint)); + + flow::CompositorContext compositor_context; + SkCanvas* canvas = surface->getCanvas(); + flow::CompositorContext::ScopedFrame frame = + compositor_context.AcquireFrame(nullptr, *canvas, false); + + canvas->clear(SK_ColorBLACK); + layer_tree->Raster(frame); + canvas->flush(); + + // Our configuration of Skia does not support rendering to the + // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. + // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). + for (int i = 0; i < pixels_size; i++) { + uint8_t* bytes = reinterpret_cast(pixels + i); + std::swap(bytes[0], bytes[2]); + } + + env->ReleaseIntArrayElements(pixels_array, pixels, 0); + + *pixels_out = env->NewGlobalRef(pixels_array); + *size_out = frame_size; + + base::android::DetachFromVM(); + + latch->Signal(); +} + } // namespace shell } // namespace sky diff --git a/sky/shell/platform/android/platform_view_android.h b/sky/shell/platform/android/platform_view_android.h index 397b7dd08a69f309e8c774c7425916161dfd0334..98f93e882d7e6128dc4fed490ed0e5317ef23db1 100644 --- a/sky/shell/platform/android/platform_view_android.h +++ b/sky/shell/platform/android/platform_view_android.h @@ -8,6 +8,7 @@ #include "base/android/jni_android.h" #include "base/android/jni_weak_ref.h" #include "lib/ftl/memory/weak_ptr.h" +#include "lib/ftl/synchronization/waitable_event.h" #include "flutter/sky/shell/platform_view.h" namespace sky { @@ -32,6 +33,10 @@ class PlatformViewAndroid : public PlatformView { // Called from Java void SurfaceDestroyed(JNIEnv* env, jobject obj); + // Called from Java + base::android::ScopedJavaLocalRef GetBitmap(JNIEnv* env, + jobject obj); + // sky::shell::PlatformView override ftl::WeakPtr GetWeakViewPtr() override; @@ -64,6 +69,10 @@ class PlatformViewAndroid : public PlatformView { private: void ReleaseSurface(); + void GetBitmapGpuTask(ftl::AutoResetWaitableEvent* latch, + jobject* pixels_out, + SkISize* size_out); + std::unique_ptr context_; ftl::WeakPtrFactory weak_factory_; JavaObjectWeakGlobalRef flutter_view_;