| // 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 |