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_external_texture_gl.h"
6
7 #include <GLES/glext.h>
8
9 #include "flutter/shell/platform/android/platform_view_android_jni.h"
10 #include "third_party/skia/include/gpu/GrBackendSurface.h"
11
12 namespace flutter {
13
AndroidExternalTextureGL(int64_t id,const fml::jni::JavaObjectWeakGlobalRef & surfaceTexture)14 AndroidExternalTextureGL::AndroidExternalTextureGL(
15 int64_t id,
16 const fml::jni::JavaObjectWeakGlobalRef& surfaceTexture)
17 : Texture(id), surface_texture_(surfaceTexture), transform(SkMatrix::I()) {}
18
~AndroidExternalTextureGL()19 AndroidExternalTextureGL::~AndroidExternalTextureGL() {
20 if (state_ == AttachmentState::attached) {
21 glDeleteTextures(1, &texture_name_);
22 }
23 }
24
OnGrContextCreated()25 void AndroidExternalTextureGL::OnGrContextCreated() {
26 state_ = AttachmentState::uninitialized;
27 }
28
MarkNewFrameAvailable()29 void AndroidExternalTextureGL::MarkNewFrameAvailable() {
30 new_frame_ready_ = true;
31 }
32
Paint(SkCanvas & canvas,const SkRect & bounds,bool freeze,GrContext * context)33 void AndroidExternalTextureGL::Paint(SkCanvas& canvas,
34 const SkRect& bounds,
35 bool freeze,
36 GrContext* context) {
37 if (state_ == AttachmentState::detached) {
38 return;
39 }
40 if (state_ == AttachmentState::uninitialized) {
41 glGenTextures(1, &texture_name_);
42 Attach(static_cast<jint>(texture_name_));
43 state_ = AttachmentState::attached;
44 }
45 if (!freeze && new_frame_ready_) {
46 Update();
47 new_frame_ready_ = false;
48 }
49 GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_,
50 GL_RGBA8_OES};
51 GrBackendTexture backendTexture(1, 1, GrMipMapped::kNo, textureInfo);
52 sk_sp<SkImage> image = SkImage::MakeFromTexture(
53 canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin,
54 kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
55 if (image) {
56 SkAutoCanvasRestore autoRestore(&canvas, true);
57 canvas.translate(bounds.x(), bounds.y());
58 canvas.scale(bounds.width(), bounds.height());
59 if (!transform.isIdentity()) {
60 SkMatrix transformAroundCenter(transform);
61
62 transformAroundCenter.preTranslate(-0.5, -0.5);
63 transformAroundCenter.postScale(1, -1);
64 transformAroundCenter.postTranslate(0.5, 0.5);
65 canvas.concat(transformAroundCenter);
66 }
67 canvas.drawImage(image, 0, 0);
68 }
69 }
70
71 // The bounds we set for the canvas are post composition.
72 // To fill the canvas we need to ensure that the transformation matrix
73 // on the `SurfaceTexture` will be scaled to fill. We rescale and preseve
74 // the scaled aspect ratio.
ScaleToFill(float scaleX,float scaleY)75 SkSize ScaleToFill(float scaleX, float scaleY) {
76 const double epsilon = std::numeric_limits<double>::epsilon();
77 // scaleY is negative.
78 const double minScale = fmin(scaleX, fabs(scaleY));
79 const double rescale = 1.0f / (minScale + epsilon);
80 return SkSize::Make(scaleX * rescale, scaleY * rescale);
81 }
82
UpdateTransform()83 void AndroidExternalTextureGL::UpdateTransform() {
84 JNIEnv* env = fml::jni::AttachCurrentThread();
85 fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
86 surface_texture_.get(env);
87 fml::jni::ScopedJavaLocalRef<jfloatArray> transformMatrix(
88 env, env->NewFloatArray(16));
89 SurfaceTextureGetTransformMatrix(env, surfaceTexture.obj(),
90 transformMatrix.obj());
91 float* m = env->GetFloatArrayElements(transformMatrix.obj(), nullptr);
92 float scaleX = m[0], scaleY = m[5];
93 const SkSize scaled = ScaleToFill(scaleX, scaleY);
94 SkScalar matrix3[] = {
95 scaled.fWidth, m[1], m[2], //
96 m[4], scaled.fHeight, m[6], //
97 m[8], m[9], m[10], //
98 };
99 env->ReleaseFloatArrayElements(transformMatrix.obj(), m, JNI_ABORT);
100 transform.set9(matrix3);
101 }
102
OnGrContextDestroyed()103 void AndroidExternalTextureGL::OnGrContextDestroyed() {
104 if (state_ == AttachmentState::attached) {
105 Detach();
106 }
107 state_ = AttachmentState::detached;
108 }
109
Attach(jint textureName)110 void AndroidExternalTextureGL::Attach(jint textureName) {
111 JNIEnv* env = fml::jni::AttachCurrentThread();
112 fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
113 surface_texture_.get(env);
114 if (!surfaceTexture.is_null()) {
115 SurfaceTextureAttachToGLContext(env, surfaceTexture.obj(), textureName);
116 }
117 }
118
Update()119 void AndroidExternalTextureGL::Update() {
120 JNIEnv* env = fml::jni::AttachCurrentThread();
121 fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
122 surface_texture_.get(env);
123 if (!surfaceTexture.is_null()) {
124 SurfaceTextureUpdateTexImage(env, surfaceTexture.obj());
125 UpdateTransform();
126 }
127 }
128
Detach()129 void AndroidExternalTextureGL::Detach() {
130 JNIEnv* env = fml::jni::AttachCurrentThread();
131 fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
132 surface_texture_.get(env);
133 if (!surfaceTexture.is_null()) {
134 SurfaceTextureDetachFromGLContext(env, surfaceTexture.obj());
135 }
136 }
137
138 } // namespace flutter
139