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
11namespace flutter {
12
13SingleFrameCodec::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
21SingleFrameCodec::~SingleFrameCodec() = default;
22
23int SingleFrameCodec::frameCount() const {
24 return 1;
25}
26
27int SingleFrameCodec::repetitionCount() const {
28 return 0;
29}
30
31Dart_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
110size_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