blob: ce1f6fc91c0dba81dfd2102970e9f37e4113926a [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/lib/ui/painting/single_frame_codec.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/tonic/logging/dart_invoke.h"
namespace flutter {
SingleFrameCodec::SingleFrameCodec(fml::RefPtr<ImageDescriptor> descriptor,
uint32_t target_width,
uint32_t target_height)
: status_(Status::kNew),
descriptor_(std::move(descriptor)),
target_width_(target_width),
target_height_(target_height) {}
SingleFrameCodec::~SingleFrameCodec() = default;
int SingleFrameCodec::frameCount() const {
return 1;
}
int SingleFrameCodec::repetitionCount() const {
return 0;
}
Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) {
if (!Dart_IsClosure(callback_handle)) {
return tonic::ToDart("Callback must be a function");
}
if (status_ == Status::kComplete) {
if (!cached_image_->image()) {
return tonic::ToDart("Decoded image has been disposed");
}
tonic::DartInvoke(callback_handle,
{tonic::ToDart(cached_image_), tonic::ToDart(0)});
return Dart_Null();
}
// This has to be valid because this method is called from Dart.
auto dart_state = UIDartState::Current();
pending_callbacks_.emplace_back(dart_state, callback_handle);
if (status_ == Status::kInProgress) {
// Another call to getNextFrame is in progress and will invoke the
// pending callbacks when decoding completes.
return Dart_Null();
}
auto decoder = dart_state->GetImageDecoder();
if (!decoder) {
return tonic::ToDart(
"Failed to access the internal image decoder "
"registry on this isolate. Please file a bug on "
"https://github.com/flutter/flutter/issues.");
}
// The SingleFrameCodec must be deleted on the UI thread. Allocate a RefPtr
// on the heap to ensure that the SingleFrameCodec remains alive until the
// decoder callback is invoked on the UI thread. The callback can then
// drop the reference.
fml::RefPtr<SingleFrameCodec>* raw_codec_ref =
new fml::RefPtr<SingleFrameCodec>(this);
decoder->Decode(
descriptor_, target_width_, target_height_, [raw_codec_ref](auto image) {
std::unique_ptr<fml::RefPtr<SingleFrameCodec>> codec_ref(raw_codec_ref);
fml::RefPtr<SingleFrameCodec> codec(std::move(*codec_ref));
auto state = codec->pending_callbacks_.front().dart_state().lock();
if (!state) {
// This is probably because the isolate has been terminated before the
// image could be decoded.
return;
}
tonic::DartState::Scope scope(state.get());
if (image) {
auto canvas_image = fml::MakeRefCounted<CanvasImage>();
canvas_image->set_image(std::move(image));
codec->cached_image_ = std::move(canvas_image);
}
// The cached frame is now available and should be returned to any
// future callers.
codec->status_ = Status::kComplete;
// Invoke any callbacks that were provided before the frame was decoded.
for (const DartPersistentValue& callback : codec->pending_callbacks_) {
tonic::DartInvoke(
callback.value(),
{tonic::ToDart(codec->cached_image_), tonic::ToDart(0)});
}
codec->pending_callbacks_.clear();
});
// The encoded data is no longer needed now that it has been handed off
// to the decoder.
descriptor_ = nullptr;
status_ = Status::kInProgress;
return Dart_Null();
}
size_t SingleFrameCodec::GetAllocationSize() const {
return sizeof(*this);
}
} // namespace flutter