mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
3aff2e3556
* [android] External textures must be rescaled to fill the canvas - After composition, we have a tight box that we need to fill. - Video decoders typically operate using output buffers that are a whole number of blocks (vertically and horizontally). This fixes https://github.com/flutter/flutter/issues/34641 where the decoded dimensions are rounded up. - The cropping information for the current frame arrives in the form of `SurfaceTexture.getTransformMatrix`. When we apply this transform we need to account for the fact that we have already composited. This means that in cases where the scaleX, scaleY are less than 1, we need to rescale the image. We do this while preserving the aspect ratio.
139 lines
4.7 KiB
C++
139 lines
4.7 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "flutter/shell/platform/android/android_external_texture_gl.h"
|
|
|
|
#include <GLES/glext.h>
|
|
|
|
#include "flutter/shell/platform/android/platform_view_android_jni.h"
|
|
#include "third_party/skia/include/gpu/GrBackendSurface.h"
|
|
|
|
namespace flutter {
|
|
|
|
AndroidExternalTextureGL::AndroidExternalTextureGL(
|
|
int64_t id,
|
|
const fml::jni::JavaObjectWeakGlobalRef& surfaceTexture)
|
|
: Texture(id), surface_texture_(surfaceTexture), transform(SkMatrix::I()) {}
|
|
|
|
AndroidExternalTextureGL::~AndroidExternalTextureGL() {
|
|
if (state_ == AttachmentState::attached) {
|
|
glDeleteTextures(1, &texture_name_);
|
|
}
|
|
}
|
|
|
|
void AndroidExternalTextureGL::OnGrContextCreated() {
|
|
state_ = AttachmentState::uninitialized;
|
|
}
|
|
|
|
void AndroidExternalTextureGL::MarkNewFrameAvailable() {
|
|
new_frame_ready_ = true;
|
|
}
|
|
|
|
void AndroidExternalTextureGL::Paint(SkCanvas& canvas,
|
|
const SkRect& bounds,
|
|
bool freeze,
|
|
GrContext* context) {
|
|
if (state_ == AttachmentState::detached) {
|
|
return;
|
|
}
|
|
if (state_ == AttachmentState::uninitialized) {
|
|
glGenTextures(1, &texture_name_);
|
|
Attach(static_cast<jint>(texture_name_));
|
|
state_ = AttachmentState::attached;
|
|
}
|
|
if (!freeze && new_frame_ready_) {
|
|
Update();
|
|
new_frame_ready_ = false;
|
|
}
|
|
GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_,
|
|
GL_RGBA8_OES};
|
|
GrBackendTexture backendTexture(1, 1, GrMipMapped::kNo, textureInfo);
|
|
sk_sp<SkImage> image = SkImage::MakeFromTexture(
|
|
canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin,
|
|
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
|
|
if (image) {
|
|
SkAutoCanvasRestore autoRestore(&canvas, true);
|
|
canvas.translate(bounds.x(), bounds.y());
|
|
canvas.scale(bounds.width(), bounds.height());
|
|
if (!transform.isIdentity()) {
|
|
SkMatrix transformAroundCenter(transform);
|
|
|
|
transformAroundCenter.preTranslate(-0.5, -0.5);
|
|
transformAroundCenter.postScale(1, -1);
|
|
transformAroundCenter.postTranslate(0.5, 0.5);
|
|
canvas.concat(transformAroundCenter);
|
|
}
|
|
canvas.drawImage(image, 0, 0);
|
|
}
|
|
}
|
|
|
|
// The bounds we set for the canvas are post composition.
|
|
// To fill the canvas we need to ensure that the transformation matrix
|
|
// on the `SurfaceTexture` will be scaled to fill. We rescale and preseve
|
|
// the scaled aspect ratio.
|
|
SkSize ScaleToFill(float scaleX, float scaleY) {
|
|
const double epsilon = std::numeric_limits<double>::epsilon();
|
|
// scaleY is negative.
|
|
const double minScale = fmin(scaleX, fabs(scaleY));
|
|
const double rescale = 1.0f / (minScale + epsilon);
|
|
return SkSize::Make(scaleX * rescale, scaleY * rescale);
|
|
}
|
|
|
|
void AndroidExternalTextureGL::UpdateTransform() {
|
|
JNIEnv* env = fml::jni::AttachCurrentThread();
|
|
fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
|
|
surface_texture_.get(env);
|
|
fml::jni::ScopedJavaLocalRef<jfloatArray> transformMatrix(
|
|
env, env->NewFloatArray(16));
|
|
SurfaceTextureGetTransformMatrix(env, surfaceTexture.obj(),
|
|
transformMatrix.obj());
|
|
float* m = env->GetFloatArrayElements(transformMatrix.obj(), nullptr);
|
|
float scaleX = m[0], scaleY = m[5];
|
|
const SkSize scaled = ScaleToFill(scaleX, scaleY);
|
|
SkScalar matrix3[] = {
|
|
scaled.fWidth, m[1], m[2], //
|
|
m[4], scaled.fHeight, m[6], //
|
|
m[8], m[9], m[10], //
|
|
};
|
|
env->ReleaseFloatArrayElements(transformMatrix.obj(), m, JNI_ABORT);
|
|
transform.set9(matrix3);
|
|
}
|
|
|
|
void AndroidExternalTextureGL::OnGrContextDestroyed() {
|
|
if (state_ == AttachmentState::attached) {
|
|
Detach();
|
|
}
|
|
state_ = AttachmentState::detached;
|
|
}
|
|
|
|
void AndroidExternalTextureGL::Attach(jint textureName) {
|
|
JNIEnv* env = fml::jni::AttachCurrentThread();
|
|
fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
|
|
surface_texture_.get(env);
|
|
if (!surfaceTexture.is_null()) {
|
|
SurfaceTextureAttachToGLContext(env, surfaceTexture.obj(), textureName);
|
|
}
|
|
}
|
|
|
|
void AndroidExternalTextureGL::Update() {
|
|
JNIEnv* env = fml::jni::AttachCurrentThread();
|
|
fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
|
|
surface_texture_.get(env);
|
|
if (!surfaceTexture.is_null()) {
|
|
SurfaceTextureUpdateTexImage(env, surfaceTexture.obj());
|
|
UpdateTransform();
|
|
}
|
|
}
|
|
|
|
void AndroidExternalTextureGL::Detach() {
|
|
JNIEnv* env = fml::jni::AttachCurrentThread();
|
|
fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture =
|
|
surface_texture_.get(env);
|
|
if (!surfaceTexture.is_null()) {
|
|
SurfaceTextureDetachFromGLContext(env, surfaceTexture.obj());
|
|
}
|
|
}
|
|
|
|
} // namespace flutter
|