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

397 行
14 KiB

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