您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

409 行
13 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using RSG;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using SchedulerBinding = Unity.UIWidgets.scheduler.SchedulerBinding;
namespace Unity.UIWidgets.painting {
public class ImageInfo : IEquatable<ImageInfo> {
public ImageInfo(Image image, float scale = 1.0f) {
D.assert(image != null);
this.image = image;
this.scale = scale;
}
public readonly Image image;
public readonly float scale;
public bool Equals(ImageInfo other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return Equals(image, other.image) && scale.Equals(other.scale);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != GetType()) {
return false;
}
return Equals((ImageInfo) obj);
}
public override int GetHashCode() {
unchecked {
return ((image != null ? image.GetHashCode() : 0) * 397) ^ scale.GetHashCode();
}
}
public static bool operator ==(ImageInfo left, ImageInfo right) {
return Equals(left, right);
}
public static bool operator !=(ImageInfo left, ImageInfo right) {
return !Equals(left, right);
}
public override string ToString() {
return $"{image} @ {scale}x";
}
}
public delegate void ImageListener(ImageInfo image, bool synchronousCall);
public delegate void ImageErrorListener(Exception exception);
class _ImageListenerPair {
public ImageListener listener;
public ImageErrorListener errorListener;
}
public class ImageStream : Diagnosticable {
public ImageStream() {
}
ImageStreamCompleter _completer;
public ImageStreamCompleter completer {
get { return _completer; }
}
List<_ImageListenerPair> _listeners;
public void setCompleter(ImageStreamCompleter value) {
D.assert(_completer == null);
_completer = value;
if (_listeners != null) {
var initialListeners = _listeners;
_listeners = null;
foreach (_ImageListenerPair listenerPair in initialListeners) {
_completer.addListener(
listenerPair.listener,
listenerPair.errorListener
);
}
}
}
public void addListener(ImageListener listener, ImageErrorListener onError = null) {
if (_completer != null) {
_completer.addListener(listener, onError);
return;
}
if (_listeners == null) {
_listeners = new List<_ImageListenerPair>();
}
_listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});
}
public void removeListener(ImageListener listener) {
if (_completer != null) {
_completer.removeListener(listener);
return;
}
D.assert(_listeners != null);
for (int i = 0; i < _listeners.Count; i++) {
if (_listeners[i].listener == listener) {
_listeners.RemoveAt(i);
break;
}
}
}
public object key {
get { return _completer != null ? (object) _completer : this; }
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new ObjectFlagProperty<ImageStreamCompleter>(
"completer",
_completer,
ifPresent: _completer?.toStringShort(),
ifNull: "unresolved"
));
properties.add(new ObjectFlagProperty<List<_ImageListenerPair>>(
"listeners",
_listeners,
ifPresent: $"{_listeners?.Count} listener{(_listeners?.Count == 1 ? "" : "s")}",
ifNull: "no listeners",
level: _completer != null ? DiagnosticLevel.hidden : DiagnosticLevel.info
));
_completer?.debugFillProperties(properties);
}
}
public abstract class ImageStreamCompleter : Diagnosticable {
internal readonly List<_ImageListenerPair> _listeners = new List<_ImageListenerPair>();
public ImageInfo currentImage;
public UIWidgetsErrorDetails currentError;
protected bool hasListeners {
get { return _listeners.isNotEmpty(); }
}
public virtual void addListener(ImageListener listener, ImageErrorListener onError = null) {
_listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});
if (currentImage != null) {
try {
listener(currentImage, true);
}
catch (Exception ex) {
reportError(
context: "by a synchronously-called image listener",
exception: ex
);
}
}
if (currentError != null && onError != null) {
try {
onError(currentError.exception);
}
catch (Exception ex) {
UIWidgetsError.reportError(
new UIWidgetsErrorDetails(
exception: ex,
library: "image resource service",
context: "when reporting an error to an image listener"
)
);
}
}
}
public virtual void removeListener(ImageListener listener) {
for (int i = 0; i < _listeners.Count; i++) {
if (_listeners[i].listener == listener) {
_listeners.RemoveAt(i);
break;
}
}
}
protected void setImage(ImageInfo image) {
currentImage = image;
if (_listeners.isEmpty()) {
return;
}
var localListeners = _listeners.Select(l => l.listener).ToList();
foreach (var listener in localListeners) {
try {
listener(image, false);
}
catch (Exception ex) {
reportError(
context: "by an image listener",
exception: ex
);
}
}
}
protected void reportError(
string context = null,
Exception exception = null,
InformationCollector informationCollector = null,
bool silent = false) {
currentError = new UIWidgetsErrorDetails(
exception: exception,
library: "image resource service",
context: context,
informationCollector: informationCollector,
silent: silent
);
var localErrorListeners = _listeners.Select(l => l.errorListener).Where(l => l != null).ToList();
if (localErrorListeners.isEmpty()) {
UIWidgetsError.reportError(currentError);
}
else {
foreach (var errorListener in localErrorListeners) {
try {
errorListener(exception);
}
catch (Exception ex) {
UIWidgetsError.reportError(
new UIWidgetsErrorDetails(
context: "when reporting an error to an image listener",
library: "image resource service",
exception: ex
)
);
}
}
}
}
public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
base.debugFillProperties(description);
description.add(new DiagnosticsProperty<ImageInfo>(
"current", currentImage, ifNull: "unresolved", showName: false));
description.add(new ObjectFlagProperty<List<_ImageListenerPair>>(
"listeners",
_listeners,
ifPresent: $"{_listeners.Count} listener{(_listeners.Count == 1 ? "" : "s")}"
));
}
}
public class OneFrameImageStreamCompleter : ImageStreamCompleter {
public OneFrameImageStreamCompleter(IPromise<ImageInfo> image,
InformationCollector informationCollector = null) {
D.assert(image != null);
image.Then(result => { setImage(result); }).Catch(err => {
reportError(
context: "resolving a single-frame image stream",
exception: err,
informationCollector: informationCollector,
silent: true
);
});
}
}
public class MultiFrameImageStreamCompleter : ImageStreamCompleter {
public MultiFrameImageStreamCompleter(
IPromise<Codec> codec,
float scale,
InformationCollector informationCollector = null
) {
D.assert(codec != null);
_scale = scale;
_informationCollector = informationCollector;
codec.Then((Action<Codec>) _handleCodecReady, ex => {
reportError(
context: "resolving an image codec",
exception: ex,
informationCollector: informationCollector,
silent: true
);
});
}
Codec _codec;
readonly float _scale;
readonly InformationCollector _informationCollector;
FrameInfo _nextFrame;
TimeSpan? _shownTimestamp;
TimeSpan? _frameDuration;
int _framesEmitted = 0;
Timer _timer;
bool _frameCallbackScheduled = false;
void _handleCodecReady(Codec codec) {
_codec = codec;
D.assert(_codec != null);
if (hasListeners) {
_decodeNextFrameAndSchedule();
}
}
void _handleAppFrame(TimeSpan timestamp) {
_frameCallbackScheduled = false;
if (!hasListeners) {
return;
}
if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
_emitFrame(new ImageInfo(image: _nextFrame.image, scale: _scale));
_shownTimestamp = timestamp;
_frameDuration = _nextFrame.duration;
_nextFrame = null;
int completedCycles = _codec.frameCount == 0 ? 0 : _framesEmitted / _codec.frameCount;
if (_codec.repetitionCount == -1 || completedCycles <= _codec.repetitionCount) {
_decodeNextFrameAndSchedule();
}
return;
}
TimeSpan delay = _frameDuration.Value - (timestamp - _shownTimestamp.Value);
delay = new TimeSpan((long) (delay.Ticks * scheduler_.timeDilation));
_timer = Window.instance.run(delay, _scheduleAppFrame);
}
bool _isFirstFrame() {
return _frameDuration == null;
}
bool _hasFrameDurationPassed(TimeSpan timestamp) {
D.assert(_shownTimestamp != null);
return timestamp - _shownTimestamp >= _frameDuration;
}
void _decodeNextFrameAndSchedule() {
var frame = _codec.getNextFrame();
_nextFrame = frame;
if (_codec.frameCount == 1) {
_emitFrame(new ImageInfo(image: _nextFrame.image, scale: _scale));
return;
}
_scheduleAppFrame();
}
void _scheduleAppFrame() {
if (_frameCallbackScheduled) {
return;
}
_frameCallbackScheduled = true;
SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
}
void _emitFrame(ImageInfo imageInfo) {
setImage(imageInfo);
_framesEmitted += 1;
}
public override void addListener(ImageListener listener, ImageErrorListener onError = null) {
if (!hasListeners && _codec != null) {
_decodeNextFrameAndSchedule();
}
base.addListener(listener, onError: onError);
}
public override void removeListener(ImageListener listener) {
base.removeListener(listener);
if (!hasListeners) {
_timer?.cancel();
_timer = null;
}
}
}
}