mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
* Revert "[fuchsia] Fix alignment of Fuchsia/non-Fuchsia tracing (#9289)" This reverts commit f80ac5f571479053b134e60bca77603269b2ce2a. * Revert "Align fuchsia and non-fuchsia tracing (#9199)" This reverts commit 78265484623037c6544dfd5380367bca29fa27b0.
657 lines
23 KiB
C++
657 lines
23 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "flutter/lib/ui/painting/codec.h"
|
|
|
|
#include "flutter/common/task_runners.h"
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/fml/make_copyable.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
#include "flutter/lib/ui/painting/frame_info.h"
|
|
#include "third_party/skia/include/codec/SkCodec.h"
|
|
#include "third_party/skia/include/core/SkPixelRef.h"
|
|
#include "third_party/tonic/dart_binding_macros.h"
|
|
#include "third_party/tonic/dart_library_natives.h"
|
|
#include "third_party/tonic/dart_state.h"
|
|
#include "third_party/tonic/logging/dart_invoke.h"
|
|
#include "third_party/tonic/typed_data/typed_list.h"
|
|
|
|
using tonic::DartInvoke;
|
|
using tonic::DartPersistentValue;
|
|
using tonic::ToDart;
|
|
|
|
namespace flutter {
|
|
|
|
namespace {
|
|
|
|
static constexpr const char* kInitCodecTraceTag = "InitCodec";
|
|
static constexpr const char* kCodecNextFrameTraceTag = "CodecNextFrame";
|
|
|
|
// This needs to be kept in sync with _kDoNotResizeDimension in painting.dart
|
|
const int kDoNotResizeDimension = -1;
|
|
|
|
// This must be kept in sync with the enum in painting.dart
|
|
enum PixelFormat {
|
|
kRGBA8888,
|
|
kBGRA8888,
|
|
};
|
|
|
|
struct ImageInfo {
|
|
SkImageInfo sk_info;
|
|
size_t row_bytes;
|
|
};
|
|
|
|
static void InvokeCodecCallback(fml::RefPtr<Codec> codec,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
size_t trace_id) {
|
|
std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
|
|
if (!dart_state) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
return;
|
|
}
|
|
tonic::DartState::Scope scope(dart_state);
|
|
if (!codec) {
|
|
DartInvoke(callback->value(), {Dart_Null()});
|
|
} else {
|
|
DartInvoke(callback->value(), {ToDart(codec)});
|
|
}
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
}
|
|
|
|
static sk_sp<SkImage> DecodeImage(fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkData> buffer,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("flutter", "DecodeImage");
|
|
|
|
if (buffer == nullptr || buffer->isEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (context) {
|
|
// This indicates that we do not want a "linear blending" decode.
|
|
sk_sp<SkColorSpace> dstColorSpace = nullptr;
|
|
return SkImage::MakeCrossContextFromEncoded(
|
|
context.get(), std::move(buffer), true, dstColorSpace.get(), true);
|
|
} else {
|
|
// Defer decoding until time of draw later on the GPU thread. Can happen
|
|
// when GL operations are currently forbidden such as in the background
|
|
// on iOS.
|
|
return SkImage::MakeFromEncoded(std::move(buffer));
|
|
}
|
|
}
|
|
|
|
// Returns true if the image needs to be resized.
|
|
//
|
|
// newWidth and newHeight will reflect the dimensions that the image should
|
|
// be scaled to.
|
|
//
|
|
// The targetWidth and targetHeight arguments specify the size of the output
|
|
// image, in image pixels. If they are not equal to the intrinsic dimensions of
|
|
// the image, then the image will be scaled after being decoded. If exactly one
|
|
// of these two arguments is equal to kDoNotResizeDimension, then the aspect
|
|
// ratio will be maintained while forcing the image to match the other given
|
|
// dimension. If both are equal to kDoNotResizeDimension, then the image
|
|
// maintains its real size.
|
|
static bool needsResize(const int currentWidth,
|
|
const int currentHeight,
|
|
const int targetWidth,
|
|
const int targetHeight,
|
|
int& newWidth,
|
|
int& newHeight) {
|
|
newWidth = currentWidth;
|
|
newHeight = currentHeight;
|
|
if (targetWidth == kDoNotResizeDimension &&
|
|
targetHeight == kDoNotResizeDimension) {
|
|
return false;
|
|
}
|
|
|
|
if (currentWidth == targetWidth && currentHeight == targetHeight) {
|
|
return false;
|
|
}
|
|
|
|
if (targetWidth == kDoNotResizeDimension) {
|
|
newHeight = targetHeight;
|
|
const double aspectRatio = (double)currentWidth / currentHeight;
|
|
newWidth = round(aspectRatio * newHeight);
|
|
return true;
|
|
} else if (targetHeight == kDoNotResizeDimension) {
|
|
newWidth = targetWidth;
|
|
const double invAspectRatio = (double)currentHeight / currentWidth;
|
|
newHeight = round(invAspectRatio * newWidth);
|
|
return true;
|
|
} else {
|
|
newWidth = targetWidth;
|
|
newHeight = targetHeight;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static sk_sp<SkImage> ResizeImageToExactSize(fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkImage> image,
|
|
SkImageInfo scaledImageInfo) {
|
|
if (image == nullptr || !image.get()) {
|
|
FML_LOG(ERROR) << "Failed to decode image.";
|
|
return nullptr;
|
|
}
|
|
|
|
SkBitmap bitmap = SkBitmap();
|
|
if (!bitmap.tryAllocPixels(scaledImageInfo)) {
|
|
FML_LOG(ERROR) << "Failed to allocate bitmap.";
|
|
return nullptr;
|
|
}
|
|
|
|
if (!image->scalePixels(bitmap.pixmap(), kLow_SkFilterQuality)) {
|
|
FML_LOG(ERROR) << "Failed to scale pixels.";
|
|
return nullptr;
|
|
}
|
|
|
|
// This indicates that we do not want a "linear blending" decode.
|
|
sk_sp<SkColorSpace> dstColorSpace = nullptr;
|
|
GrContext* grContext = context ? context.get() : nullptr;
|
|
return SkImage::MakeCrossContextFromPixmap(grContext, bitmap.pixmap(), true,
|
|
dstColorSpace.get(), true);
|
|
}
|
|
|
|
static sk_sp<SkImage> DecodeAndResizeImageToExactSize(
|
|
fml::WeakPtr<GrContext> context,
|
|
SkImageInfo scaledImageInfo,
|
|
sk_sp<SkData> buffer,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("flutter", "DecodeAndResizeImageToExactSize");
|
|
|
|
// Do not create a cross context image here, since it can not be resized.
|
|
sk_sp<SkImage> image = SkImage::MakeFromEncoded(std::move(buffer));
|
|
return ResizeImageToExactSize(context, image, scaledImageInfo);
|
|
}
|
|
|
|
static sk_sp<SkImage> DecodeAndResizeImage(fml::WeakPtr<GrContext> context,
|
|
std::unique_ptr<SkCodec>& skCodec,
|
|
sk_sp<SkData> buffer,
|
|
const int targetWidth,
|
|
const int targetHeight,
|
|
size_t trace_id) {
|
|
const SkImageInfo imageInfo = skCodec->getInfo();
|
|
|
|
const int width = imageInfo.width();
|
|
const int height = imageInfo.height();
|
|
|
|
int newWidth, newHeight;
|
|
if (needsResize(width, height, targetWidth, targetHeight, newWidth,
|
|
newHeight)) {
|
|
return DecodeAndResizeImageToExactSize(
|
|
context, imageInfo.makeWH(newWidth, newHeight), buffer, trace_id);
|
|
} else {
|
|
return DecodeImage(context, buffer, trace_id);
|
|
}
|
|
}
|
|
|
|
fml::RefPtr<Codec> InitCodec(fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkData> buffer,
|
|
fml::RefPtr<flutter::SkiaUnrefQueue> unref_queue,
|
|
const int targetWidth,
|
|
const int targetHeight,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("blink", "InitCodec");
|
|
|
|
if (buffer == nullptr || buffer->isEmpty()) {
|
|
FML_LOG(ERROR) << "InitCodec failed - buffer was empty ";
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<SkCodec> skCodec = SkCodec::MakeFromData(buffer);
|
|
if (!skCodec) {
|
|
FML_LOG(ERROR) << "Failed decoding image. Data is either invalid, or it is "
|
|
"encoded using an unsupported format.";
|
|
return nullptr;
|
|
}
|
|
if (skCodec->getFrameCount() > 1) {
|
|
return fml::MakeRefCounted<MultiFrameCodec>(std::move(skCodec));
|
|
}
|
|
|
|
auto skImage = DecodeAndResizeImage(context, skCodec, buffer, targetWidth,
|
|
targetHeight, trace_id);
|
|
FML_DCHECK(skImage) << "Unable to resize the image to (w, h): " << targetWidth
|
|
<< ", " << targetHeight << ".";
|
|
if (!skImage) {
|
|
return nullptr;
|
|
}
|
|
auto image = CanvasImage::Create();
|
|
image->set_image({skImage, unref_queue});
|
|
auto frameInfo = fml::MakeRefCounted<FrameInfo>(std::move(image), 0);
|
|
return fml::MakeRefCounted<SingleFrameCodec>(std::move(frameInfo));
|
|
}
|
|
|
|
fml::RefPtr<Codec> InitCodecUncompressed(
|
|
fml::WeakPtr<GrContext> context,
|
|
sk_sp<SkData> buffer,
|
|
ImageInfo image_info,
|
|
fml::RefPtr<flutter::SkiaUnrefQueue> unref_queue,
|
|
int targetWidth,
|
|
int targetHeight,
|
|
size_t trace_id) {
|
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
|
TRACE_EVENT0("blink", "InitCodecUncompressed");
|
|
|
|
if (buffer == nullptr || buffer->isEmpty()) {
|
|
FML_LOG(ERROR) << "InitCodecUncompressed failed - buffer was empty";
|
|
return nullptr;
|
|
}
|
|
|
|
sk_sp<SkImage> skImage;
|
|
int newWidth, newHeight;
|
|
if (needsResize(image_info.sk_info.width(), image_info.sk_info.height(),
|
|
targetWidth, targetHeight, newWidth, newHeight)) {
|
|
auto imageToResize = SkImage::MakeRasterData(
|
|
image_info.sk_info, std::move(buffer), image_info.row_bytes);
|
|
skImage = ResizeImageToExactSize(
|
|
context, imageToResize, image_info.sk_info.makeWH(newWidth, newHeight));
|
|
} else if (context) {
|
|
SkPixmap pixmap(image_info.sk_info, buffer->data(), image_info.row_bytes);
|
|
skImage = SkImage::MakeCrossContextFromPixmap(context.get(), pixmap, true,
|
|
nullptr, true);
|
|
} else {
|
|
skImage = SkImage::MakeRasterData(image_info.sk_info, std::move(buffer),
|
|
image_info.row_bytes);
|
|
}
|
|
|
|
auto image = CanvasImage::Create();
|
|
image->set_image({skImage, unref_queue});
|
|
auto frameInfo = fml::MakeRefCounted<FrameInfo>(std::move(image), 0);
|
|
return fml::MakeRefCounted<SingleFrameCodec>(std::move(frameInfo));
|
|
}
|
|
|
|
void InitCodecAndInvokeCodecCallback(
|
|
fml::RefPtr<fml::TaskRunner> ui_task_runner,
|
|
fml::WeakPtr<GrContext> context,
|
|
fml::RefPtr<flutter::SkiaUnrefQueue> unref_queue,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
sk_sp<SkData> buffer,
|
|
std::unique_ptr<ImageInfo> image_info,
|
|
const int targetWidth,
|
|
const int targetHeight,
|
|
size_t trace_id) {
|
|
fml::RefPtr<Codec> codec;
|
|
if (image_info) {
|
|
codec = InitCodecUncompressed(context, std::move(buffer), *image_info,
|
|
std::move(unref_queue), targetWidth,
|
|
targetHeight, trace_id);
|
|
} else {
|
|
codec = InitCodec(context, std::move(buffer), std::move(unref_queue),
|
|
targetWidth, targetHeight, trace_id);
|
|
}
|
|
ui_task_runner->PostTask(
|
|
fml::MakeCopyable([callback = std::move(callback),
|
|
codec = std::move(codec), trace_id]() mutable {
|
|
InvokeCodecCallback(std::move(codec), std::move(callback), trace_id);
|
|
}));
|
|
}
|
|
|
|
bool ConvertImageInfo(Dart_Handle image_info_handle,
|
|
Dart_NativeArguments args,
|
|
ImageInfo* image_info) {
|
|
Dart_Handle width_handle = Dart_GetField(image_info_handle, ToDart("width"));
|
|
if (!Dart_IsInteger(width_handle)) {
|
|
Dart_SetReturnValue(args, ToDart("ImageInfo.width must be an integer"));
|
|
return false;
|
|
}
|
|
Dart_Handle height_handle =
|
|
Dart_GetField(image_info_handle, ToDart("height"));
|
|
if (!Dart_IsInteger(height_handle)) {
|
|
Dart_SetReturnValue(args, ToDart("ImageInfo.height must be an integer"));
|
|
return false;
|
|
}
|
|
Dart_Handle format_handle =
|
|
Dart_GetField(image_info_handle, ToDart("format"));
|
|
if (!Dart_IsInteger(format_handle)) {
|
|
Dart_SetReturnValue(args, ToDart("ImageInfo.format must be an integer"));
|
|
return false;
|
|
}
|
|
Dart_Handle row_bytes_handle =
|
|
Dart_GetField(image_info_handle, ToDart("rowBytes"));
|
|
if (!Dart_IsInteger(row_bytes_handle)) {
|
|
Dart_SetReturnValue(args, ToDart("ImageInfo.rowBytes must be an integer"));
|
|
return false;
|
|
}
|
|
|
|
PixelFormat pixel_format = static_cast<PixelFormat>(
|
|
tonic::DartConverter<int>::FromDart(format_handle));
|
|
SkColorType color_type = kUnknown_SkColorType;
|
|
switch (pixel_format) {
|
|
case kRGBA8888:
|
|
color_type = kRGBA_8888_SkColorType;
|
|
break;
|
|
case kBGRA8888:
|
|
color_type = kBGRA_8888_SkColorType;
|
|
break;
|
|
}
|
|
if (color_type == kUnknown_SkColorType) {
|
|
Dart_SetReturnValue(args, ToDart("Invalid pixel format"));
|
|
return false;
|
|
}
|
|
|
|
int width = tonic::DartConverter<int>::FromDart(width_handle);
|
|
if (width <= 0) {
|
|
Dart_SetReturnValue(args, ToDart("width must be greater than zero"));
|
|
return false;
|
|
}
|
|
int height = tonic::DartConverter<int>::FromDart(height_handle);
|
|
if (height <= 0) {
|
|
Dart_SetReturnValue(args, ToDart("height must be greater than zero"));
|
|
return false;
|
|
}
|
|
image_info->sk_info =
|
|
SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
|
|
image_info->row_bytes =
|
|
tonic::DartConverter<size_t>::FromDart(row_bytes_handle);
|
|
|
|
if (image_info->row_bytes < image_info->sk_info.minRowBytes()) {
|
|
Dart_SetReturnValue(
|
|
args, ToDart("rowBytes does not match the width of the image"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void InstantiateImageCodec(Dart_NativeArguments args) {
|
|
static size_t trace_counter = 1;
|
|
const size_t trace_id = trace_counter++;
|
|
TRACE_FLOW_BEGIN("flutter", kInitCodecTraceTag, trace_id);
|
|
|
|
Dart_Handle callback_handle = Dart_GetNativeArgument(args, 1);
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
Dart_SetReturnValue(args, ToDart("Callback must be a function"));
|
|
return;
|
|
}
|
|
|
|
Dart_Handle image_info_handle = Dart_GetNativeArgument(args, 2);
|
|
std::unique_ptr<ImageInfo> image_info;
|
|
if (!Dart_IsNull(image_info_handle)) {
|
|
image_info = std::make_unique<ImageInfo>();
|
|
if (!ConvertImageInfo(image_info_handle, args, image_info.get())) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Dart_Handle exception = nullptr;
|
|
tonic::Uint8List list =
|
|
tonic::DartConverter<tonic::Uint8List>::FromArguments(args, 0, exception);
|
|
if (exception) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
Dart_SetReturnValue(args, exception);
|
|
return;
|
|
}
|
|
|
|
if (image_info) {
|
|
int expected_size = image_info->row_bytes * image_info->sk_info.height();
|
|
if (list.num_elements() < expected_size) {
|
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
|
list.Release();
|
|
Dart_SetReturnValue(
|
|
args, ToDart("Pixel buffer size does not match image size"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const int targetWidth =
|
|
tonic::DartConverter<int>::FromDart(Dart_GetNativeArgument(args, 3));
|
|
const int targetHeight =
|
|
tonic::DartConverter<int>::FromDart(Dart_GetNativeArgument(args, 4));
|
|
|
|
auto buffer = SkData::MakeWithCopy(list.data(), list.num_elements());
|
|
|
|
auto* dart_state = UIDartState::Current();
|
|
|
|
const auto& task_runners = dart_state->GetTaskRunners();
|
|
task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
|
|
[callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle),
|
|
buffer = std::move(buffer), trace_id, image_info = std::move(image_info),
|
|
ui_task_runner = task_runners.GetUITaskRunner(),
|
|
context = dart_state->GetResourceContext(),
|
|
queue = UIDartState::Current()->GetSkiaUnrefQueue(), targetWidth,
|
|
targetHeight]() mutable {
|
|
InitCodecAndInvokeCodecCallback(
|
|
std::move(ui_task_runner), context, std::move(queue),
|
|
std::move(callback), std::move(buffer), std::move(image_info),
|
|
targetWidth, targetHeight, trace_id);
|
|
}));
|
|
}
|
|
|
|
bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
|
|
SkPixmap srcPM;
|
|
if (!src.peekPixels(&srcPM)) {
|
|
return false;
|
|
}
|
|
|
|
SkBitmap tmpDst;
|
|
SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
|
|
if (!tmpDst.setInfo(dstInfo)) {
|
|
return false;
|
|
}
|
|
|
|
if (!tmpDst.tryAllocPixels()) {
|
|
return false;
|
|
}
|
|
|
|
SkPixmap dstPM;
|
|
if (!tmpDst.peekPixels(&dstPM)) {
|
|
return false;
|
|
}
|
|
|
|
if (!srcPM.readPixels(dstPM)) {
|
|
return false;
|
|
}
|
|
|
|
dst->swap(tmpDst);
|
|
return true;
|
|
}
|
|
|
|
void InvokeNextFrameCallback(fml::RefPtr<FrameInfo> frameInfo,
|
|
std::unique_ptr<DartPersistentValue> callback,
|
|
size_t trace_id) {
|
|
std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
|
|
if (!dart_state) {
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
return;
|
|
}
|
|
tonic::DartState::Scope scope(dart_state);
|
|
if (!frameInfo) {
|
|
DartInvoke(callback->value(), {Dart_Null()});
|
|
} else {
|
|
DartInvoke(callback->value(), {ToDart(frameInfo)});
|
|
}
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
IMPLEMENT_WRAPPERTYPEINFO(ui, Codec);
|
|
|
|
#define FOR_EACH_BINDING(V) \
|
|
V(Codec, getNextFrame) \
|
|
V(Codec, frameCount) \
|
|
V(Codec, repetitionCount) \
|
|
V(Codec, dispose)
|
|
|
|
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
|
|
|
|
void Codec::dispose() {
|
|
ClearDartWrapper();
|
|
}
|
|
|
|
MultiFrameCodec::MultiFrameCodec(std::unique_ptr<SkCodec> codec)
|
|
: codec_(std::move(codec)),
|
|
frameCount_(codec_->getFrameCount()),
|
|
repetitionCount_(codec_->getRepetitionCount()),
|
|
nextFrameIndex_(0) {}
|
|
|
|
MultiFrameCodec::~MultiFrameCodec() {}
|
|
|
|
int MultiFrameCodec::frameCount() const {
|
|
return frameCount_;
|
|
}
|
|
|
|
int MultiFrameCodec::repetitionCount() const {
|
|
return repetitionCount_;
|
|
}
|
|
|
|
sk_sp<SkImage> MultiFrameCodec::GetNextFrameImage(
|
|
fml::WeakPtr<GrContext> resourceContext) {
|
|
SkBitmap bitmap = SkBitmap();
|
|
SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType);
|
|
if (info.alphaType() == kUnpremul_SkAlphaType) {
|
|
info = info.makeAlphaType(kPremul_SkAlphaType);
|
|
}
|
|
bitmap.allocPixels(info);
|
|
|
|
SkCodec::Options options;
|
|
options.fFrameIndex = nextFrameIndex_;
|
|
SkCodec::FrameInfo frameInfo;
|
|
codec_->getFrameInfo(nextFrameIndex_, &frameInfo);
|
|
const int requiredFrameIndex = frameInfo.fRequiredFrame;
|
|
if (requiredFrameIndex != SkCodec::kNoFrame) {
|
|
if (lastRequiredFrame_ == nullptr) {
|
|
FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame "
|
|
<< requiredFrameIndex
|
|
<< " and no required frames are cached.";
|
|
return NULL;
|
|
} else if (lastRequiredFrameIndex_ != requiredFrameIndex) {
|
|
FML_DLOG(INFO) << "Required frame " << requiredFrameIndex
|
|
<< " is not cached. Using " << lastRequiredFrameIndex_
|
|
<< " instead";
|
|
}
|
|
|
|
if (lastRequiredFrame_->getPixels() &&
|
|
copy_to(&bitmap, lastRequiredFrame_->colorType(),
|
|
*lastRequiredFrame_)) {
|
|
options.fPriorFrame = requiredFrameIndex;
|
|
}
|
|
}
|
|
|
|
if (SkCodec::kSuccess != codec_->getPixels(info, bitmap.getPixels(),
|
|
bitmap.rowBytes(), &options)) {
|
|
FML_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_;
|
|
return NULL;
|
|
}
|
|
|
|
// Hold onto this if we need it to decode future frames.
|
|
if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kKeep) {
|
|
lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
|
|
lastRequiredFrameIndex_ = nextFrameIndex_;
|
|
}
|
|
|
|
if (resourceContext) {
|
|
SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
|
|
bitmap.pixelRef()->rowBytes());
|
|
// This indicates that we do not want a "linear blending" decode.
|
|
sk_sp<SkColorSpace> dstColorSpace = nullptr;
|
|
return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap,
|
|
true, dstColorSpace.get());
|
|
} else {
|
|
// Defer decoding until time of draw later on the GPU thread. Can happen
|
|
// when GL operations are currently forbidden such as in the background
|
|
// on iOS.
|
|
return SkImage::MakeFromBitmap(bitmap);
|
|
}
|
|
}
|
|
|
|
void MultiFrameCodec::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) {
|
|
fml::RefPtr<FrameInfo> frameInfo = NULL;
|
|
sk_sp<SkImage> skImage = GetNextFrameImage(resourceContext);
|
|
if (skImage) {
|
|
fml::RefPtr<CanvasImage> image = CanvasImage::Create();
|
|
image->set_image({skImage, std::move(unref_queue)});
|
|
SkCodec::FrameInfo skFrameInfo;
|
|
codec_->getFrameInfo(nextFrameIndex_, &skFrameInfo);
|
|
frameInfo =
|
|
fml::MakeRefCounted<FrameInfo>(std::move(image), skFrameInfo.fDuration);
|
|
}
|
|
nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_;
|
|
|
|
ui_task_runner->PostTask(fml::MakeCopyable(
|
|
[callback = std::move(callback), frameInfo, trace_id]() mutable {
|
|
InvokeNextFrameCallback(frameInfo, std::move(callback), trace_id);
|
|
}));
|
|
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
}
|
|
|
|
Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) {
|
|
static size_t trace_counter = 1;
|
|
const size_t trace_id = trace_counter++;
|
|
TRACE_FLOW_BEGIN("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
TRACE_FLOW_END("flutter", kCodecNextFrameTraceTag, trace_id);
|
|
return ToDart("Callback must be a function");
|
|
}
|
|
|
|
auto* dart_state = UIDartState::Current();
|
|
|
|
const auto& task_runners = dart_state->GetTaskRunners();
|
|
|
|
task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
|
|
[callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle),
|
|
this, trace_id, ui_task_runner = task_runners.GetUITaskRunner(),
|
|
queue = UIDartState::Current()->GetSkiaUnrefQueue(),
|
|
context = dart_state->GetResourceContext()]() mutable {
|
|
GetNextFrameAndInvokeCallback(std::move(callback),
|
|
std::move(ui_task_runner), context,
|
|
std::move(queue), trace_id);
|
|
}));
|
|
|
|
return Dart_Null();
|
|
}
|
|
|
|
SingleFrameCodec::SingleFrameCodec(fml::RefPtr<FrameInfo> frame)
|
|
: frame_(std::move(frame)) {}
|
|
|
|
SingleFrameCodec::~SingleFrameCodec() {}
|
|
|
|
int SingleFrameCodec::frameCount() const {
|
|
return 1;
|
|
}
|
|
|
|
int SingleFrameCodec::repetitionCount() const {
|
|
return 0;
|
|
}
|
|
|
|
Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) {
|
|
if (!Dart_IsClosure(callback_handle)) {
|
|
return ToDart("Callback must be a function");
|
|
}
|
|
|
|
auto callback = std::make_unique<DartPersistentValue>(
|
|
tonic::DartState::Current(), callback_handle);
|
|
std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
|
|
if (!dart_state) {
|
|
return ToDart("Invalid dart state");
|
|
}
|
|
|
|
tonic::DartState::Scope scope(dart_state);
|
|
DartInvoke(callback->value(), {ToDart(frame_)});
|
|
return Dart_Null();
|
|
}
|
|
|
|
void Codec::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
|
natives->Register({
|
|
{"instantiateImageCodec", InstantiateImageCodec, 5, true},
|
|
});
|
|
natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
|
|
}
|
|
|
|
} // namespace flutter
|