blob: 3e40b99c3d5828fdd0e766a7d9b9b9a942d78448 [file] [log] [blame]
// 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 <utility>
#include "flutter/display_list/effects/dl_color_source.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
namespace flutter {
AndroidExternalTextureGL::AndroidExternalTextureGL(
int64_t id,
const fml::jni::ScopedJavaGlobalRef<jobject>& surface_texture,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: Texture(id),
jni_facade_(std::move(jni_facade)),
surface_texture_(surface_texture),
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(PaintContext& context,
const SkRect& bounds,
bool freeze,
const DlImageSampling sampling) {
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 = SkImages::BorrowTextureFrom(
context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
if (image) {
DlAutoCanvasRestore autoRestore(context.canvas, true);
// The incoming texture is vertically flipped, so we flip it
// back. OpenGL's coordinate system has Positive Y equivalent to up, while
// Skia's coordinate system has Negative Y equvalent to up.
context.canvas->Translate(bounds.x(), bounds.y() + bounds.height());
context.canvas->Scale(bounds.width(), -bounds.height());
auto dl_image = DlImage::Make(image);
if (!transform.isIdentity()) {
DlImageColorSource source(dl_image, DlTileMode::kRepeat,
DlTileMode::kRepeat, sampling, &transform);
DlPaint paintWithShader;
if (context.paint) {
paintWithShader = *context.paint;
}
paintWithShader.setColorSource(&source);
context.canvas->DrawRect(SkRect::MakeWH(1, 1), paintWithShader);
} else {
context.canvas->DrawImage(dl_image, {0, 0}, sampling, context.paint);
}
}
}
void AndroidExternalTextureGL::UpdateTransform() {
jni_facade_->SurfaceTextureGetTransformMatrix(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_), transform);
// Android's SurfaceTexture transform matrix works on texture coordinate
// lookups in the range 0.0-1.0, while Skia's Shader transform matrix works on
// the image itself, as if it were inscribed inside a clip rect.
// An Android transform that scales lookup by 0.5 (displaying 50% of the
// texture) is the same as a Skia transform by 2.0 (scaling 50% of the image
// outside of the virtual "clip rect"), so we invert the incoming matrix.
SkMatrix inverted;
if (!transform.invert(&inverted)) {
FML_LOG(FATAL) << "Invalid SurfaceTexture transformation matrix";
}
transform = inverted;
}
void AndroidExternalTextureGL::OnGrContextDestroyed() {
if (state_ == AttachmentState::attached) {
Detach();
glDeleteTextures(1, &texture_name_);
}
state_ = AttachmentState::detached;
}
void AndroidExternalTextureGL::Attach(jint textureName) {
jni_facade_->SurfaceTextureAttachToGLContext(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_), textureName);
}
void AndroidExternalTextureGL::Update() {
jni_facade_->SurfaceTextureUpdateTexImage(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_));
UpdateTransform();
}
void AndroidExternalTextureGL::Detach() {
jni_facade_->SurfaceTextureDetachFromGLContext(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_));
}
void AndroidExternalTextureGL::OnTextureUnregistered() {}
} // namespace flutter