• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)12 SingleFrameCodec::SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor)
13     : status_(Status::kNew), descriptor_(std::move(descriptor)) {}
14 
15 SingleFrameCodec::~SingleFrameCodec() = default;
16 
frameCount() const17 int SingleFrameCodec::frameCount() const {
18   return 1;
19 }
20 
repetitionCount() const21 int SingleFrameCodec::repetitionCount() const {
22   return 0;
23 }
24 
getNextFrame(Dart_Handle callback_handle)25 Dart_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()103 size_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