Flutter Engine
multi_frame_codec.cc
Go to the documentation of this file.
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/multi_frame_codec.h"
6 
7 #include "flutter/fml/make_copyable.h"
8 #include "flutter/lib/ui/painting/image.h"
9 #include "third_party/dart/runtime/include/dart_api.h"
10 #include "third_party/skia/include/core/SkPixelRef.h"
12 
13 namespace flutter {
14 
15 MultiFrameCodec::MultiFrameCodec(std::shared_ptr<ImageGenerator> generator)
16  : state_(new State(std::move(generator))) {}
17 
19 
20 MultiFrameCodec::State::State(std::shared_ptr<ImageGenerator> generator)
21  : generator_(std::move(generator)),
22  frameCount_(generator_->GetFrameCount()),
23  repetitionCount_(generator_->GetPlayCount() ==
25  ? -1
26  : generator_->GetPlayCount() - 1),
27  nextFrameIndex_(0) {}
28 
31  int duration,
32  std::unique_ptr<DartPersistentValue> callback,
33  size_t trace_id) {
34  std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
35  if (!dart_state) {
36  FML_DLOG(ERROR) << "Could not acquire Dart state while attempting to fire "
37  "next frame callback.";
38  return;
39  }
40  tonic::DartState::Scope scope(dart_state);
41  tonic::DartInvoke(callback->value(),
42  {tonic::ToDart(image), tonic::ToDart(duration)});
43 }
44 
45 // Copied the source bitmap to the destination. If this cannot occur due to
46 // running out of memory or the image info not being compatible, returns false.
47 static bool CopyToBitmap(SkBitmap* dst,
48  SkColorType dstColorType,
49  const SkBitmap& src) {
50  SkPixmap srcPM;
51  if (!src.peekPixels(&srcPM)) {
52  return false;
53  }
54 
55  SkBitmap tmpDst;
56  SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
57  if (!tmpDst.setInfo(dstInfo)) {
58  return false;
59  }
60 
61  if (!tmpDst.tryAllocPixels()) {
62  return false;
63  }
64 
65  SkPixmap dstPM;
66  if (!tmpDst.peekPixels(&dstPM)) {
67  return false;
68  }
69 
70  if (!srcPM.readPixels(dstPM)) {
71  return false;
72  }
73 
74  dst->swap(tmpDst);
75  return true;
76 }
77 
78 sk_sp<SkImage> MultiFrameCodec::State::GetNextFrameImage(
79  fml::WeakPtr<GrDirectContext> resourceContext,
80  const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch) {
81  SkBitmap bitmap = SkBitmap();
82  SkImageInfo info = generator_->GetInfo().makeColorType(kN32_SkColorType);
83  if (info.alphaType() == kUnpremul_SkAlphaType) {
84  SkImageInfo updated = info.makeAlphaType(kPremul_SkAlphaType);
85  info = updated;
86  }
87  bitmap.allocPixels(info);
88 
89  ImageGenerator::FrameInfo frameInfo =
90  generator_->GetFrameInfo(nextFrameIndex_);
91 
92  const int requiredFrameIndex =
93  frameInfo.required_frame.value_or(SkCodec::kNoFrame);
94  std::optional<unsigned int> prior_frame_index = std::nullopt;
95 
96  if (requiredFrameIndex != SkCodec::kNoFrame) {
97  if (lastRequiredFrame_ == nullptr) {
98  FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame "
99  << requiredFrameIndex
100  << " and no required frames are cached.";
101  return nullptr;
102  } else if (lastRequiredFrameIndex_ != requiredFrameIndex) {
103  FML_DLOG(INFO) << "Required frame " << requiredFrameIndex
104  << " is not cached. Using " << lastRequiredFrameIndex_
105  << " instead";
106  }
107 
108  if (lastRequiredFrame_->getPixels() &&
109  CopyToBitmap(&bitmap, lastRequiredFrame_->colorType(),
110  *lastRequiredFrame_)) {
111  prior_frame_index = requiredFrameIndex;
112  }
113  }
114 
115  if (!generator_->GetPixels(info, bitmap.getPixels(), bitmap.rowBytes(),
116  nextFrameIndex_, requiredFrameIndex)) {
117  FML_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_;
118  return nullptr;
119  }
120 
121  // Hold onto this if we need it to decode future frames.
122  if (frameInfo.disposal_method == SkCodecAnimation::DisposalMethod::kKeep) {
123  lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
124  lastRequiredFrameIndex_ = nextFrameIndex_;
125  }
126  sk_sp<SkImage> result;
127 
128  gpu_disable_sync_switch->Execute(
130  .SetIfTrue([&result, &bitmap] {
131  // Defer decoding until time of draw later on the raster thread. Can
132  // happen when GL operations are currently forbidden such as in the
133  // background on iOS.
134  result = SkImage::MakeFromBitmap(bitmap);
135  })
136  .SetIfFalse([&result, &resourceContext, &bitmap] {
137  if (resourceContext) {
138  SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
139  bitmap.pixelRef()->rowBytes());
140  result = SkImage::MakeCrossContextFromPixmap(
141  resourceContext.get(), pixmap, true);
142  } else {
143  // Defer decoding until time of draw later on the raster thread.
144  // Can happen when GL operations are currently forbidden such as
145  // in the background on iOS.
146  result = SkImage::MakeFromBitmap(bitmap);
147  }
148  }));
149  return result;
150 }
151 
152 void MultiFrameCodec::State::GetNextFrameAndInvokeCallback(
153  std::unique_ptr<DartPersistentValue> callback,
154  fml::RefPtr<fml::TaskRunner> ui_task_runner,
155  fml::WeakPtr<GrDirectContext> resourceContext,
157  const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
158  size_t trace_id) {
159  fml::RefPtr<CanvasImage> image = nullptr;
160  int duration = 0;
161  sk_sp<SkImage> skImage =
162  GetNextFrameImage(resourceContext, gpu_disable_sync_switch);
163  if (skImage) {
164  image = CanvasImage::Create();
165  image->set_image({skImage, std::move(unref_queue)});
166  ImageGenerator::FrameInfo frameInfo =
167  generator_->GetFrameInfo(nextFrameIndex_);
168  duration = frameInfo.duration;
169  }
170  nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_;
171 
172  ui_task_runner->PostTask(fml::MakeCopyable([callback = std::move(callback),
173  image = std::move(image),
174  duration, trace_id]() mutable {
175  InvokeNextFrameCallback(std::move(image), duration, std::move(callback),
176  trace_id);
177  }));
178 }
179 
180 Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) {
181  static size_t trace_counter = 1;
182  const size_t trace_id = trace_counter++;
183 
184  if (!Dart_IsClosure(callback_handle)) {
185  return tonic::ToDart("Callback must be a function");
186  }
187 
188  auto* dart_state = UIDartState::Current();
189 
190  const auto& task_runners = dart_state->GetTaskRunners();
191 
192  if (state_->frameCount_ == 0) {
193  FML_LOG(ERROR) << "Could not provide any frame.";
194  task_runners.GetUITaskRunner()->PostTask(fml::MakeCopyable(
195  [trace_id,
196  callback = std::make_unique<DartPersistentValue>(
197  tonic::DartState::Current(), callback_handle)]() mutable {
198  InvokeNextFrameCallback(nullptr, 0, std::move(callback), trace_id);
199  }));
200  return Dart_Null();
201  }
202 
203  task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
204  [callback = std::make_unique<DartPersistentValue>(
205  tonic::DartState::Current(), callback_handle),
206  weak_state = std::weak_ptr<MultiFrameCodec::State>(state_), trace_id,
207  ui_task_runner = task_runners.GetUITaskRunner(),
208  io_manager = dart_state->GetIOManager()]() mutable {
209  auto state = weak_state.lock();
210  if (!state) {
211  ui_task_runner->PostTask(fml::MakeCopyable(
212  [callback = std::move(callback)]() { callback->Clear(); }));
213  return;
214  }
215  state->GetNextFrameAndInvokeCallback(
216  std::move(callback), std::move(ui_task_runner),
217  io_manager->GetResourceContext(), io_manager->GetSkiaUnrefQueue(),
218  io_manager->GetIsGpuDisabledSyncSwitch(), trace_id);
219  }));
220 
221  return Dart_Null();
222 }
223 
225  return state_->frameCount_;
226 }
227 
229  return state_->repetitionCount_;
230 }
231 
232 } // namespace flutter
static bool CopyToBitmap(SkBitmap *dst, SkColorType dstColorType, const SkBitmap &src)
Info about a single frame in the context of a multi-frame image, useful for animation and blending...
SkCodecAnimation::DisposalMethod disposal_method
How this frame should be modified before decoding the next one.
Definition: ref_ptr.h:252
static DartState * Current()
Definition: dart_state.cc:56
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:65
Dart_Handle DartInvoke(Dart_Handle closure, std::initializer_list< Dart_Handle > args)
Definition: dart_invoke.cc:20
int repetitionCount() const override
FlKeyEvent FlKeyResponderAsyncCallback callback
std::optional< unsigned int > required_frame
static fml::RefPtr< CanvasImage > Create()
Definition: image.h:25
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
T * get() const
Definition: weak_ptr.h:88
int frameCount() const override
static const unsigned int kInfinitePlayCount
Frame count value to denote infinite looping.
unsigned int duration
Number of milliseconds to show this frame.
#define FML_DLOG(severity)
Definition: logging.h:85
Dart_Handle ToDart(const T &object)
MultiFrameCodec(std::shared_ptr< ImageGenerator > generator)
static void InvokeNextFrameCallback(fml::RefPtr< CanvasImage > image, int duration, std::unique_ptr< DartPersistentValue > callback, size_t trace_id)
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
AtkStateType state
Dart_Handle getNextFrame(Dart_Handle args) override
static UIDartState * Current()
Represents the 2 code paths available when calling |SyncSwitch::Execute|.
Definition: sync_switch.h:24