mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
Initial implementation of ui.Codec (a wrapper for SkCodec) (#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",
|
||||
"painting/canvas.cc",
|
||||
"painting/canvas.h",
|
||||
"painting/codec.cc",
|
||||
"painting/codec.h",
|
||||
"painting/gradient.cc",
|
||||
"painting/gradient.h",
|
||||
"painting/image.cc",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "flutter/lib/ui/compositing/scene_builder.h"
|
||||
#include "flutter/lib/ui/dart_runtime_hooks.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/image.h"
|
||||
#include "flutter/lib/ui/painting/image_decoding.h"
|
||||
@@ -53,6 +54,7 @@ void DartUI::InitForGlobal() {
|
||||
CanvasGradient::RegisterNatives(g_natives);
|
||||
CanvasImage::RegisterNatives(g_natives);
|
||||
CanvasPath::RegisterNatives(g_natives);
|
||||
Codec::RegisterNatives(g_natives);
|
||||
DartRuntimeHooks::RegisterNatives(g_natives);
|
||||
ImageDecoding::RegisterNatives(g_natives);
|
||||
ImageFilter::RegisterNatives(g_natives);
|
||||
|
||||
@@ -868,6 +868,45 @@ abstract class Image extends NativeFieldWrapperClass2 {
|
||||
/// Callback signature for [decodeImageFromList].
|
||||
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.
|
||||
void decodeImageFromList(Uint8List list, ImageDecoderCallback callback)
|
||||
native "decodeImageFromList";
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1137,6 +1137,8 @@ FILE: ../../../flutter/fml/trace_event.cc
|
||||
FILE: ../../../flutter/fml/trace_event.h
|
||||
FILE: ../../../flutter/lib/ui/compositing/scene_host.cc
|
||||
FILE: ../../../flutter/lib/ui/compositing/scene_host.h
|
||||
FILE: ../../../flutter/lib/ui/painting/codec.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/codec.h
|
||||
FILE: ../../../flutter/lib/ui/painting/utils.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/vertices.cc
|
||||
FILE: ../../../flutter/lib/ui/painting/vertices.h
|
||||
|
||||
Reference in New Issue
Block a user