mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
358 lines
12 KiB
C++
358 lines
12 KiB
C++
// Copyright 2017 The Chromium 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/lib/ui/painting/codec.h"
|
|
|
|
#include "flutter/common/task_runners.h"
|
|
#include "flutter/glue/trace_event.h"
|
|
#include "flutter/lib/ui/painting/frame_info.h"
|
|
#include "lib/fxl/functional/make_copyable.h"
|
|
#include "lib/fxl/logging.h"
|
|
#include "lib/tonic/dart_binding_macros.h"
|
|
#include "lib/tonic/dart_library_natives.h"
|
|
#include "lib/tonic/dart_state.h"
|
|
#include "lib/tonic/logging/dart_invoke.h"
|
|
#include "lib/tonic/typed_data/uint8_list.h"
|
|
#include "third_party/skia/include/codec/SkCodec.h"
|
|
#include "third_party/skia/include/core/SkPixelRef.h"
|
|
|
|
#ifdef ERROR
|
|
#undef ERROR
|
|
#endif
|
|
|
|
using tonic::DartInvoke;
|
|
using tonic::DartPersistentValue;
|
|
using tonic::ToDart;
|
|
|
|
namespace blink {
|
|
|
|
namespace {
|
|
|
|
static constexpr const char* kInitCodecTraceTag = "InitCodec";
|
|
static constexpr const char* kCodecNextFrameTraceTag = "CodecNextFrame";
|
|
|
|
static void InvokeCodecCallback(fxl::RefPtr<Codec> codec,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
size_t trace_id) {
|
|
tonic::DartState* dart_state = callback->dart_state().get();
|
|
if (!dart_state) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
return;
|
|
}
|
|
tonic::DartState::Scope scope(dart_state);
|
|
if (!codec) {
|
|
DartInvoke(callback->value(), {Dart_Null()});
|
|
} else {
|
|
DartInvoke(callback->value(), {ToDart(codec)});
|
|
}
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
}
|
|
|
|
static sk_sp<SkImage> DecodeImage(fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkData> buffer,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("flutter", "DecodeImage");
|
|
|
|
if (buffer == nullptr || buffer->isEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (context) {
|
|
// This indicates that we do not want a "linear blending" decode.
|
|
sk_sp<SkColorSpace> dstColorSpace = nullptr;
|
|
return SkImage::MakeCrossContextFromEncoded(
|
|
context.get(), std::move(buffer), false, dstColorSpace.get(), true);
|
|
} else {
|
|
// Defer decoding until time of draw later on the GPU thread. Can happen
|
|
// when GL operations are currently forbidden such as in the background
|
|
// on iOS.
|
|
return SkImage::MakeFromEncoded(std::move(buffer));
|
|
}
|
|
}
|
|
|
|
fxl::RefPtr<Codec> InitCodec(fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkData> buffer,
|
|
fxl::RefPtr<flow::SkiaUnrefQueue> unref_queue,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("blink", "InitCodec");
|
|
|
|
if (buffer == nullptr || buffer->isEmpty()) {
|
|
FXL_LOG(ERROR) << "InitCodec failed - buffer was empty ";
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<SkCodec> skCodec = SkCodec::MakeFromData(buffer);
|
|
if (!skCodec) {
|
|
FXL_LOG(ERROR) << "Failed decoding image. Data is either invalid, or it is "
|
|
"encoded using an unsupported format.";
|
|
return nullptr;
|
|
}
|
|
if (skCodec->getFrameCount() > 1) {
|
|
return fxl::MakeRefCounted<MultiFrameCodec>(std::move(skCodec));
|
|
}
|
|
auto skImage = DecodeImage(context, buffer, trace_id);
|
|
if (!skImage) {
|
|
FXL_LOG(ERROR) << "DecodeImage failed";
|
|
return nullptr;
|
|
}
|
|
auto image = CanvasImage::Create();
|
|
image->set_image({skImage, unref_queue});
|
|
auto frameInfo = fxl::MakeRefCounted<FrameInfo>(std::move(image), 0);
|
|
return fxl::MakeRefCounted<SingleFrameCodec>(std::move(frameInfo));
|
|
}
|
|
|
|
void InitCodecAndInvokeCodecCallback(
|
|
fxl::RefPtr<fxl::TaskRunner> ui_task_runner,
|
|
fml::WeakPtr<GrContext> context,
|
|
fxl::RefPtr<flow::SkiaUnrefQueue> unref_queue,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
sk_sp<SkData> buffer,
|
|
size_t trace_id) {
|
|
auto codec =
|
|
InitCodec(context, std::move(buffer), std::move(unref_queue), trace_id);
|
|
ui_task_runner->PostTask(
|
|
fxl::MakeCopyable([callback = std::move(callback),
|
|
codec = std::move(codec), trace_id]() mutable {
|
|
InvokeCodecCallback(std::move(codec), std::move(callback), trace_id);
|
|
}));
|
|
}
|
|
|
|
void InstantiateImageCodec(Dart_NativeArguments args) {
|
|
static size_t trace_counter = 1;
|
|
const size_t trace_id = trace_counter++;
|
|
TRACE_FLOW_BEGIN("flutter", kInitCodecTraceTag, trace_id);
|
|
|
|
Dart_Handle exception = nullptr;
|
|
|
|
tonic::Uint8List list =
|
|
tonic::DartConverter<tonic::Uint8List>::FromArguments(args, 0, exception);
|
|
if (exception) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
Dart_SetReturnValue(args, exception);
|
|
return;
|
|
}
|
|
|
|
Dart_Handle callback_handle = Dart_GetNativeArgument(args, 1);
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
Dart_SetReturnValue(args, ToDart("Callback must be a function"));
|
|
return;
|
|
}
|
|
|
|
auto buffer = SkData::MakeWithCopy(list.data(), list.num_elements());
|
|
|
|
auto dart_state = UIDartState::Current();
|
|
|
|
const auto& task_runners = dart_state->GetTaskRunners();
|
|
task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable(
|
|
[callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle),
|
|
buffer = std::move(buffer), trace_id,
|
|
ui_task_runner = task_runners.GetUITaskRunner(),
|
|
context = dart_state->GetResourceContext(),
|
|
queue = UIDartState::Current()->GetSkiaUnrefQueue()]() mutable {
|
|
InitCodecAndInvokeCodecCallback(std::move(ui_task_runner), context,
|
|
std::move(queue), std::move(callback),
|
|
std::move(buffer), trace_id);
|
|
}));
|
|
}
|
|
|
|
bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
|
|
SkPixmap srcPM;
|
|
if (!src.peekPixels(&srcPM)) {
|
|
return false;
|
|
}
|
|
|
|
SkBitmap tmpDst;
|
|
SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
|
|
if (!tmpDst.setInfo(dstInfo)) {
|
|
return false;
|
|
}
|
|
|
|
if (!tmpDst.tryAllocPixels()) {
|
|
return false;
|
|
}
|
|
|
|
SkPixmap dstPM;
|
|
if (!tmpDst.peekPixels(&dstPM)) {
|
|
return false;
|
|
}
|
|
|
|
if (!srcPM.readPixels(dstPM)) {
|
|
return false;
|
|
}
|
|
|
|
dst->swap(tmpDst);
|
|
return true;
|
|
}
|
|
|
|
void InvokeNextFrameCallback(fxl::RefPtr<FrameInfo> frameInfo,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
size_t trace_id) {
|
|
tonic::DartState* dart_state = callback->dart_state().get();
|
|
if (!dart_state) {
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
return;
|
|
}
|
|
tonic::DartState::Scope scope(dart_state);
|
|
if (!frameInfo) {
|
|
DartInvoke(callback->value(), {Dart_Null()});
|
|
} else {
|
|
DartInvoke(callback->value(), {ToDart(frameInfo)});
|
|
}
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
IMPLEMENT_WRAPPERTYPEINFO(ui, Codec);
|
|
|
|
#define FOR_EACH_BINDING(V) \
|
|
V(Codec, getNextFrame) \
|
|
V(Codec, frameCount) \
|
|
V(Codec, repetitionCount) \
|
|
V(Codec, dispose)
|
|
|
|
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
|
|
|
|
void Codec::dispose() {
|
|
ClearDartWrapper();
|
|
}
|
|
|
|
MultiFrameCodec::MultiFrameCodec(std::unique_ptr<SkCodec> codec)
|
|
: codec_(std::move(codec)) {
|
|
repetitionCount_ = codec_->getRepetitionCount();
|
|
frameInfos_ = codec_->getFrameInfo();
|
|
frameBitmaps_.resize(frameInfos_.size());
|
|
nextFrameIndex_ = 0;
|
|
}
|
|
|
|
sk_sp<SkImage> MultiFrameCodec::GetNextFrameImage(
|
|
fml::WeakPtr<GrContext> resourceContext) {
|
|
SkBitmap& bitmap = frameBitmaps_[nextFrameIndex_];
|
|
if (!bitmap.getPixels()) { // We haven't decoded this frame yet
|
|
const SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType);
|
|
bitmap.allocPixels(info);
|
|
|
|
SkCodec::Options options;
|
|
options.fFrameIndex = nextFrameIndex_;
|
|
const int requiredFrame = frameInfos_[nextFrameIndex_].fRequiredFrame;
|
|
if (requiredFrame != SkCodec::kNone) {
|
|
if (requiredFrame < 0 ||
|
|
static_cast<size_t>(requiredFrame) >= frameBitmaps_.size()) {
|
|
FXL_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame "
|
|
<< requiredFrame << " which out of range (0,"
|
|
<< frameBitmaps_.size() << ").";
|
|
return NULL;
|
|
}
|
|
SkBitmap& requiredBitmap = frameBitmaps_[requiredFrame];
|
|
// For simplicity, do not try to cache old frames
|
|
if (requiredBitmap.getPixels() &&
|
|
copy_to(&bitmap, requiredBitmap.colorType(), requiredBitmap)) {
|
|
options.fPriorFrame = requiredFrame;
|
|
}
|
|
}
|
|
|
|
if (SkCodec::kSuccess != codec_->getPixels(info, bitmap.getPixels(),
|
|
bitmap.rowBytes(), &options)) {
|
|
FXL_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (resourceContext) {
|
|
SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
|
|
bitmap.pixelRef()->rowBytes());
|
|
// This indicates that we do not want a "linear blending" decode.
|
|
sk_sp<SkColorSpace> dstColorSpace = nullptr;
|
|
return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap,
|
|
false, dstColorSpace.get());
|
|
} else {
|
|
// Defer decoding until time of draw later on the GPU thread. Can happen
|
|
// when GL operations are currently forbidden such as in the background
|
|
// on iOS.
|
|
return SkImage::MakeFromBitmap(bitmap);
|
|
}
|
|
}
|
|
|
|
void MultiFrameCodec::GetNextFrameAndInvokeCallback(
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
fxl::RefPtr<fxl::TaskRunner> ui_task_runner,
|
|
fml::WeakPtr<GrContext> resourceContext,
|
|
fxl::RefPtr<flow::SkiaUnrefQueue> unref_queue,
|
|
size_t trace_id) {
|
|
fxl::RefPtr<FrameInfo> frameInfo = NULL;
|
|
sk_sp<SkImage> skImage = GetNextFrameImage(resourceContext);
|
|
if (skImage) {
|
|
fxl::RefPtr<CanvasImage> image = CanvasImage::Create();
|
|
image->set_image({skImage, std::move(unref_queue)});
|
|
frameInfo = fxl::MakeRefCounted<FrameInfo>(
|
|
std::move(image), frameInfos_[nextFrameIndex_].fDuration);
|
|
}
|
|
nextFrameIndex_ = (nextFrameIndex_ + 1) % frameInfos_.size();
|
|
|
|
ui_task_runner->PostTask(fxl::MakeCopyable(
|
|
[callback = std::move(callback), frameInfo, trace_id]() mutable {
|
|
InvokeNextFrameCallback(frameInfo, std::move(callback), trace_id);
|
|
}));
|
|
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
}
|
|
|
|
Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) {
|
|
static size_t trace_counter = 1;
|
|
const size_t trace_id = trace_counter++;
|
|
TRACE_FLOW_BEGIN("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
return ToDart("Callback must be a function");
|
|
}
|
|
|
|
auto dart_state = UIDartState::Current();
|
|
|
|
const auto& task_runners = dart_state->GetTaskRunners();
|
|
|
|
task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable(
|
|
[callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle),
|
|
this, trace_id, ui_task_runner = task_runners.GetUITaskRunner(),
|
|
queue = UIDartState::Current()->GetSkiaUnrefQueue(),
|
|
context = dart_state->GetResourceContext()]() mutable {
|
|
GetNextFrameAndInvokeCallback(std::move(callback),
|
|
std::move(ui_task_runner), context,
|
|
std::move(queue), trace_id);
|
|
}));
|
|
|
|
return Dart_Null();
|
|
}
|
|
|
|
Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) {
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
return ToDart("Callback must be a function");
|
|
}
|
|
|
|
auto callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle);
|
|
tonic::DartState* dart_state = callback->dart_state().get();
|
|
if (!dart_state) {
|
|
return ToDart("Invalid dart state");
|
|
}
|
|
|
|
tonic::DartState::Scope scope(dart_state);
|
|
DartInvoke(callback->value(), {ToDart(frame_)});
|
|
return Dart_Null();
|
|
}
|
|
|
|
void Codec::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
|
natives->Register({
|
|
{"instantiateImageCodec", InstantiateImageCodec, 2, true},
|
|
});
|
|
natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
|
|
}
|
|
|
|
} // namespace blink
|