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

379 行
12 KiB

using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.painting {
//TODO: TimeLineTask
public class ImageCache {
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
readonly Dictionary<object, _PendingImage> _pendingImages =
new Dictionary<object, _PendingImage>();
readonly Dictionary<object, _CachedImage> _cache = new Dictionary<object, _CachedImage>();
readonly Dictionary<object, _LiveImage> _liveImages = new Dictionary<object, _LiveImage>();
readonly LinkedList<object> _lruKeys = new LinkedList<object>();
int _maximumSize = _kDefaultSize;
public int maximumSize {
get { return _maximumSize; }
set {
D.assert(value >= 0);
if (value == _maximumSize) {
return;
}
_maximumSize = value;
if (_maximumSize == 0) {
clear();
}
else {
_checkCacheSize();
}
}
}
public int currentSize {
get { return _cache.Count; }
}
int _maximumSizeBytes = _kDefaultSizeBytes;
public int maximumSizeBytes {
get { return _maximumSizeBytes; }
set {
D.assert(value >= 0);
if (value == _maximumSizeBytes) {
return;
}
_maximumSizeBytes = value;
if (_maximumSizeBytes == 0) {
clear();
}
else {
_checkCacheSize();
}
}
}
int _currentSizeBytes;
public int currentSizeBytes {
get { return _currentSizeBytes; }
}
public void clear() {
_cache.Clear();
_pendingImages.Clear();
_currentSizeBytes = 0;
_lruKeys.Clear();
}
public bool evict(object key, bool includeLive = true) {
if (includeLive) {
_LiveImage liveImage = _liveImages.getOrDefault(key);
_liveImages.Remove(key);
liveImage?.removeListener();
}
D.assert(key != null);
if (_pendingImages.TryGetValue(key, out var pendingImage)) {
pendingImage.removeListener();
_pendingImages.Remove(key);
return true;
}
if (_cache.TryGetValue(key, out var image)) {
_currentSizeBytes -= image.sizeBytes ?? 0;
_cache.Remove(key);
_lruKeys.Remove(image.node);
return true;
}
return false;
}
void _touch(Object key, _CachedImage image) {
// D.assert(foundation_.kReleaseMode);
if (image.sizeBytes != null && image.sizeBytes <= maximumSizeBytes) {
_currentSizeBytes += image.sizeBytes ?? 0;
_cache[key] = image;
// _checkCacheSize(timelineTask);
}
}
void _trackLiveImage(Object key, _LiveImage image, bool debugPutOk = true) {
var imageOut = _liveImages.putIfAbsent(key, () => {
D.assert(debugPutOk);
image.completer.addOnLastListenerRemovedCallback(image.handleRemove);
return image;
});
imageOut.sizeBytes = image.sizeBytes ?? image.sizeBytes;
}
public ImageStreamCompleter putIfAbsent(object key, Func<ImageStreamCompleter> loader,
ImageErrorListener onError = null) {
D.assert(key != null);
D.assert(loader != null);
ImageStreamCompleter result = null;
if (_pendingImages.TryGetValue(key, out var pendingImage)) {
result = pendingImage.completer;
return result;
}
if (_cache.TryGetValue(key, out var image)) {
if (image.node != null) {
_lruKeys.Remove(image.node);
}
_trackLiveImage(key, new _LiveImage(image.completer, image.sizeBytes, () => _liveImages.Remove(key)));
image.node = _lruKeys.AddLast(key);
return image.completer;
}
_liveImages.TryGetValue(key, out var liveImage);
if (liveImage != null) {
_touch(key, liveImage);
return liveImage.completer;
}
try {
result = loader();
_trackLiveImage(key, new _LiveImage(result, null, () => _liveImages.Remove(key)));
}
catch (Exception ex) {
if (onError != null) {
onError(ex);
}
else {
throw;
}
}
bool listenedOnce = false;
_PendingImage untrackedPendingImage = null;
void listener(ImageInfo info, bool syncCall) {
int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
_CachedImage cachedImage = new _CachedImage(result, imageSize);
_trackLiveImage(
key,
new _LiveImage(
result,
imageSize,
() => _liveImages.Remove(key)
),
debugPutOk: syncCall
);
_PendingImage _pendingImage = untrackedPendingImage ?? _pendingImages.getOrDefault(key);
_pendingImages.Remove(key);
if (_pendingImage != null) {
_pendingImage.removeListener();
}
if (untrackedPendingImage == null) {
_touch(key, cachedImage);
}
listenedOnce = true;
}
ImageStreamListener streamListener = new ImageStreamListener(listener);
if (maximumSize > 0 && maximumSizeBytes > 0) {
_pendingImages[key] = new _PendingImage(result, streamListener);
}
else {
untrackedPendingImage = new _PendingImage(result, streamListener);
}
result.addListener(streamListener);
return result;
}
public ImageCacheStatus statusForKey(Object key) {
return new ImageCacheStatus(
pending: _pendingImages.ContainsKey(key),
keepAlive: _cache.ContainsKey(key),
live: _liveImages.ContainsKey(key)
);
}
public bool containsKey(Object key) {
return _pendingImages[key] != null || _cache[key] != null;
}
int liveImageCount {
get => _liveImages.Count;
}
int pendingImageCount {
get => _pendingImages.Count;
}
void clearLiveImages() {
foreach (_LiveImage
image in _liveImages.Values) {
image.removeListener();
}
_liveImages.Clear();
}
void _checkCacheSize() {
Dictionary<string, object> finishArgs = new Dictionary<string, object>();
// TimelineTask checkCacheTask;
if (!foundation_.kReleaseMode) {
// checkCacheTask = TimelineTask(parent: timelineTask)..start('checkCacheSize');
finishArgs["evictedKeys"] = new List<string>();
finishArgs["currentSize"] = currentSize;
finishArgs["currentSizeBytes"] = currentSizeBytes;
}
while (_currentSizeBytes > _maximumSizeBytes || _cache.Count > _maximumSize) {
object key = _cache.Keys.GetEnumerator().Current;
_CachedImage image = _cache[key];
_currentSizeBytes -= image.sizeBytes ?? 0;
_cache.Remove(key);
if (!foundation_.kReleaseMode) {
((List<string>) finishArgs["evictedKeys"]).Add(key.ToString());
}
}
if (!foundation_.kReleaseMode) {
finishArgs["endSize"] = currentSize;
finishArgs["endSizeBytes"] = currentSizeBytes;
// checkCacheTask.finish(arguments: finishArgs);
}
D.assert(_currentSizeBytes >= 0);
D.assert(_cache.Count <= maximumSize);
D.assert(_currentSizeBytes <= maximumSizeBytes);
}
}
public class ImageCacheStatus : IEquatable<ImageCacheStatus> {
internal ImageCacheStatus(
bool pending = false,
bool keepAlive = false,
bool live = false
) {
D.assert(!pending || !keepAlive);
this.pending = pending;
this.keepAlive = keepAlive;
this.live = live;
}
public bool pending;
public bool keepAlive;
public bool live;
public bool tracked {
get => pending || keepAlive || live;
}
public bool untracked {
get => !pending && !keepAlive && !live;
}
public override string ToString() =>
$"{foundation_.objectRuntimeType(this, "ImageCacheStatus")}(pending: {pending}, live: {live}, keepAlive: {keepAlive})";
public bool Equals(ImageCacheStatus other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return pending == other.pending && keepAlive == other.keepAlive && live == other.live;
}
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((ImageCacheStatus) obj);
}
public static bool operator ==(ImageCacheStatus left, object right) {
return Equals(left, right);
}
public static bool operator !=(ImageCacheStatus left, object right) {
return !Equals(left, right);
}
public override int GetHashCode() {
unchecked {
var hashCode = pending.GetHashCode();
hashCode = (hashCode * 397) ^ keepAlive.GetHashCode();
hashCode = (hashCode * 397) ^ live.GetHashCode();
return hashCode;
}
}
}
class _CachedImage {
public _CachedImage(ImageStreamCompleter completer, int? sizeBytes) {
this.completer = completer;
this.sizeBytes = sizeBytes;
}
public ImageStreamCompleter completer;
public int? sizeBytes;
public LinkedListNode<object> node;
}
class _LiveImage : _CachedImage {
internal _LiveImage(ImageStreamCompleter completer, int? sizeBytes, VoidCallback handleRemove)
: base(completer, sizeBytes) {
this.handleRemove = handleRemove;
}
public readonly VoidCallback handleRemove;
public void removeListener() {
completer.removeOnLastListenerRemovedCallback(handleRemove);
}
}
class _PendingImage {
public _PendingImage(
ImageStreamCompleter completer,
ImageStreamListener listener
) {
this.completer = completer;
this.listener = listener;
}
public readonly ImageStreamCompleter completer;
public readonly ImageStreamListener listener;
public void removeListener() {
completer.removeListener(listener);
}
}
}