浏览代码

SplayTree & painting/image

/siyaoH-1.17-PlatformMessage
siyao 4 年前
当前提交
fc22bbbe
共有 16 个文件被更改,包括 1000 次插入208 次删除
  1. 32
      com.unity.uiwidgets/Runtime/external/SplayTree.cs
  2. 6
      com.unity.uiwidgets/Runtime/painting/binding.cs
  3. 3
      com.unity.uiwidgets/Runtime/painting/circle_border.cs
  4. 276
      com.unity.uiwidgets/Runtime/painting/colors.cs
  5. 10
      com.unity.uiwidgets/Runtime/painting/continuous_rectangle_border.cs
  6. 2
      com.unity.uiwidgets/Runtime/painting/decoration.cs
  7. 69
      com.unity.uiwidgets/Runtime/painting/decoration_image.cs
  8. 233
      com.unity.uiwidgets/Runtime/painting/image_cache.cs
  9. 241
      com.unity.uiwidgets/Runtime/painting/image_provider.cs
  10. 66
      com.unity.uiwidgets/Runtime/painting/image_resolution.cs
  11. 198
      com.unity.uiwidgets/Runtime/painting/image_stream.cs
  12. 2
      com.unity.uiwidgets/Runtime/rendering/image.cs
  13. 13
      com.unity.uiwidgets/Runtime/widgets/fade_in_image.cs
  14. 41
      com.unity.uiwidgets/Runtime/widgets/image.cs
  15. 13
      com.unity.uiwidgets/Runtime/painting/image_decoder.cs
  16. 3
      com.unity.uiwidgets/Runtime/painting/image_decoder.cs.meta

32
com.unity.uiwidgets/Runtime/external/SplayTree.cs


return new KeyValuePair<TKey, TValue>(t.Key, t.Value);
}
public TKey lastKeyBefore(TKey key) {
if (key == null) throw new Exception("should input null");
if (root == null) throw new Exception("root is null");
int comp = Splay(key);
if (comp < 0) return root.Key;
SplayTreeNode node = root.LeftChild;
if (node == null) throw new Exception("does not exist");
while (node.RightChild != null) {
node = node.RightChild;
}
return node.Key;
}
public TKey firstKeyAfter(TKey key) {
if (key == null) throw new Exception("should input null");
if (root == null) throw new Exception("root is null");
int comp = Splay(key);
if (comp > 0) return root.Key;
SplayTreeNode node = root.LeftChild;
if (node == null) throw new Exception("does not exist");
while (node.LeftChild != null) {
node = node.LeftChild;
}
return node.Key;
}
public KeyValuePair<TKey, TValue>? Last() {
SplayTreeNode t = root;

return new KeyValuePair<TKey, TValue>(t.Key, t.Value);
}
void Splay(TKey key) {
int Splay(TKey key) {
int c;
var c = key.CompareTo(t.Key);
c = key.CompareTo(t.Key);
if (c < 0) {
if (t.LeftChild == null) {
break;

t.LeftChild = header.RightChild;
t.RightChild = header.LeftChild;
root = t;
return c;
}
public bool Remove(TKey key) {

6
com.unity.uiwidgets/Runtime/painting/binding.cs


return new ImageCache();
}
Future<ui.Codec> instantiateImageCodec(byte[] bytes,
int? cacheWidth,
int? cacheHeight
public Future<ui.Codec> instantiateImageCodec(byte[] bytes,
int? cacheWidth = null,
int? cacheHeight = null
) {
D.assert(cacheWidth == null || cacheWidth > 0);
D.assert(cacheHeight == null || cacheHeight > 0);

3
com.unity.uiwidgets/Runtime/painting/circle_border.cs


using System;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;

}
public override string ToString() {
return $"{GetType()}({side})";
return $"{foundation_.objectRuntimeType(this, "CircleBorder")}({side})";
}
}
}

276
com.unity.uiwidgets/Runtime/painting/colors.cs


using System;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
public partial class painting_ {
internal static float _getHue(float red, float green, float blue, float max, float delta) {
float hue = 0;
if (max == 0.0f) {
hue = 0.0f;
}
else if (max == red) {
hue = 60.0f * (((green - blue) / delta) % 6);
}
else if (max == green) {
hue = 60.0f * (((blue - red) / delta) + 2);
}
else if (max == blue) {
hue = 60.0f * (((red - green) / delta) + 4);
}
return hue;
}
internal static Color _colorFromHue(
float alpha,
float hue,
float chroma,
float secondary,
float match
) {
float red;
float green;
float blue;
if (hue < 60.0f) {
red = chroma;
green = secondary;
blue = 0.0f;
}
else if (hue < 120.0f) {
red = secondary;
green = chroma;
blue = 0.0f;
}
else if (hue < 180.0f) {
red = 0.0f;
green = chroma;
blue = secondary;
}
else if (hue < 240.0f) {
red = 0.0f;
green = secondary;
blue = chroma;
}
else if (hue < 300.0f) {
red = secondary;
green = 0.0f;
blue = chroma;
}
else {
red = chroma;
green = 0.0f;
blue = secondary;
}
return Color.fromARGB((alpha * 0xFF).round(), ((red + match) * 0xFF).round(),
((green + match) * 0xFF).round(), ((blue + match) * 0xFF).round());
}
}
public class HSVColor {
HSVColor(float alpha, float hue, float saturation, float value) {
D.assert(this.alpha >= 0);

float secondary = chroma * (1.0f - (((hue / 60.0f) % 2.0f) - 1.0f).abs());
float match = value - chroma;
return ColorUtils._colorFromHue(alpha, hue, chroma, secondary, match);
return painting_._colorFromHue(alpha, hue, chroma, secondary, match);
}
public readonly float alpha;

public override string ToString() {
return $"{foundation_.objectRuntimeType(this, "HSVColor")}({alpha}, {hue}, {saturation},{value})";
}
public class HSLColor : IEquatable<HSLColor> {
private HSLColor(float alpha,
float hue,
float saturation,
float lightness) {
this.alpha = alpha;
this.hue = hue;
this.saturation = saturation;
this.lightness = lightness;
}
public static HSLColor fromAHSL(float alpha, float hue, float saturation, float lightness) {
D.assert(alpha >= 0.0f);
D.assert(alpha <= 1.0f);
D.assert(hue >= 0.0f);
D.assert(hue <= 360.0f);
D.assert(saturation >= 0.0f);
D.assert(saturation <= 1.0f);
D.assert(lightness >= 0.0f);
D.assert(lightness <= 1.0f);
return new HSLColor(alpha, hue, saturation, lightness);
}
public static HSLColor fromColor(Color color) {
float red = color.red / 0xFF;
float green = color.green / 0xFF;
float blue = color.blue / 0xFF;
float max = Mathf.Max(red, Mathf.Max(green, blue));
float min = Mathf.Min(red, Mathf.Min(green, blue));
float delta = max - min;
float alpha = color.alpha / 0xFF;
float hue = painting_._getHue(red, green, blue, max, delta);
float lightness = (max + min) / 2.0f;
float saturation = lightness == 1.0f
? 0.0f
: ((delta / (1.0f - Mathf.Abs(2.0f * lightness - 1.0f))).clamp(0.0f, 1.0f));
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
readonly float alpha;
readonly float hue;
readonly float saturation;
readonly float lightness;
HSLColor withAlpha(float alpha) {
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
HSLColor withHue(float hue) {
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
HSLColor withSaturation(float saturation) {
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
HSLColor withLightness(float lightness) {
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
Color toColor() {
float chroma = Mathf.Abs(1.0f - (2.0f * lightness - 1.0f) * saturation);
float secondary = chroma * (1.0f - Mathf.Abs(((hue / 60.0f) % 2.0f) - 1.0f));
float match = lightness - chroma / 2.0f;
return painting_._colorFromHue(alpha, hue, chroma, secondary, match);
}
HSLColor _scaleAlpha(float factor) {
return withAlpha(alpha * factor);
}
static HSLColor lerp(HSLColor a, HSLColor b, float t) {
D.assert(t != null);
if (a == null && b == null)
return null;
if (a == null)
return b._scaleAlpha(t);
if (b == null)
return a._scaleAlpha(1.0f - t);
return HSLColor.fromAHSL(
Mathf.Lerp(a.alpha, b.alpha, t).clamp(0.0f, 1.0f),
Mathf.Lerp(a.hue, b.hue, t) % 360.0f,
Mathf.Lerp(a.saturation, b.saturation, t).clamp(0.0f, 1.0f),
Mathf.Lerp(a.lightness, b.lightness, t).clamp(0.0f, 1.0f)
);
}
public override string ToString() {
return $"{foundation_.objectRuntimeType(this, "HSLColor")}({alpha}, {hue}, {saturation}, {lightness})";
}
public static bool operator ==(HSLColor left, HSLColor right) {
return Equals(left, right);
}
public static bool operator !=(HSLColor left, HSLColor right) {
return !Equals(left, right);
}
public bool Equals(HSLColor other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return alpha.Equals(other.alpha) && hue.Equals(other.hue) && saturation.Equals(other.saturation) &&
lightness.Equals(other.lightness);
}
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((HSLColor) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = alpha.GetHashCode();
hashCode = (hashCode * 397) ^ hue.GetHashCode();
hashCode = (hashCode * 397) ^ saturation.GetHashCode();
hashCode = (hashCode * 397) ^ lightness.GetHashCode();
return hashCode;
}
}
}
public class ColorSwatch<T> : Color {
public ColorSwatch(
uint primary,

return GetType() + "(primary value: " + base.ToString() + ")";
}
}
public class ColorProperty : DiagnosticsProperty<Color> {
public ColorProperty(
string name = "",

D.assert(showName != null);
D.assert(style != null);
D.assert(level != null);
}
public override Dictionary<string, object> toJsonMap(DiagnosticsSerializationDelegate Delegate) {
}
public override Dictionary<string, object> toJsonMap(DiagnosticsSerializationDelegate Delegate) {
Dictionary<string, object> json = base.toJsonMap(Delegate);
if (value != null) {
json["valueProperties"] = new Dictionary<string, object> {

{"alpha", value.alpha}
};
}
}
}
public static class ColorUtils {
internal static Color _colorFromHue(
float alpha,
float hue,
float chroma,
float secondary,
float match
) {
float red;
float green;
float blue;
if (hue < 60.0) {
red = chroma;
green = secondary;
blue = 0.0f;
}
else if (hue < 120.0) {
red = secondary;
green = chroma;
blue = 0.0f;
}
else if (hue < 180.0) {
red = 0.0f;
green = chroma;
blue = secondary;
}
else if (hue < 240.0) {
red = 0.0f;
green = secondary;
blue = chroma;
}
else if (hue < 300.0) {
red = secondary;
green = 0.0f;
blue = chroma;
}
else {
red = chroma;
green = 0.0f;
blue = secondary;
}
return Color.fromARGB((alpha * 0xFF).round(),
((red + match) * 0xFF).round(),
((green + match) * 0xFF).round(), ((blue + match) * 0xFF).round());
}
}
}

10
com.unity.uiwidgets/Runtime/painting/continuous_rectangle_border.cs


path.cubicTo(left, top, left, top, left + tlRadiusY, top);
path.lineTo(right - trRadiusX, top);
path.cubicTo(right, top, right, top, right, top + trRadiusY);
path.lineTo(right, bottom - blRadiusX);
path.cubicTo(right, bottom, right, bottom, right - blRadiusY, bottom);
path.lineTo(left + brRadiusX, bottom);
path.cubicTo(left, bottom, left, bottom, left, bottom - brRadiusY);
path.lineTo(right, bottom - brRadiusX);
path.cubicTo(right, bottom, right, bottom, right - brRadiusY, bottom);
path.lineTo(left + blRadiusX, bottom);
path.cubicTo(left, bottom, left, bottom, left, bottom - blRadiusY);
path.close();
return path;
}

}
public override string ToString() {
return $"{GetType()}({side}, {borderRadius})";
return $"{foundation_.objectRuntimeType(this, "ContinuousRectangleBorder")}({side}, {borderRadius})";
}
}
}

2
com.unity.uiwidgets/Runtime/painting/decoration.cs


}
public override string toStringShort() {
return GetType().ToString();
return foundation_.objectRuntimeType(this, "Decoration");
}
public virtual bool debugAssertIsValid() {

69
com.unity.uiwidgets/Runtime/painting/decoration_image.cs


public class DecorationImage : IEquatable<DecorationImage> {
public DecorationImage(
ImageProvider image = null,
ImageErrorListener onError = null,
ImageRepeat repeat = ImageRepeat.noRepeat
ImageRepeat repeat = ImageRepeat.noRepeat,
bool matchTextDirection = false
this.onError = onError;
this.matchTextDirection = matchTextDirection;
public readonly ImageErrorListener onError;
public readonly bool matchTextDirection;
public DecorationImagePainter createPainter(VoidCallback onChanged) {
D.assert(onChanged != null);

D.assert(rect != null);
D.assert(configuration != null);
bool flipHorizontally = false;
if (_details.matchTextDirection) {
D.assert(() => {
// We check this first so that the assert will fire immediately, not just
// when the image is ready.
if (configuration.textDirection == null) {
throw new UIWidgetsError(new List<DiagnosticsNode>() {
new ErrorSummary(
"DecorationImage.matchTextDirection can only be used when a TextDirection is available."),
new ErrorDescription(
"When DecorationImagePainter.paint() was called, there was no text direction provided " +
"in the ImageConfiguration object to match."
),
new DiagnosticsProperty<DecorationImage>("The DecorationImage was", _details,
style: DiagnosticsTreeStyle.errorProperty),
new DiagnosticsProperty<ImageConfiguration>("The ImageConfiguration was", configuration,
style: DiagnosticsTreeStyle.errorProperty)
});
}
return true;
});
if (configuration.textDirection == TextDirection.rtl)
flipHorizontally = true;
}
_imageStream?.removeListener(_imageListener);
ImageStreamListener listener = new ImageStreamListener(
_handleImage,
onError: _details.onError
);
_imageStream?.removeListener(listener);
_imageStream.addListener(_imageListener);
_imageStream.addListener(listener);
}
if (_image == null) {

canvas.clipPath(clipPath);
}
ImageUtils.paintImage(
painting_.paintImage(
canvas: canvas,
rect: rect,
image: _image.image,

alignment: _details.alignment,
centerSlice: _details.centerSlice,
repeat: _details.repeat
repeat: _details.repeat,
flipHorizontally: flipHorizontally,
filterQuality: FilterQuality.low
);
if (clipPath != null) {

void _imageListener(ImageInfo value, bool synchronousCall) {
void _handleImage(ImageInfo value, bool synchronousCall) {
if (_image == value) {
return;
}

}
public void Dispose() {
_imageStream?.removeListener(_imageListener);
_imageStream?.removeListener(new ImageStreamListener(
_handleImage,
onError: _details.onError
));
}
public override string ToString() {

public static class ImageUtils {
public static partial class painting_ {
public static void paintImage(
Canvas canvas = null,
Rect rect = null,

Alignment alignment = null,
Rect centerSlice = null,
ImageRepeat repeat = ImageRepeat.noRepeat,
bool flipHorizontally = false,
bool invertColors = false,
FilterQuality filterQuality = FilterQuality.low
) {

float halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0f;
float halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0f;
float dx = halfWidthDelta + alignment.x * halfWidthDelta;
float dx = halfWidthDelta + (flipHorizontally ? -alignment.x : alignment.x) * halfWidthDelta;
bool needSave = repeat != ImageRepeat.noRepeat;
bool needSave = repeat != ImageRepeat.noRepeat || flipHorizontally;
}
if (flipHorizontally) {
float dxInside = -(rect.left + rect.width / 2.0f);
canvas.translate(-dxInside, 0.0f);
canvas.scale(-1.0f, 1.0f);
canvas.translate(dxInside, 0.0f);
}
if (repeat != ImageRepeat.noRepeat) {

233
com.unity.uiwidgets/Runtime/painting/image_cache.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
//TODO: TimeLineTask
public class ImageCache {
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB

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;

_lruKeys.Clear();
}
public bool evict(object key) {
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)) {

}
if (_cache.TryGetValue(key, out var image)) {
_currentSizeBytes -= image.sizeBytes;
_currentSizeBytes -= image.sizeBytes ?? 0;
_cache.Remove(key);
_lruKeys.Remove(image.node);
return true;

}
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);

}
if (_cache.TryGetValue(key, out var image)) {
// put to the MRU position
_trackLiveImage(key, new _LiveImage(image.completer, image.sizeBytes, () => _liveImages.Remove(key)));
_CachedImage liveImage = _liveImages[key];
if (liveImage != null) {
_touch(key, liveImage);
return liveImage.completer;
}
_trackLiveImage(key, new _LiveImage(result, null, () => _liveImages.Remove(key)));
}
catch (Exception ex) {
if (onError != null) {

}
}
bool listenedOnce = false;
_PendingImage untrackedPendingImage = null;
if (maximumSizeBytes > 0 && imageSize > maximumSizeBytes) {
_maximumSizeBytes = imageSize + 1000;
_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();
_currentSizeBytes += imageSize;
if (_pendingImages.TryGetValue(key, out var loadedPendingImage)) {
loadedPendingImage.removeListener();
_pendingImages.Remove(key);
if (untrackedPendingImage == null) {
_touch(key, image);
D.assert(!_cache.ContainsKey(key));
_cache[key] = cachedImage;
cachedImage.node = _lruKeys.AddLast(key);
_checkCacheSize();
listenedOnce = true;
ImageStreamListener streamListener = new ImageStreamListener(listener);
_pendingImages[key] = new _PendingImage(result, listener);
result.addListener(listener);
_pendingImages[key] = new _PendingImage(result, streamListener);
}
else {
untrackedPendingImage = new _PendingImage(result, streamListener);
result.addListener(streamListener);
public ImageCacheStatus statusForKey(Object key) {
return new ImageCacheStatus(
pending: _pendingImages.ContainsKey(key),
keepAlive: _cache.ContainsKey(key),
live: _liveImages.ContainsKey(key)
);
}
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();
}
while (_currentSizeBytes > _maximumSizeBytes || _cache.Count > _maximumSize) {
var node = _lruKeys.First;
var key = node.Value; // get the LRU item
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;
}
D.assert(_cache.ContainsKey(key));
while (_currentSizeBytes > _maximumSizeBytes || _cache.Count > _maximumSize) {
object key = _cache.Keys.GetEnumerator().Current;
_currentSizeBytes -= image.sizeBytes ?? 0;
_cache.Remove(key);
if (!foundation_.kReleaseMode) {
((List<string>) finishArgs["evictedKeys"]).Add(key.ToString());
}
}
D.assert(node == image.node);
_currentSizeBytes -= image.sizeBytes;
_cache.Remove(key);
_lruKeys.Remove(image.node);
if (!foundation_.kReleaseMode) {
finishArgs["endSize"] = currentSize;
finishArgs["endSizeBytes"] = currentSizeBytes;
// checkCacheTask.finish(arguments: finishArgs);
}
D.assert(_currentSizeBytes >= 0);

}
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;
}
}
}
public _CachedImage(ImageStreamCompleter completer, int sizeBytes) {
public _CachedImage(ImageStreamCompleter completer, int? sizeBytes) {
public int sizeBytes;
public int? sizeBytes;
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);
}
}
ImageListener listener
ImageStreamListener listener
) {
this.completer = completer;
this.listener = listener;

public readonly ImageListener listener;
public readonly ImageStreamListener listener;
public void removeListener() {
completer.removeListener(listener);

241
com.unity.uiwidgets/Runtime/painting/image_provider.cs


public abstract class ImageProvider {
public abstract ImageStream resolve(ImageConfiguration configuration);
public static bool operator !=(ImageProvider left, ImageProvider right) {
return !Equals(left, right);
}

return stream;
}
public ImageStream createStream(ImageConfiguration configuration) {
return new ImageStream();
}
// This is an unusual edge case where someone has told us that they found
// the image we want before getting to this method. We should avoid calling
// load again, but still update the image cache with LRU information.
if (stream.completer != null) {
ImageStreamCompleter completerEdge = PaintingBinding.instance.imageCache.putIfAbsent(
key,

return obtainKey(configuration).then(key => cache.evict(key)).to<bool>();
}
protected abstract ImageStreamCompleter load(T assetBundleImageKey, DecoderCallback decode);
public abstract ImageStreamCompleter load(T assetBundleImageKey, DecoderCallback decode);
public abstract Future<T> obtainKey(ImageConfiguration configuration);
Future<ImageCacheStatus> obtainCacheStatus(
ImageConfiguration configuration,
ImageErrorListener handleError = null
) {
D.assert(configuration != null);
Completer completer = Completer.create();
_createErrorHandlerAndKey(
configuration,
(T key, Action<Exception> innerHandleError) => {
completer.complete(FutureOr.value(PaintingBinding.instance.imageCache.statusForKey(key)));
},
(T key, Exception exception) => {
if (handleError != null) {
handleError(exception);
}
else {
InformationCollector collector = null;
D.assert(() => {
IEnumerable<DiagnosticsNode> infoCollector() {
yield return new DiagnosticsProperty<ImageProvider>("Image provider", this);
yield return new DiagnosticsProperty<ImageConfiguration>("Image configuration",
configuration);
yield return new DiagnosticsProperty<T>("Image key", key, defaultValue: null);
}
protected abstract Future<T> obtainKey(ImageConfiguration configuration);
collector = infoCollector;
return true;
});
UIWidgetsError.onError(new UIWidgetsErrorDetails(
context: new ErrorDescription("while checking the cache location of an image"),
informationCollector: collector,
exception: exception
));
completer.complete();
}
return Future.value();
}
);
return completer.future.to<ImageCacheStatus>();
}
private void _createErrorHandlerAndKey(
ImageConfiguration configuration,

didError = true;
};
// If an error is added to a synchronous completer before a listener has been
// added, it can throw an error both into the zone and up the stack. Thus, it
// looks like the error has been caught, but it is in fact also bubbling to the
// zone. Since we cannot prevent all usage of Completer.sync here, or rather
// that changing them would be too breaking, we instead hook into the same
// zone mechanism to intercept the uncaught error and deliver it to the
// image stream's error handler. Note that these errors may be duplicated,
// hence the need for the `didError` flag.
Zone dangerZone = Zone.current.fork(
specification: new ZoneSpecification(
handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone, Exception error) => {

protected AssetBundleImageProvider() {
}
protected override ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
public override ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
return new MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: key.scale,

}
}
internal class _SizeAwareCacheKey : IEquatable<_SizeAwareCacheKey> {
internal _SizeAwareCacheKey(object providerCacheKey, int width, int height) {
this.providerCacheKey = providerCacheKey;
this.width = width;
this.height = height;
}
public readonly object providerCacheKey;
public readonly int width;
public readonly int height;
public static bool operator ==(_SizeAwareCacheKey left, object right) {
return Equals(left, right);
}
public static bool operator !=(_SizeAwareCacheKey left, object right) {
return !Equals(left, right);
}
public bool Equals(_SizeAwareCacheKey other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return Equals(providerCacheKey, other.providerCacheKey) && width == other.width && height == other.height;
}
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((_SizeAwareCacheKey) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = (providerCacheKey != null ? providerCacheKey.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ width;
hashCode = (hashCode * 397) ^ height;
return hashCode;
}
}
}
internal class ResizeImage : ImageProvider<_SizeAwareCacheKey> {
public ResizeImage(
ImageProvider<object> imageProvider,
int width = 0,
int height = 0
) {
D.assert(width != null || height != null);
this.imageProvider = imageProvider;
this.width = width;
this.height = height;
}
public readonly ImageProvider<object> imageProvider;
public readonly int width;
public readonly int height;
public static ImageProvider resizeIfNeeded(int cacheWidth, int cacheHeight, ImageProvider<object> provider) {
if (cacheWidth != null || cacheHeight != null) {
return new ResizeImage(provider, width: cacheWidth, height: cacheHeight);
}
return provider;
}
public override ImageStreamCompleter load(_SizeAwareCacheKey assetBundleImageKey, DecoderCallback decode) {
Future<Codec> decodeResize(byte[] bytes, int? cacheWidth = 0, int? cacheHeight = 0) {
D.assert(
cacheWidth == null && cacheHeight == null,
() =>
"ResizeImage cannot be composed with another ImageProvider that applies cacheWidth or cacheHeight."
);
return decode(bytes, cacheWidth: width, cacheHeight: height);
}
return imageProvider.load(assetBundleImageKey.providerCacheKey, decodeResize);
}
public override Future<_SizeAwareCacheKey> obtainKey(ImageConfiguration configuration) {
Completer completer = null;
SynchronousFuture<_SizeAwareCacheKey> result = null;
imageProvider.obtainKey(configuration).then((object key) => {
// TODO: completer is always null?
if (completer == null) {
result = new SynchronousFuture<_SizeAwareCacheKey>(new _SizeAwareCacheKey(key, width, height));
}
else {
completer.complete(FutureOr.value(new _SizeAwareCacheKey(key, width, height)));
}
});
if (result != null) {
return result;
}
completer = Completer.create();
return completer.future.to<_SizeAwareCacheKey>();
}
}
public class NetworkImage : ImageProvider<NetworkImage>, IEquatable<NetworkImage> {
public NetworkImage(string url,
float scale = 1.0f,

public readonly IDictionary<string, string> headers;
protected override Future<NetworkImage> obtainKey(ImageConfiguration configuration) {
public override Future<NetworkImage> obtainKey(ImageConfiguration configuration) {
protected override ImageStreamCompleter load(NetworkImage key, DecoderCallback decode) {
public override ImageStreamCompleter load(NetworkImage key, DecoderCallback decode) {
return new MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: key.scale,

var completer = Completer.create();
var isolate = Isolate.current;
var panel = UIWidgetsPanel.current;
panel.StartCoroutine(_loadCoroutine(key.url, completer, isolate));
panel.StartCoroutine(_loadCoroutine(key.url, completer, isolate));
IEnumerator _loadCoroutine(string key, Completer completer, Isolate isolate) {
var url = new Uri(key);
using (var www = UnityWebRequest.Get(url)) {

public readonly float scale;
protected override Future<FileImage> obtainKey(ImageConfiguration configuration) {
public override Future<FileImage> obtainKey(ImageConfiguration configuration) {
protected override ImageStreamCompleter load(FileImage key, DecoderCallback decode) {
public override ImageStreamCompleter load(FileImage key, DecoderCallback decode) {
return new MultiFrameImageStreamCompleter(_loadAsync(key, decode),
scale: key.scale,
informationCollector: infoCollector);

byte[] bytes = File.ReadAllBytes("Assets/StreamingAssets/" + key.file);
if (bytes != null && bytes.Length > 0 ) {
if (bytes != null && bytes.Length > 0) {
throw new Exception("not loaded");
}

}
public override string ToString() {
return $"{GetType()}(\"{file}\", scale: {scale})";
return $"{foundation_.objectRuntimeType(this, "FileImage")}({file}, scale: {scale})";
}
}

public readonly float scale;
protected override Future<MemoryImage> obtainKey(ImageConfiguration configuration) {
public override Future<MemoryImage> obtainKey(ImageConfiguration configuration) {
protected override ImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {
public override ImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {
return new MultiFrameImageStreamCompleter(
_loadAsync(key, decode),
scale: key.scale);

}
public override string ToString() {
return $"{GetType()}({foundation_.describeIdentity(bytes)}), scale: {scale}";
return
$"{foundation_.objectRuntimeType(this, "MemoryImage")}({foundation_.describeIdentity(bytes)}), scale: {scale}";
}
}

public readonly AssetBundle bundle;
protected override Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
public override Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
return Future.value(FutureOr.value(new AssetBundleImageKey(
bundle: bundle ? bundle : configuration.bundle,
name: assetName,

}
public override string ToString() {
return $"{GetType()}(name: \"{assetName}\", scale: {scale}, bundle: {bundle})";
return
$"{foundation_.objectRuntimeType(this, "ExactAssetImage")}(name: \"{assetName}\", scale: {scale}, bundle: {bundle})";
}
}

silent: silent
);
}
}
public class NetworkImageLoadException : Exception {
NetworkImageLoadException(int statusCode, Uri uri) {
D.assert(uri != null);
D.assert(statusCode != null);
this.statusCode = statusCode;
this.uri = uri;
_message = $"HTTP request failed, statusCode: {statusCode}, {uri}";
}
public readonly int statusCode;
readonly string _message;
public readonly Uri uri;
public override string ToString() => _message;
}
}

66
com.unity.uiwidgets/Runtime/painting/image_resolution.cs


using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Unity.UIWidgets.external;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;

}
public readonly string assetName;
public string keyName {
get { return package == null ? assetName : "packages/$package/$assetName"; }
}
public readonly string package;
public const float _naturalResolution = 1.0f;
protected override Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
public override Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
AssetImageConfiguration assetConfig = new AssetImageConfiguration(configuration, assetName);
AssetBundleImageKey key;
var cache = AssetBundleCache.instance.get(configuration.bundle);

var devicePixelRatio = configuration.devicePixelRatio ?? Window.instance.devicePixelRatio;
key = _loadAsset(chosenBundle, devicePixelRatio);
cache[assetConfig] = key;
internal string _chooseVariant(string main, ImageConfiguration config, List<string> candidates) {
if (config.devicePixelRatio == null || candidates == null || candidates.isEmpty())
return main;
SplayTree<float, string> mapping = new SplayTree<float, string>();
foreach (string candidate in candidates) {
mapping[_parseScale(candidate)] = candidate;
}
return _findNearest(mapping, config.devicePixelRatio ?? 0);
}
string _findNearest(SplayTree<float, string> candidates, float value) {
if (candidates.ContainsKey(value))
return candidates[value];
float lower = candidates.lastKeyBefore(value);
float upper = candidates.firstKeyAfter(value);
if (lower == null)
return candidates[upper];
if (upper == null)
return candidates[lower];
if (value > (lower + upper) / 2)
return candidates[upper];
else
return candidates[lower];
}
internal static readonly Regex _extractRatioRegExp = new Regex(@"/?(\d+(\.\d*)?)x$");
float _parseScale(string key) {
if (key == assetName) {
return _naturalResolution;
}
Uri assetUri = new Uri(key);
string directoryPath = "";
if (assetUri.Segments.Length > 1) {
directoryPath = assetUri.Segments[assetUri.Segments.Length - 2];
}
Match match = _extractRatioRegExp.Match(directoryPath);
if (match != null && match.Groups.Count > 0)
return float.Parse(match.Groups[1].Value);
return _naturalResolution;
}
AssetBundleImageKey _loadAsset(AssetBundle bundle, float devicePixelRatio) {
var extension = Path.GetExtension(this.assetName);
var name = Path.GetFileNameWithoutExtension(this.assetName);

Object asset;
if (bundle == null) {
asset = Resources.Load(assetName);
} else {
}
else {
asset = bundle.LoadAsset(assetName);
}

} else {
}
else {
bundle.Unload(asset);
}

}
public override string ToString() {
return $"{GetType()}(bundle: {bundle}, name: \"{assetName}\")";
return $"{foundation_.objectRuntimeType(this, "AssetImage")}(bundle: {bundle}, name: \"{assetName}\")";
}
}

198
com.unity.uiwidgets/Runtime/painting/image_stream.cs


}
}
public class ImageStreamListener : IEquatable<ImageStreamListener> {
public ImageStreamListener(
ImageListener onImage,
ImageChunkListener onChunk = null,
ImageErrorListener onError = null
) {
D.assert(onImage != null);
this.onImage = onImage;
this.onChunk = onChunk;
this.onError = onError;
}
public readonly ImageListener onImage;
public readonly ImageChunkListener onChunk;
public readonly ImageErrorListener onError;
public bool Equals(ImageStreamListener other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return Equals(onImage, other.onImage) && Equals(onChunk, other.onChunk) && Equals(onError, other.onError);
}
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((ImageStreamListener) obj);
}
public static bool operator ==(ImageStreamListener left, ImageStreamListener right) {
return Equals(left, right);
}
public static bool operator !=(ImageStreamListener left, ImageStreamListener right) {
return !Equals(left, right);
}
public override int GetHashCode() {
unchecked {
var hashCode = (onImage != null ? onImage.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (onChunk != null ? onChunk.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (onError != null ? onError.GetHashCode() : 0);
return hashCode;
}
}
}
public delegate void ImageChunkListener(ImageChunkEvent evt);
class _ImageListenerPair {
public ImageListener listener;
public ImageErrorListener errorListener;
public class ImageChunkEvent : Diagnosticable {
public ImageChunkEvent(
int cumulativeBytesLoaded,
int expectedTotalBytes
) {
D.assert(cumulativeBytesLoaded >= 0);
D.assert(expectedTotalBytes >= 0);
this.cumulativeBytesLoaded = cumulativeBytesLoaded;
this.expectedTotalBytes = expectedTotalBytes;
}
public readonly int cumulativeBytesLoaded;
public readonly int expectedTotalBytes;
public override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new IntProperty("cumulativeBytesLoaded", cumulativeBytesLoaded));
properties.add(new IntProperty("expectedTotalBytes", expectedTotalBytes));
}
}
public class ImageStream : Diagnosticable {

get { return _completer; }
}
List<_ImageListenerPair> _listeners;
List<ImageStreamListener> _listeners;
public void setCompleter(ImageStreamCompleter value) {
D.assert(_completer == null);

var initialListeners = _listeners;
_listeners = null;
foreach (_ImageListenerPair listenerPair in initialListeners) {
_completer.addListener(
listenerPair.listener,
listenerPair.errorListener
);
}
initialListeners.ForEach(_completer.addListener);
public void addListener(ImageListener listener, ImageErrorListener onError = null) {
public void addListener(ImageStreamListener listener) {
_completer.addListener(listener, onError);
_completer.addListener(listener);
_listeners = new List<_ImageListenerPair>();
_listeners = new List<ImageStreamListener>();
_listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});
_listeners.Add(listener);
public void removeListener(ImageListener listener) {
public void removeListener(ImageStreamListener listener) {
if (_completer != null) {
_completer.removeListener(listener);
return;

for (int i = 0; i < _listeners.Count; i++) {
if (_listeners[i].listener == listener) {
if (_listeners[i] == listener) {
_listeners.RemoveAt(i);
break;
}

ifPresent: _completer?.toStringShort(),
ifNull: "unresolved"
));
properties.add(new ObjectFlagProperty<List<_ImageListenerPair>>(
properties.add(new ObjectFlagProperty<List<ImageStreamListener>>(
"listeners",
_listeners,
ifPresent: $"{_listeners?.Count} listener{(_listeners?.Count == 1 ? "" : "s")}",

}
public abstract class ImageStreamCompleter : Diagnosticable {
internal readonly List<_ImageListenerPair> _listeners = new List<_ImageListenerPair>();
public ImageInfo currentImage;
public UIWidgetsErrorDetails currentError;
internal readonly List<ImageStreamListener> _listeners = new List<ImageStreamListener>();
internal ImageInfo _currentImage;
internal UIWidgetsErrorDetails _currentError;
public virtual void addListener(ImageListener listener, ImageErrorListener onError = null) {
_listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});
if (currentImage != null) {
public virtual void addListener(ImageStreamListener listener) {
_listeners.Add(listener);
if (_currentImage != null) {
listener(currentImage, true);
listener.onImage(_currentImage, true);
catch (Exception ex) {
catch (Exception exception) {
exception: ex
exception: exception
if (currentError != null && onError != null) {
if (_currentError != null && listener.onError != null) {
onError(currentError.exception);
listener.onError(_currentError.exception);
catch (Exception ex) {
catch (Exception exception) {
exception: ex,
exception: exception,
context: new ErrorDescription("when reporting an error to an image listener")
context: new ErrorDescription("by a synchronously-called image error listener")
)
);
}

public virtual void removeListener(ImageListener listener) {
for (int i = 0; i < _listeners.Count; i++) {
if (_listeners[i].listener == listener) {
public virtual void removeListener(ImageStreamListener listener) {
for (int i = 0; i < _listeners.Count; i += 1) {
if (_listeners[i] == listener) {
if (_listeners.isEmpty()) {
foreach (VoidCallback callback in _onLastListenerRemovedCallbacks) {
callback();
}
_onLastListenerRemovedCallbacks.Clear();
}
readonly List<VoidCallback> _onLastListenerRemovedCallbacks = new List<VoidCallback>();
public void addOnLastListenerRemovedCallback(VoidCallback callback) {
D.assert(callback != null);
_onLastListenerRemovedCallbacks.Add(callback);
}
public void removeOnLastListenerRemovedCallback(VoidCallback callback) {
D.assert(callback != null);
_onLastListenerRemovedCallbacks.Remove(callback);
}
currentImage = image;
_currentImage = image;
var localListeners = _listeners.Select(l => l.listener).ToList();
var localListeners = _listeners.Select(l => l).ToList();
listener(image, false);
listener.onImage(image, false);
}
catch (Exception ex) {
reportError(

Exception exception = null,
InformationCollector informationCollector = null,
bool silent = false) {
currentError = new UIWidgetsErrorDetails(
_currentError = new UIWidgetsErrorDetails(
exception: exception,
library: "image resource service",
context: context,

var localErrorListeners = _listeners.Select(l => l.errorListener).Where(l => l != null).ToList();
var localErrorListeners = _listeners
.Select(l => l.onError)
.Where(l => l != null)
.ToList();
UIWidgetsError.reportError(currentError);
UIWidgetsError.reportError(_currentError);
}
else {
foreach (var errorListener in localErrorListeners) {

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>>(
"current", _currentImage, ifNull: "unresolved", showName: false));
description.add(new ObjectFlagProperty<List<ImageStreamListener>>(
"listeners",
_listeners,
ifPresent: $"{_listeners.Count} listener{(_listeners.Count == 1 ? "" : "s")}"

}
}
// TODO: update stream
public class MultiFrameImageStreamCompleter : ImageStreamCompleter {
public MultiFrameImageStreamCompleter(
Future<Codec> codec,

TimeSpan delay = _frameDuration.Value - (timestamp - _shownTimestamp.Value);
delay = new TimeSpan((long) (delay.Ticks * scheduler_.timeDilation));
// TODO: time dilation
_timer = Timer.create(delay , ()=> _scheduleAppFrame());
_timer = Timer.create(delay, () => _scheduleAppFrame());
}
bool _isFirstFrame() {

_framesEmitted += 1;
}
public override void addListener(ImageListener listener, ImageErrorListener onError = null) {
public override void addListener(ImageStreamListener listener) {
base.addListener(listener, onError: onError);
base.addListener(listener);
public override void removeListener(ImageListener listener) {
public override void removeListener(ImageStreamListener listener) {
base.removeListener(listener);
if (!hasListeners) {
_timer?.cancel();

2
com.unity.uiwidgets/Runtime/rendering/image.cs


return;
}
ImageUtils.paintImage(
painting_.paintImage(
canvas: context.canvas,
rect: offset & size,
image: _image,

13
com.unity.uiwidgets/Runtime/widgets/fade_in_image.cs


_imageStream = provider.resolve(ImageUtils.createLocalImageConfiguration(state.context, size));
D.assert(_imageStream != null);
if (_imageStream.key != oldImageStream?.key) {
oldImageStream?.removeListener(_handleImageChanged);
_imageStream.addListener(_handleImageChanged);
}
// TODO: update
// if (_imageStream.key != oldImageStream?.key) {
// oldImageStream?.removeListener(_handleImageChanged);
// _imageStream.addListener(_handleImageChanged);
// }
}
void _handleImageChanged(ImageInfo imageInfo, bool synchronousCall) {

public void stopListening() {
_imageStream?.removeListener(_handleImageChanged);
// TODO: update
// _imageStream?.removeListener(_handleImageChanged);
}
}

41
com.unity.uiwidgets/Runtime/widgets/image.cs


ImageStream stream = provider.resolve(config);
void listener(ImageInfo image, bool sync) {
if (!completer.isCompleted) {
stream.removeListener(listener);
}
SchedulerBinding.instance.addPostFrameCallback(timeStamp => { stream.removeListener(listener); });
// TODO: update
// if (!completer.isCompleted) {
// stream.removeListener(listener);
// }
//
// SchedulerBinding.instance.addPostFrameCallback(timeStamp => { stream.removeListener(listener); });
}
void errorListener(Exception exception) {

stream.removeListener(listener);
// TODO: update
// stream.removeListener(listener);
if (onError != null) {
onError(exception);
}

}
}
stream.addListener(listener, onError: errorListener);
// TODO: update
// stream.addListener(listener, onError: errorListener);
return completer.future;
}
}

return;
}
if (_isListeningToStream) {
_imageStream.removeListener(_handleImageChanged);
}
// TODO: update
// if (_isListeningToStream) {
// _imageStream.removeListener(_handleImageChanged);
// }
_imageStream = newStream;
if (_isListeningToStream) {
_imageStream.addListener(_handleImageChanged);
}
// TODO: update
// _imageStream = newStream;
// if (_isListeningToStream) {
// _imageStream.addListener(_handleImageChanged);
// }
}
void _listenToStream() {

_imageStream.addListener(_handleImageChanged);
_isListeningToStream = true;
// TODO: update
// _imageStream.addListener(_handleImageChanged);
// _isListeningToStream = true;
}
void _stopListeningToStream() {

_imageStream.removeListener(_handleImageChanged);
// TODO: update
// _imageStream.removeListener(_handleImageChanged);
_isListeningToStream = false;
}

13
com.unity.uiwidgets/Runtime/painting/image_decoder.cs


using Unity.UIWidgets.async2;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.painting {
public partial class painting_ {
public static Future<ui.Image> decodeImageFromList(byte[] bytes) {
Future<Codec> codec = PaintingBinding.instance.instantiateImageCodec(bytes);
Future<FrameInfo> frameInfo = codec.then_<FrameInfo>(code => code.getNextFrame());
var result = frameInfo.then_<Image>(frame => FutureOr.value(frame.image));
return result;
}
}
}

3
com.unity.uiwidgets/Runtime/painting/image_decoder.cs.meta


fileFormatVersion: 2
guid: 8e4fc5e77c5b4ce3b8cdc8644ac3f398
timeCreated: 1610527018
正在加载...
取消
保存