1 // Copyright 2013 The Flutter Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "flutter/lib/ui/painting/single_frame_codec.h" 6 7 #include "flutter/lib/ui/painting/frame_info.h" 8 #include "flutter/lib/ui/ui_dart_state.h" 9 10 namespace flutter { 11 SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor)12SingleFrameCodec::SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor) 13 : status_(Status::kNew), descriptor_(std::move(descriptor)) {} 14 15 SingleFrameCodec::~SingleFrameCodec() = default; 16 frameCount() const17int SingleFrameCodec::frameCount() const { 18 return 1; 19 } 20 repetitionCount() const21int SingleFrameCodec::repetitionCount() const { 22 return 0; 23 } 24 getNextFrame(Dart_Handle callback_handle)25Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) { 26 if (!Dart_IsClosure(callback_handle)) { 27 return tonic::ToDart("Callback must be a function"); 28 } 29 30 if (status_ == Status::kComplete) { 31 tonic::DartInvoke(callback_handle, {tonic::ToDart(cached_frame_)}); 32 return Dart_Null(); 33 } 34 35 // This has to be valid because this method is called from Dart. 36 auto dart_state = UIDartState::Current(); 37 38 pending_callbacks_.emplace_back(dart_state, callback_handle); 39 40 if (status_ == Status::kInProgress) { 41 // Another call to getNextFrame is in progress and will invoke the 42 // pending callbacks when decoding completes. 43 return Dart_Null(); 44 } 45 46 auto decoder = dart_state->GetImageDecoder(); 47 48 if (!decoder) { 49 return tonic::ToDart("Image decoder not available."); 50 } 51 52 // The SingleFrameCodec must be deleted on the UI thread. Allocate a RefPtr 53 // on the heap to ensure that the SingleFrameCodec remains alive until the 54 // decoder callback is invoked on the UI thread. The callback can then 55 // drop the reference. 56 fml::RefPtr<SingleFrameCodec>* raw_codec_ref = 57 new fml::RefPtr<SingleFrameCodec>(this); 58 59 decoder->Decode(descriptor_, [raw_codec_ref](auto image) { 60 std::unique_ptr<fml::RefPtr<SingleFrameCodec>> codec_ref(raw_codec_ref); 61 fml::RefPtr<SingleFrameCodec> codec(std::move(*codec_ref)); 62 63 auto state = codec->pending_callbacks_.front().dart_state().lock(); 64 65 if (!state) { 66 // This is probably because the isolate has been terminated before the 67 // image could be decoded. 68 69 return; 70 } 71 72 tonic::DartState::Scope scope(state.get()); 73 74 if (image.get()) { 75 auto canvas_image = fml::MakeRefCounted<CanvasImage>(); 76 canvas_image->set_image(std::move(image)); 77 78 codec->cached_frame_ = fml::MakeRefCounted<FrameInfo>( 79 std::move(canvas_image), 0 /* duration */); 80 } 81 82 // The cached frame is now available and should be returned to any future 83 // callers. 84 codec->status_ = Status::kComplete; 85 86 // Invoke any callbacks that were provided before the frame was decoded. 87 Dart_Handle frame = tonic::ToDart(codec->cached_frame_); 88 for (const DartPersistentValue& callback : codec->pending_callbacks_) { 89 tonic::DartInvoke(callback.value(), {frame}); 90 } 91 codec->pending_callbacks_.clear(); 92 }); 93 94 // The encoded data is no longer needed now that it has been handed off 95 // to the decoder. 96 descriptor_.data.reset(); 97 98 status_ = Status::kInProgress; 99 100 return Dart_Null(); 101 } 102 GetAllocationSize()103size_t SingleFrameCodec::GetAllocationSize() { 104 const auto& data = descriptor_.data; 105 const auto data_byte_size = data ? data->size() : 0; 106 const auto frame_byte_size = (cached_frame_ && cached_frame_->image()) 107 ? cached_frame_->image()->GetAllocationSize() 108 : 0; 109 return data_byte_size + frame_byte_size + sizeof(this); 110 } 111 112 } // namespace flutter 113