blob: d4013a0a64e40e179ae46c653a97306fa9f24359 [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/surface_texture_external_texture.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"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
namespace flutter {
SurfaceTextureExternalTexture::SurfaceTextureExternalTexture(
int64_t id,
const fml::jni::ScopedJavaGlobalRef<jobject>& surface_texture,
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade)
: Texture(id),
jni_facade_(jni_facade),
surface_texture_(surface_texture),
transform_(SkMatrix::I()) {}
SurfaceTextureExternalTexture::~SurfaceTextureExternalTexture() {}
void SurfaceTextureExternalTexture::OnGrContextCreated() {
state_ = AttachmentState::kUninitialized;
}
void SurfaceTextureExternalTexture::MarkNewFrameAvailable() {
new_frame_ready_ = true;
}
void SurfaceTextureExternalTexture::Paint(PaintContext& context,
const SkRect& bounds,
bool freeze,
const DlImageSampling sampling) {
if (state_ == AttachmentState::kDetached) {
return;
}
const bool should_process_frame =
(!freeze && new_frame_ready_) || dl_image_ == nullptr;
if (should_process_frame) {
ProcessFrame(context, bounds);
new_frame_ready_ = false;
}
FML_CHECK(state_ == AttachmentState::kAttached);
if (dl_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 (as well as Impeller's) has Negative Y equvalent to up.
{
// Move (translate) the origin to the bottom-left corner of the image.
context.canvas->Translate(bounds.x(), bounds.y() + bounds.height());
// No change in the X axis, but we need to flip the Y axis.
context.canvas->Scale(1, -1);
}
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(bounds.width(), bounds.height()),
paintWithShader);
} else {
context.canvas->DrawImage(dl_image_, {0, 0}, sampling, context.paint);
}
} else {
FML_LOG(WARNING)
<< "No DlImage available for SurfaceTextureExternalTexture to paint.";
}
}
void SurfaceTextureExternalTexture::OnGrContextDestroyed() {
if (state_ == AttachmentState::kAttached) {
Detach();
}
state_ = AttachmentState::kDetached;
}
void SurfaceTextureExternalTexture::OnTextureUnregistered() {}
void SurfaceTextureExternalTexture::Detach() {
jni_facade_->SurfaceTextureDetachFromGLContext(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_));
dl_image_.reset();
}
void SurfaceTextureExternalTexture::Attach(int gl_tex_id) {
jni_facade_->SurfaceTextureAttachToGLContext(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_), gl_tex_id);
state_ = AttachmentState::kAttached;
}
void SurfaceTextureExternalTexture::Update() {
jni_facade_->SurfaceTextureUpdateTexImage(
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_));
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 (not invertable) SurfaceTexture transformation matrix";
}
transform_ = inverted;
}
} // namespace flutter