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 
16  std::shared_ptr<SkCodecImageGenerator> generator)
17  : state_(new State(std::move(generator))) {}
18 
20 
21 MultiFrameCodec::State::State(std::shared_ptr<SkCodecImageGenerator> generator)
22  : generator_(std::move(generator)),
23  frameCount_(generator_->getFrameCount()),
24  repetitionCount_(generator_->getRepetitionCount()),
25  nextFrameIndex_(0) {}
26 
29  int duration,
30  std::unique_ptr<DartPersistentValue> callback,
31  size_t trace_id) {
32  std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
33  if (!dart_state) {
34  FML_DLOG(ERROR) << "Could not acquire Dart state while attempting to fire "
35  "next frame callback.";
36  return;
37  }
38  tonic::DartState::Scope scope(dart_state);
39  tonic::DartInvoke(callback->value(),
40  {tonic::ToDart(image), tonic::ToDart(duration)});
41 }
42 
43 // Copied the source bitmap to the destination. If this cannot occur due to
44 // running out of memory or the image info not being compatible, returns false.
45 static bool CopyToBitmap(SkBitmap* dst,
46  SkColorType dstColorType,
47  const SkBitmap& src) {
48  SkPixmap srcPM;
49  if (!src.peekPixels(&srcPM)) {
50  return false;
51  }
52 
53  SkBitmap tmpDst;
54  SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
55  if (!tmpDst.setInfo(dstInfo)) {
56  return false;
57  }
58 
59  if (!tmpDst.tryAllocPixels()) {
60  return false;
61  }
62 
63  SkPixmap dstPM;
64  if (!tmpDst.peekPixels(&dstPM)) {
65  return false;
66  }
67 
68  if (!srcPM.readPixels(dstPM)) {
69  return false;
70  }
71 
72  dst->swap(tmpDst);
73  return true;
74 }
75 
76 sk_sp<SkImage> MultiFrameCodec::State::GetNextFrameImage(
77  fml::WeakPtr<GrDirectContext> resourceContext) {
78  SkBitmap bitmap = SkBitmap();
79  SkImageInfo info = generator_->getInfo().makeColorType(kN32_SkColorType);
80  if (info.alphaType() == kUnpremul_SkAlphaType) {
81  SkImageInfo updated = info.makeAlphaType(kPremul_SkAlphaType);
82  info = updated;
83  }
84  bitmap.allocPixels(info);
85 
86  SkCodec::Options options;
87  options.fFrameIndex = nextFrameIndex_;
88  SkCodec::FrameInfo frameInfo{0};
89  generator_->getFrameInfo(nextFrameIndex_, &frameInfo);
90  const int requiredFrameIndex = frameInfo.fRequiredFrame;
91  if (requiredFrameIndex != SkCodec::kNoFrame) {
92  if (lastRequiredFrame_ == nullptr) {
93  FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame "
94  << requiredFrameIndex
95  << " and no required frames are cached.";
96  return nullptr;
97  } else if (lastRequiredFrameIndex_ != requiredFrameIndex) {
98  FML_DLOG(INFO) << "Required frame " << requiredFrameIndex
99  << " is not cached. Using " << lastRequiredFrameIndex_
100  << " instead";
101  }
102 
103  if (lastRequiredFrame_->getPixels() &&
104  CopyToBitmap(&bitmap, lastRequiredFrame_->colorType(),
105  *lastRequiredFrame_)) {
106  options.fPriorFrame = requiredFrameIndex;
107  }
108  }
109 
110  if (!generator_->getPixels(info, bitmap.getPixels(), bitmap.rowBytes(),
111  &options)) {
112  FML_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_;
113  return nullptr;
114  }
115 
116  // Hold onto this if we need it to decode future frames.
117  if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kKeep) {
118  lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
119  lastRequiredFrameIndex_ = nextFrameIndex_;
120  }
121 
122  if (resourceContext) {
123  SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
124  bitmap.pixelRef()->rowBytes());
125  return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap,
126  true);
127  } else {
128  // Defer decoding until time of draw later on the raster thread. Can happen
129  // when GL operations are currently forbidden such as in the background
130  // on iOS.
131  return SkImage::MakeFromBitmap(bitmap);
132  }
133 }
134 
135 void MultiFrameCodec::State::GetNextFrameAndInvokeCallback(
136  std::unique_ptr<DartPersistentValue> callback,
137  fml::RefPtr<fml::TaskRunner> ui_task_runner,
138  fml::WeakPtr<GrDirectContext> resourceContext,
140  size_t trace_id) {
141  fml::RefPtr<CanvasImage> image = nullptr;
142  int duration = 0;
143  sk_sp<SkImage> skImage = GetNextFrameImage(resourceContext);
144  if (skImage) {
145  image = CanvasImage::Create();
146  image->set_image({skImage, std::move(unref_queue)});
147  SkCodec::FrameInfo skFrameInfo{0};
148  generator_->getFrameInfo(nextFrameIndex_, &skFrameInfo);
149  duration = skFrameInfo.fDuration;
150  }
151  nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_;
152 
153  ui_task_runner->PostTask(fml::MakeCopyable([callback = std::move(callback),
154  image = std::move(image),
155  duration, trace_id]() mutable {
156  InvokeNextFrameCallback(std::move(image), duration, std::move(callback),
157  trace_id);
158  }));
159 }
160 
161 Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) {
162  static size_t trace_counter = 1;
163  const size_t trace_id = trace_counter++;
164 
165  if (!Dart_IsClosure(callback_handle)) {
166  return tonic::ToDart("Callback must be a function");
167  }
168 
169  auto* dart_state = UIDartState::Current();
170 
171  const auto& task_runners = dart_state->GetTaskRunners();
172 
173  task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
174  [callback = std::make_unique<DartPersistentValue>(
175  tonic::DartState::Current(), callback_handle),
176  weak_state = std::weak_ptr<MultiFrameCodec::State>(state_), trace_id,
177  ui_task_runner = task_runners.GetUITaskRunner(),
178  io_manager = dart_state->GetIOManager()]() mutable {
179  auto state = weak_state.lock();
180  if (!state) {
181  ui_task_runner->PostTask(fml::MakeCopyable(
182  [callback = std::move(callback)]() { callback->Clear(); }));
183  return;
184  }
185  state->GetNextFrameAndInvokeCallback(
186  std::move(callback), std::move(ui_task_runner),
187  io_manager->GetResourceContext(), io_manager->GetSkiaUnrefQueue(),
188  trace_id);
189  }));
190 
191  return Dart_Null();
192 }
193 
195  return state_->frameCount_;
196 }
197 
199  return state_->repetitionCount_;
200 }
201 
202 } // namespace flutter
static bool CopyToBitmap(SkBitmap *dst, SkColorType dstColorType, const SkBitmap &src)
Definition: ref_ptr.h:252
static DartState * Current()
Definition: dart_state.cc:55
virtual void PostTask(const fml::closure &task)
Definition: task_runner.cc:24
#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
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
MultiFrameCodec(std::shared_ptr< SkCodecImageGenerator > generator)
#define FML_DLOG(severity)
Definition: logging.h:85
Dart_Handle ToDart(const T &object)
static void InvokeNextFrameCallback(fml::RefPtr< CanvasImage > image, int duration, std::unique_ptr< DartPersistentValue > callback, size_t trace_id)
Dart_Handle getNextFrame(Dart_Handle args) override
static UIDartState * Current()