Initial implementation of ui.Codec (a wrapper for SkCodec) (flutter/engine#4318)
This is the first step to support animated GIFs: flutter/flutter#204 TBD in following CLs: * Implement Codec.getNextFrame. * Add Framework side support to run animations.
This commit is contained in:
@@ -16,6 +16,8 @@ source_set("ui") {
|
|||||||
"dart_ui.h",
|
"dart_ui.h",
|
||||||
"painting/canvas.cc",
|
"painting/canvas.cc",
|
||||||
"painting/canvas.h",
|
"painting/canvas.h",
|
||||||
|
"painting/codec.cc",
|
||||||
|
"painting/codec.h",
|
||||||
"painting/gradient.cc",
|
"painting/gradient.cc",
|
||||||
"painting/gradient.h",
|
"painting/gradient.h",
|
||||||
"painting/image.cc",
|
"painting/image.cc",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "flutter/lib/ui/compositing/scene_builder.h"
|
#include "flutter/lib/ui/compositing/scene_builder.h"
|
||||||
#include "flutter/lib/ui/dart_runtime_hooks.h"
|
#include "flutter/lib/ui/dart_runtime_hooks.h"
|
||||||
#include "flutter/lib/ui/painting/canvas.h"
|
#include "flutter/lib/ui/painting/canvas.h"
|
||||||
|
#include "flutter/lib/ui/painting/codec.h"
|
||||||
#include "flutter/lib/ui/painting/gradient.h"
|
#include "flutter/lib/ui/painting/gradient.h"
|
||||||
#include "flutter/lib/ui/painting/image.h"
|
#include "flutter/lib/ui/painting/image.h"
|
||||||
#include "flutter/lib/ui/painting/image_decoding.h"
|
#include "flutter/lib/ui/painting/image_decoding.h"
|
||||||
@@ -53,6 +54,7 @@ void DartUI::InitForGlobal() {
|
|||||||
CanvasGradient::RegisterNatives(g_natives);
|
CanvasGradient::RegisterNatives(g_natives);
|
||||||
CanvasImage::RegisterNatives(g_natives);
|
CanvasImage::RegisterNatives(g_natives);
|
||||||
CanvasPath::RegisterNatives(g_natives);
|
CanvasPath::RegisterNatives(g_natives);
|
||||||
|
Codec::RegisterNatives(g_natives);
|
||||||
DartRuntimeHooks::RegisterNatives(g_natives);
|
DartRuntimeHooks::RegisterNatives(g_natives);
|
||||||
ImageDecoding::RegisterNatives(g_natives);
|
ImageDecoding::RegisterNatives(g_natives);
|
||||||
ImageFilter::RegisterNatives(g_natives);
|
ImageFilter::RegisterNatives(g_natives);
|
||||||
|
|||||||
@@ -868,6 +868,45 @@ abstract class Image extends NativeFieldWrapperClass2 {
|
|||||||
/// Callback signature for [decodeImageFromList].
|
/// Callback signature for [decodeImageFromList].
|
||||||
typedef void ImageDecoderCallback(Image result);
|
typedef void ImageDecoderCallback(Image result);
|
||||||
|
|
||||||
|
/// Information for a single animation frame.
|
||||||
|
///
|
||||||
|
/// Obtain a FrameInfo with [Codec.getNextFrame].
|
||||||
|
abstract class FrameInfo extends NativeFieldWrapperClass2 {
|
||||||
|
// The duration this frame should be shown.
|
||||||
|
int get durationMillis native "FrameInfo_durationMillis";
|
||||||
|
|
||||||
|
// The Image object for this frame.
|
||||||
|
Image get image native "FrameInfo_image";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to an image codec.
|
||||||
|
abstract class Codec extends NativeFieldWrapperClass2 {
|
||||||
|
/// Number of frames in this image.
|
||||||
|
int get frameCount native "Codec_frameCount";
|
||||||
|
|
||||||
|
/// Number of times to repeat the animation.
|
||||||
|
///
|
||||||
|
/// * 0 when the animation should be played once.
|
||||||
|
/// * -1 for infinity repetitions.
|
||||||
|
int get repetitionCount native "Codec_repetitionCount";
|
||||||
|
|
||||||
|
/// Returns the next animation frame.
|
||||||
|
///
|
||||||
|
/// Wraps back to the first frame after returning the last frame.
|
||||||
|
FrameInfo getNextFrame() native "Codec_getNextFrame";
|
||||||
|
|
||||||
|
/// Release the resources used by this object. The object is no longer usable
|
||||||
|
/// after this method is called.
|
||||||
|
void dispose() native "Codec_dispose";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback signature for [imageCodecFromList].
|
||||||
|
typedef void CodecCallback(Codec result);
|
||||||
|
|
||||||
|
/// Instatiates a [Codec] object for an image binary data.
|
||||||
|
void instantiateImageCodec(Uint8List list, CodecCallback callback)
|
||||||
|
native "instantiateImageCodec";
|
||||||
|
|
||||||
/// Convert an image file from a byte array into an [Image] object.
|
/// Convert an image file from a byte array into an [Image] object.
|
||||||
void decodeImageFromList(Uint8List list, ImageDecoderCallback callback)
|
void decodeImageFromList(Uint8List list, ImageDecoderCallback callback)
|
||||||
native "decodeImageFromList";
|
native "decodeImageFromList";
|
||||||
|
|||||||
136
engine/src/flutter/lib/ui/painting/codec.cc
Normal file
136
engine/src/flutter/lib/ui/painting/codec.cc
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2017 The Chromium 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/threads.h"
|
||||||
|
#include "flutter/glue/trace_event.h"
|
||||||
|
#include "lib/fxl/functional/make_copyable.h"
|
||||||
|
#include "lib/tonic/dart_binding_macros.h"
|
||||||
|
#include "lib/tonic/dart_library_natives.h"
|
||||||
|
#include "lib/tonic/dart_state.h"
|
||||||
|
#include "lib/tonic/logging/dart_invoke.h"
|
||||||
|
#include "lib/tonic/typed_data/uint8_list.h"
|
||||||
|
#include "third_party/skia/include/codec/SkCodec.h"
|
||||||
|
|
||||||
|
using tonic::DartInvoke;
|
||||||
|
using tonic::DartPersistentValue;
|
||||||
|
using tonic::ToDart;
|
||||||
|
|
||||||
|
namespace blink {
|
||||||
|
|
||||||
|
IMPLEMENT_WRAPPERTYPEINFO(ui, Codec);
|
||||||
|
|
||||||
|
#define FOR_EACH_BINDING(V) \
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static constexpr const char* kInitCodecTraceTag = "InitCodec";
|
||||||
|
|
||||||
|
std::unique_ptr<SkCodec> InitCodec(sk_sp<SkData> buffer, size_t trace_id) {
|
||||||
|
TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id);
|
||||||
|
TRACE_EVENT0("blink", "InitCodec");
|
||||||
|
|
||||||
|
if (buffer == nullptr || buffer->isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SkCodec::MakeFromData(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvokeCodecCallback(std::unique_ptr<SkCodec> codec,
|
||||||
|
int frameCount,
|
||||||
|
int repetitionCount,
|
||||||
|
std::unique_ptr<DartPersistentValue> callback,
|
||||||
|
size_t trace_id) {
|
||||||
|
tonic::DartState* dart_state = callback->dart_state().get();
|
||||||
|
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 {
|
||||||
|
fxl::RefPtr<Codec> resultCodec = MultiFrameCodec::Create(std::move(codec));
|
||||||
|
DartInvoke(callback->value(), {ToDart(resultCodec)});
|
||||||
|
}
|
||||||
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitCodecAndInvokeCodecCallback(
|
||||||
|
std::unique_ptr<DartPersistentValue> callback,
|
||||||
|
sk_sp<SkData> buffer,
|
||||||
|
size_t trace_id) {
|
||||||
|
std::unique_ptr<SkCodec> codec = InitCodec(std::move(buffer), trace_id);
|
||||||
|
int frameCount = codec->getFrameCount();
|
||||||
|
int repetitionCount = codec->getRepetitionCount();
|
||||||
|
Threads::UI()->PostTask(fxl::MakeCopyable([
|
||||||
|
callback = std::move(callback), codec = std::move(codec), trace_id,
|
||||||
|
frameCount, repetitionCount
|
||||||
|
]() mutable {
|
||||||
|
InvokeCodecCallback(std::move(codec), frameCount, repetitionCount,
|
||||||
|
std::move(callback), trace_id);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 exception = nullptr;
|
||||||
|
|
||||||
|
tonic::Uint8List list =
|
||||||
|
tonic::DartConverter<tonic::Uint8List>::FromArguments(args, 0, exception);
|
||||||
|
if (exception) {
|
||||||
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
||||||
|
Dart_ThrowException(exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dart_Handle callback_handle = Dart_GetNativeArgument(args, 1);
|
||||||
|
if (!Dart_IsClosure(callback_handle)) {
|
||||||
|
TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id);
|
||||||
|
Dart_ThrowException(ToDart("Callback must be a function"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = SkData::MakeWithCopy(list.data(), list.num_elements());
|
||||||
|
|
||||||
|
Threads::IO()->PostTask(fxl::MakeCopyable([
|
||||||
|
callback = std::make_unique<DartPersistentValue>(
|
||||||
|
tonic::DartState::Current(), callback_handle),
|
||||||
|
buffer = std::move(buffer), trace_id
|
||||||
|
]() mutable {
|
||||||
|
InitCodecAndInvokeCodecCallback(std::move(callback), std::move(buffer),
|
||||||
|
trace_id);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Codec::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
||||||
|
natives->Register({
|
||||||
|
{"instantiateImageCodec", InstantiateImageCodec, 2, true},
|
||||||
|
});
|
||||||
|
natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blink
|
||||||
54
engine/src/flutter/lib/ui/painting/codec.h
Normal file
54
engine/src/flutter/lib/ui/painting/codec.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_LIB_UI_PAINTING_CODEC_H_
|
||||||
|
#define FLUTTER_LIB_UI_PAINTING_CODEC_H_
|
||||||
|
|
||||||
|
#include "lib/tonic/dart_wrappable.h"
|
||||||
|
#include "third_party/skia/include/codec/SkCodec.h"
|
||||||
|
|
||||||
|
namespace tonic {
|
||||||
|
class DartLibraryNatives;
|
||||||
|
} // namespace tonic
|
||||||
|
|
||||||
|
namespace blink {
|
||||||
|
|
||||||
|
// A handle to an SkCodec object.
|
||||||
|
//
|
||||||
|
// Doesn't mirror SkCodec's API but provides a simple sequential access API.
|
||||||
|
class Codec : public fxl::RefCountedThreadSafe<Codec>,
|
||||||
|
public tonic::DartWrappable {
|
||||||
|
DEFINE_WRAPPERTYPEINFO();
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual int frameCount() = 0;
|
||||||
|
virtual int repetitionCount() = 0;
|
||||||
|
void dispose();
|
||||||
|
|
||||||
|
static void RegisterNatives(tonic::DartLibraryNatives* natives);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MultiFrameCodec : public Codec {
|
||||||
|
FRIEND_MAKE_REF_COUNTED(MultiFrameCodec);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static fxl::RefPtr<MultiFrameCodec> Create(std::unique_ptr<SkCodec> codec) {
|
||||||
|
return fxl::MakeRefCounted<MultiFrameCodec>(std::move(codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
int frameCount() { return frameCount_; }
|
||||||
|
int repetitionCount() { return repetitionCount_; }
|
||||||
|
|
||||||
|
static void RegisterNatives(tonic::DartLibraryNatives* natives);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MultiFrameCodec(std::unique_ptr<SkCodec> codec);
|
||||||
|
|
||||||
|
const std::unique_ptr<SkCodec> codec_;
|
||||||
|
int frameCount_;
|
||||||
|
int repetitionCount_;
|
||||||
|
};
|
||||||
|
} // namespace blink
|
||||||
|
|
||||||
|
#endif // FLUTTER_LIB_UI_PAINTING_CODEC_H_
|
||||||
43
engine/src/flutter/testing/dart/codec_test.dart
Normal file
43
engine/src/flutter/testing/dart/codec_test.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
test('Animation metadata', () async {
|
||||||
|
Uint8List data = await _getSkiaResource('alphabetAnim.gif').readAsBytes();
|
||||||
|
Completer<ui.Codec> completer = new Completer<ui.Codec>();
|
||||||
|
ui.instantiateImageCodec(data, completer.complete);
|
||||||
|
ui.Codec codec = await completer.future;
|
||||||
|
expect(codec.frameCount, 13);
|
||||||
|
expect(codec.repetitionCount, 0);
|
||||||
|
codec.dispose();
|
||||||
|
|
||||||
|
data = await _getSkiaResource('test640x479.gif').readAsBytes();
|
||||||
|
completer = new Completer<ui.Codec>();
|
||||||
|
ui.instantiateImageCodec(data, completer.complete);
|
||||||
|
codec = await completer.future;
|
||||||
|
expect(codec.frameCount, 4);
|
||||||
|
expect(codec.repetitionCount, -1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a File handle to a file in the skia/resources directory.
|
||||||
|
File _getSkiaResource(String fileName) {
|
||||||
|
// As Platform.script is not working for flutter_tester
|
||||||
|
// (https://github.com/flutter/flutter/issues/12847), this is currently
|
||||||
|
// assuming the curent working directory is engine/src.
|
||||||
|
// This is fragile and should be changed once the Platform.script issue is
|
||||||
|
// resolved.
|
||||||
|
String assetPath =
|
||||||
|
path.join('third_party', 'skia', 'resources', fileName);
|
||||||
|
return new File(assetPath);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user