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