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