13#if IMPELLER_SUPPORTS_RENDERING
17#include "third_party/dart/runtime/include/dart_api.h"
18#include "third_party/skia/include/codec/SkCodecAnimation.h"
19#include "third_party/skia/include/core/SkImage.h"
20#include "third_party/skia/include/core/SkPixelRef.h"
21#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
27 : state_(new State(
std::move(generator))) {}
31MultiFrameCodec::State::State(std::shared_ptr<ImageGenerator> generator)
32 : generator_(
std::move(generator)),
33 frameCount_(generator_->GetFrameCount()),
34 repetitionCount_(generator_->GetPlayCount() ==
37 : generator_->GetPlayCount() - 1),
38 is_impeller_enabled_(
UIDartState::Current()->IsImpellerEnabled()) {}
43 const std::string& decode_error,
44 std::unique_ptr<tonic::DartPersistentValue>
callback,
46 std::shared_ptr<tonic::DartState> dart_state =
callback->dart_state().lock();
48 FML_DLOG(ERROR) <<
"Could not acquire Dart state while attempting to fire "
49 "next frame callback.";
54 {tonic::ToDart(image), tonic::ToDart(duration),
55 tonic::ToDart(decode_error)});
58std::pair<sk_sp<DlImage>, std::string>
59MultiFrameCodec::State::GetNextFrameImage(
61 const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
62 const std::shared_ptr<impeller::Context>& impeller_context,
64 SkBitmap
bitmap = SkBitmap();
65 SkImageInfo info = generator_->GetInfo().makeColorType(kN32_SkColorType);
66 if (info.alphaType() == kUnpremul_SkAlphaType) {
67 SkImageInfo updated = info.makeAlphaType(kPremul_SkAlphaType);
70 if (!
bitmap.tryAllocPixels(info)) {
71 std::ostringstream ostr;
72 ostr <<
"Failed to allocate memory for bitmap of size "
73 << info.computeMinByteSize() <<
"B";
74 std::string decode_error = ostr.str();
76 return std::make_pair(
nullptr, decode_error);
79 ImageGenerator::FrameInfo frameInfo =
80 generator_->GetFrameInfo(nextFrameIndex_);
82 const int requiredFrameIndex =
83 frameInfo.required_frame.value_or(SkCodec::kNoFrame);
85 if (requiredFrameIndex != SkCodec::kNoFrame) {
89 if (!lastRequiredFrame_.has_value()) {
91 <<
"Frame " << nextFrameIndex_ <<
" depends on frame "
93 <<
" and no required frames are cached. Using blank slate instead.";
97 bitmap.writePixels(lastRequiredFrame_->pixmap());
98 if (restoreBGColorRect_.has_value()) {
99 bitmap.erase(SK_ColorTRANSPARENT, restoreBGColorRect_.value());
106 if (!generator_->GetPixels(info,
bitmap.getPixels(),
bitmap.rowBytes(),
107 nextFrameIndex_, requiredFrameIndex)) {
108 std::ostringstream ostr;
109 ostr <<
"Could not getPixels for frame " << nextFrameIndex_;
110 std::string decode_error = ostr.str();
111 FML_LOG(ERROR) << decode_error;
112 return std::make_pair(
nullptr, decode_error);
115 const bool keep_current_frame =
116 frameInfo.disposal_method == SkCodecAnimation::DisposalMethod::kKeep;
117 const bool restore_previous_frame =
118 frameInfo.disposal_method ==
119 SkCodecAnimation::DisposalMethod::kRestorePrevious;
120 const bool previous_frame_available = lastRequiredFrame_.has_value();
132 if (keep_current_frame ||
133 (previous_frame_available && !restore_previous_frame)) {
136 lastRequiredFrame_ =
bitmap;
137 lastRequiredFrameIndex_ = nextFrameIndex_;
140 if (frameInfo.disposal_method ==
141 SkCodecAnimation::DisposalMethod::kRestoreBGColor) {
142 restoreBGColorRect_ = frameInfo.disposal_rect;
144 restoreBGColorRect_.reset();
147#if IMPELLER_SUPPORTS_RENDERING
148 if (is_impeller_enabled_) {
153 impeller_context, std::make_shared<SkBitmap>(
bitmap));
155 sk_sp<DlImage> dl_image;
156 std::string error_message;
157 auto mapping = std::make_unique<fml::NonOwnedMapping>(
158 reinterpret_cast<const uint8_t*
>(
bitmap.getAddr(0, 0)),
159 bitmap.dimensions().area() * info.bytesPerPixel(),
160 [
bitmap](
auto,
auto)
mutable { bitmap.reset(); }
162 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
163 impeller_context->GetResourceAllocator()->CreateBufferWithCopy(
165 if (!device_buffer) {
166 return std::make_pair(
nullptr,
"Failed to allocate staging buffer.");
169 const auto pixel_format =
171 if (!pixel_format.has_value()) {
172 std::string decode_error(
173 std::format(
"Unsupported pixel format (SkColorType={})",
174 static_cast<int>(info.colorType())));
176 return std::make_pair(
nullptr, decode_error);
178 ImageDecoderImpeller::ImageInfo image_info = {
180 .format = pixel_format.value()};
184 dl_image = std::move(
image);
185 error_message = std::move(
message);
187 impeller_context, device_buffer, image_info, std::nullopt,
188 gpu_disable_sync_switch);
189 return std::make_pair(dl_image, error_message);
195 sk_sp<SkImage> skImage;
196 gpu_disable_sync_switch->Execute(
198 .SetIfTrue([&skImage, &
bitmap] {
202 skImage = SkImages::RasterFromBitmap(
bitmap);
204 .SetIfFalse([&skImage, &resourceContext, &
bitmap] {
205 if (resourceContext) {
206 SkPixmap pixmap(
bitmap.info(),
bitmap.pixelRef()->pixels(),
207 bitmap.pixelRef()->rowBytes());
208 skImage = SkImages::CrossContextTextureFromPixmap(
209 resourceContext.
get(), pixmap,
true);
214 skImage = SkImages::RasterFromBitmap(
bitmap);
221 return std::make_pair(
nullptr,
"Unsupported backend.");
225void MultiFrameCodec::State::GetNextFrameAndInvokeCallback(
226 std::unique_ptr<tonic::DartPersistentValue>
callback,
230 const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
232 const std::shared_ptr<impeller::Context>& impeller_context) {
233#if FML_OS_IOS_SIMULATOR
235 if (!resourceContext && !impeller_context) {
236 ui_task_runner->PostTask(
246 sk_sp<DlImage> dlImage;
247 std::string decode_error;
248 std::tie(dlImage, decode_error) = GetNextFrameImage(
249 resourceContext, gpu_disable_sync_switch, impeller_context, unref_queue);
252 image->set_image(dlImage);
253 ImageGenerator::FrameInfo frameInfo =
254 generator_->GetFrameInfo(nextFrameIndex_);
255 duration = frameInfo.duration;
257 nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_;
263 decode_error = std::move(decode_error), duration, trace_id]()
mutable {
270 static size_t trace_counter = 1;
271 const size_t trace_id = trace_counter++;
273 if (!Dart_IsClosure(callback_handle)) {
279 const auto& task_runners = dart_state->GetTaskRunners();
281 if (state_->frameCount_ == 0) {
282 std::string decode_error(
"Could not provide any frame.");
283 FML_LOG(ERROR) << decode_error;
285 [trace_id, decode_error = std::move(decode_error),
286 callback = std::make_unique<tonic::DartPersistentValue>(
295 [
callback = std::make_unique<tonic::DartPersistentValue>(
297 weak_state = std::weak_ptr<MultiFrameCodec::State>(state_), trace_id,
298 ui_task_runner = task_runners.GetUITaskRunner(),
299 io_manager = dart_state->GetIOManager()]()
mutable {
300 auto state = weak_state.lock();
302 ui_task_runner->PostTask(fml::MakeCopyable(
303 [callback = std::move(callback)]() { callback->Clear(); }));
306 state->GetNextFrameAndInvokeCallback(
307 std::move(
callback), ui_task_runner,
308 io_manager->GetResourceContext(), io_manager->GetSkiaUnrefQueue(),
309 io_manager->GetIsGpuDisabledSyncSwitch(), trace_id,
310 io_manager->GetImpellerContext());
319int MultiFrameCodec::frameCount()
const {
320 return state_->frameCount_;
323int MultiFrameCodec::repetitionCount()
const {
324 return state_->repetitionCount_;
static fml::RefPtr< CanvasImage > Create()
static sk_sp< DlImageGPU > Make(SkiaGPUObject< SkImage > image)
static std::optional< impeller::PixelFormat > ToPixelFormat(SkColorType type)
static void UploadTextureToPrivate(ImageResult result, const std::shared_ptr< impeller::Context > &context, const std::shared_ptr< impeller::DeviceBuffer > &buffer, const ImageInfo &image_info, const std::optional< SkImageInfo > &resize_info, const std::shared_ptr< const fml::SyncSwitch > &gpu_disabled_switch)
Create a device private texture from the provided host buffer.
static std::pair< sk_sp< DlImage >, std::string > UploadTextureToStorage(const std::shared_ptr< impeller::Context > &context, std::shared_ptr< SkBitmap > bitmap)
Create a texture from the provided bitmap.
The minimal interface necessary for defining a decoder that can be used for both single and multi-fra...
~MultiFrameCodec() override
MultiFrameCodec(std::shared_ptr< ImageGenerator > generator)
Dart_Handle getNextFrame(Dart_Handle args) override
static UIDartState * Current()
static DartState * Current()
FlutterVulkanImage * image
G_BEGIN_DECLS GBytes * message
FlutterDesktopBinaryReply callback
#define FML_DLOG(severity)
#define FML_LOG(severity)
std::shared_ptr< SkBitmap > bitmap
static void InvokeNextFrameCallback(const fml::RefPtr< CanvasImage > &image, int duration, const std::string &decode_error, std::unique_ptr< tonic::DartPersistentValue > callback, size_t trace_id)
internal::CopyableLambda< T > MakeCopyable(T lambda)
Dart_Handle ToDart(const T &object)
Dart_Handle DartInvoke(Dart_Handle closure, std::initializer_list< Dart_Handle > args)
Represents the 2 code paths available when calling |SyncSwitchExecute|.