浏览代码

Merge remote-tracking branch 'upstream/master'

/main
fzhangtj 6 年前
当前提交
52c16787
共有 57 个文件被更改,包括 3274 次插入840 次删除
  1. 6
      Runtime/animation/listener_helpers.mixin.njk
  2. 2
      Runtime/async/microtask_queue.cs
  3. 109
      Runtime/async/timer.cs
  4. 7
      Runtime/debugger/inpsector_panel.cs
  5. 23
      Runtime/editor/editor_window.cs
  6. 5
      Runtime/editor/surface.cs
  7. 18
      Runtime/engine/WidgetCanvas.cs
  8. 21
      Runtime/flow/raster_cache.cs
  9. 15
      Runtime/foundation/basic_types.cs
  10. 11
      Runtime/foundation/debug.cs
  11. 12
      Runtime/foundation/node.mixin.gen.cs
  12. 14
      Runtime/foundation/node.mixin.njk
  13. 3
      Runtime/gestures/binding.cs
  14. 8
      Runtime/gestures/drag_details.cs
  15. 22
      Runtime/gestures/events.cs
  16. 4
      Runtime/gestures/monodrag.cs
  17. 6
      Runtime/gestures/velocity_tracker.cs
  18. 33
      Runtime/painting/binding.cs
  19. 102
      Runtime/painting/decoration_image.cs
  20. 177
      Runtime/painting/image_cache.cs
  21. 636
      Runtime/painting/image_provider.cs
  22. 385
      Runtime/painting/image_stream.cs
  23. 84
      Runtime/promise/Promise.cs
  24. 95
      Runtime/promise/Promise_NonGeneric.cs
  25. 8
      Runtime/rendering/binding.cs
  26. 6
      Runtime/rendering/box.mixin.njk
  27. 289
      Runtime/rendering/image.cs
  28. 4
      Runtime/rendering/object.mixin.njk
  29. 6
      Runtime/rendering/proxy_box.mixin.njk
  30. 17
      Runtime/ui/geometry.cs
  31. 21
      Runtime/ui/painting/canvas.cs
  32. 25
      Runtime/ui/painting/canvas_impl.cs
  33. 7
      Runtime/ui/painting/draw_cmd.cs
  34. 67
      Runtime/ui/painting/image.cs
  35. 4
      Runtime/ui/pointer.cs
  36. 62
      Runtime/ui/window.cs
  37. 135
      Runtime/widgets/basic.cs
  38. 4
      Runtime/widgets/container.cs
  39. 296
      Runtime/widgets/image.cs
  40. 6
      Runtime/widgets/scroll_activity.cs
  41. 4
      Runtime/widgets/scroll_notification.mixin.njk
  42. 38
      Tests/Editor/CanvasAndLayers.cs
  43. 4
      Tests/Editor/EditableTextWiget.cs
  44. 7
      Tests/Editor/Widgets.cs
  45. 2
      Runtime/async/coroutine.cs.meta
  46. 371
      Runtime/async/coroutine.cs
  47. 678
      Runtime/ui/painting/GifDecoder.cs
  48. 11
      Runtime/ui/painting/GifDecoder.cs.meta
  49. 63
      Runtime/ui/painting/codec.cs
  50. 11
      Runtime/ui/painting/codec.cs.meta
  51. 148
      Runtime/ui/painting/codec_gif.cs
  52. 11
      Runtime/ui/painting/codec_gif.cs.meta
  53. 3
      Runtime/lib.meta
  54. 8
      Assets.meta
  55. 0
      /Runtime/async/coroutine.cs.meta

6
Runtime/animation/listener_helpers.mixin.njk


using System;
using System.Collections.Generic;
using UIWidgets.foundation;
using UIWidgets.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
namespace UIWidgets.animation {
namespace Unity.UIWidgets.animation {
{% macro AnimationLazyListenerMixin(with) %}
public abstract class AnimationLazyListenerMixin{{with | safe}} : {{with | safe}} {
int _listenerCounter = 0;

2
Runtime/async/microtask_queue.cs


namespace Unity.UIWidgets.async {
public class MicrotaskQueue {
private Queue<Action> _queue = new Queue<Action>();
readonly Queue<Action> _queue = new Queue<Action>();
public void scheduleMicrotask(Action action) {
this._queue.Enqueue(action);

109
Runtime/async/timer.cs


using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
static readonly TimerProvider _globalTimerProvider = new TimerProvider();
public static Timer run(TimeSpan duration, Action callback) {
return _globalTimerProvider.run(duration, callback);
public static double timeSinceStartup {
get {
#if UNITY_EDITOR
return EditorApplication.timeSinceStartup;
#else
return Time.realtimeSinceStartup;
#endif
}
public static Timer run(Action callback) {
return run(TimeSpan.Zero, callback);
}
public static Timer periodic(TimeSpan duration, Action callback) {
return _globalTimerProvider.periodic(duration, callback);
public static TimeSpan timespanSinceStartup => TimeSpan.FromSeconds(timeSinceStartup);
static readonly List<Action> _callbacks = new List<Action>();
public static void runInMain(Action callback) {
lock (_callbacks) {
_callbacks.Add(callback);
}
_globalTimerProvider.update();
Action[] callbacks;
lock (_callbacks) {
callbacks = _callbacks.ToArray();
_callbacks.Clear();
}
foreach (var callback in callbacks) {
try {
callback();
}
catch (Exception ex) {
Debug.LogError("Error to execute runInMain callback: " + ex);
}
}
}
}

public Timer run(TimeSpan duration, Action callback) {
var timer = new TimerImpl(duration, callback);
lock (this._queue) {
this._queue.enqueue(timer);
}
this._queue.enqueue(timer);
return timer;
}

lock (this._queue) {
this._queue.enqueue(timer);
}
this._queue.enqueue(timer);
var now = DateTime.Now;
var now = Timer.timeSinceStartup;
lock (this._queue) {
while (this._queue.count > 0 && this._queue.peek().deadline <= now) {
var timer = this._queue.dequeue();
if (timers == null) {
timers = new List<TimerImpl>();
}
timers.Add(timer);
while (this._queue.count > 0 && this._queue.peek().deadline <= now) {
var timer = this._queue.dequeue();
if (timers == null) {
timers = new List<TimerImpl>();
timers.Add(timer);
}
if (timers != null) {

if (appendList == null) {
appendList = new List<TimerImpl>();
}
appendList.Add(timer);
}
}

}
}
}
private DateTime _deadline;
private double _deadline;
this._deadline = DateTime.Now + duration;
this._deadline = Timer.timeSinceStartup + duration.TotalSeconds;
this._callback = callback;
this.periodic = periodic;
this._done = false;

}
public DateTime deadline
{
get { return _deadline; }
}
public double deadline => this._deadline;
public bool done
{
get { return _done; }
}
public bool done => this._done;
DateTime now = DateTime.Now;
if (!periodic)
{
this._done = true;
var now = Timer.timeSinceStartup;
if (!this.periodic) {
this._done = true;
}
try {

}
if (this.periodic) {
this._deadline = now + this.internval;
this._deadline = now + this.internval.TotalSeconds;
}
}

7
Runtime/debugger/inpsector_panel.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using UnityEditor;
using UnityEditor.IMGUI.Controls;

private List<DiagnosticsNode> m_Properties;
private InspectorInstanceRef m_SelectedNodeRef;
private bool m_VisibleToUser;
private DateTime m_LastPropertyRefresh = DateTime.MinValue;
private TimeSpan m_LastPropertyRefresh = TimeSpan.Zero;
private float m_SplitOffset = -1;

updateDetailTree();
if (treeType == WidgetTreeType.Render &&
DateTime.Now - m_LastPropertyRefresh > TimeSpan.FromMilliseconds(200))
Timer.timespanSinceStartup - m_LastPropertyRefresh > TimeSpan.FromMilliseconds(200))
m_LastPropertyRefresh = DateTime.Now;
m_LastPropertyRefresh = Timer.timespanSinceStartup;
m_Properties = m_SelectedNodeRef == null
? new List<DiagnosticsNode>()
: m_InspectorService.getProperties(m_SelectedNodeRef, m_GroupName);

23
Runtime/editor/editor_window.cs


float _lastWindowWidth;
float _lastWindowHeight;
readonly DateTime _epoch = new DateTime(Stopwatch.GetTimestamp());
readonly TimeSpan _epoch = new TimeSpan(Stopwatch.GetTimestamp());
readonly WindowCallbacks _windowCallbacks = new WindowCallbacks();
readonly TextInput _textInput = new TextInput();
readonly Rasterizer _rasterizer = new Rasterizer();

bool _alive;
public bool alive
{
get { return this._alive; }

void _beginFrame() {
if (this.onBeginFrame != null) {
this.onBeginFrame(new DateTime(Stopwatch.GetTimestamp()) - this._epoch);
this.onBeginFrame(new TimeSpan(Stopwatch.GetTimestamp()) - this._epoch);
}
this.flushMicrotasks();

if (evt.type == EventType.MouseDown) {
pointerData = new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.down,
kind: PointerDeviceKind.mouse,
device: evt.button,

} else if (evt.type == EventType.MouseUp || evt.rawType == EventType.MouseUp) {
pointerData = new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.up,
kind: PointerDeviceKind.mouse,
device: evt.button,

} else if (evt.type == EventType.MouseDrag) {
pointerData = new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.move,
kind: PointerDeviceKind.mouse,
device: evt.button,

} else if (evt.type == EventType.MouseMove) {
pointerData = new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.hover,
kind: PointerDeviceKind.mouse,
device: evt.button,

Timer.update();
using (this.getScope()) {
this.doUpdate();
this._doUpdate();
private void doUpdate() {
void _doUpdate() {
this._windowCallbacks.update();
this._timerProvider.update();
}

return periodic
? this._timerProvider.periodic(duration, callback)
: this._timerProvider.run(duration, callback);
}
public override IDisposable onUpdate(VoidCallback callback) {
return this._windowCallbacks.onUpdate(callback);
}
public void attachRootRenderBox(RenderBox root) {

5
Runtime/editor/surface.cs


public void Dispose() {
D.assert(this._renderTexture);
Object.DestroyImmediate(this._renderTexture);
this._renderTexture = null;
this._renderTexture = ObjectUtils.SafeDestroy(this._renderTexture);
D.assert(this._canvas != null);
this._canvas = null;
}

18
Runtime/engine/WidgetCanvas.cs


using System;
using Unity.UIWidgets.async;
using Unity.UIWidgets.editor;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;

IPointerEnterHandler, IPointerExitHandler
{
private WindowAdapter _windowAdapter;
private PaintingBinding _paintingBinding;
private Texture _texture;
private Vector2 _lastMouseMove;
private bool _mouseEntered;

if (_windowAdapter == null)
{
this._paintingBinding = new PaintingBinding(null);
_paintingBinding.initInstances();
_windowAdapter = new UIWidgetWindowAdapter(this);
}

private void OnMouseOver()
{
// Debug.Log(string.Format("mouse move {0} {1}", pos.x, pos.y));
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.hover,
kind: PointerDeviceKind.mouse,
device: getMouseButtonDown(),

EventSystem.current.SetSelectedGameObject(gameObject, eventData);
var position = getPointPosition(eventData);
this._windowAdapter.PostPointerEvent(new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.down,
kind: PointerDeviceKind.mouse,
device: (int) eventData.button,

{
var position = getPointPosition(eventData);
this._windowAdapter.PostPointerEvent(new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.up,
kind: PointerDeviceKind.mouse,
device: (int) eventData.button,

{
var position = getPointPosition(eventData);
this._windowAdapter.PostPointerEvent(new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.move,
kind: PointerDeviceKind.mouse,
device: (int) eventData.button,

_lastMouseMove = eventData.position;
var position = getPointPosition(eventData);
this._windowAdapter.PostPointerEvent(new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.hover,
kind: PointerDeviceKind.mouse,
device: (int) eventData.button,

_mouseEntered = false;
var position = getPointPosition(eventData);
this._windowAdapter.PostPointerEvent(new PointerData(
timeStamp: DateTime.Now,
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.hover,
kind: PointerDeviceKind.mouse,
device: (int) eventData.button,

21
Runtime/flow/raster_cache.cs


using Unity.UIWidgets.ui;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Object = UnityEngine.Object;
public RasterCacheResult(Texture texture, Rect logicalRect, float devicePixelRatio) {
D.assert(texture != null);
public RasterCacheResult(Image image, Rect logicalRect, float devicePixelRatio) {
D.assert(image != null);
this.texture = texture;
this.image = image;
public readonly Texture texture;
public readonly Image image;
public readonly Rect logicalRect;

var textureWidth = Mathf.CeilToInt((float) bounds.width * this.devicePixelRatio);
var textureHeight = Mathf.CeilToInt((float) bounds.height * this.devicePixelRatio);
D.assert(this.texture.width == textureWidth);
D.assert(this.texture.height == textureHeight);
D.assert(this.image.width == textureWidth);
D.assert(this.image.height == textureHeight);
return true;
});

canvas.resetMatrix();
canvas.drawImage(this.texture, bounds.topLeft, new Paint());
canvas.drawImage(this.image, bounds.topLeft, new Paint());
}
finally {
canvas.restore();

canvas.drawPicture(picture);
canvas.flush();
return new RasterCacheResult(renderTexture, bounds, devicePixelRatio);
return new RasterCacheResult(new Image(renderTexture), bounds, devicePixelRatio);
}
public void sweepAfterFrame() {

foreach (var entry in dead) {
this._cache.Remove(entry.Key);
if (entry.Value.image != null) {
Object.DestroyImmediate(entry.Value.image.texture);
entry.Value.image.image.Dispose();
}
}
}

if (entry.Value.image != null) {
Object.DestroyImmediate(entry.Value.image.texture);
entry.Value.image.image.Dispose();
}
}
this._cache.Clear();

15
Runtime/foundation/basic_types.cs


using System;
using System.Collections;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Unity.UIWidgets.foundation {

public static class ObjectUtils {
public static T SafeDestroy<T>(T obj) where T : Object {
if (Application.isEditor) {
Object.DestroyImmediate(obj);
} else {
Object.Destroy(obj);
}
return null;
}
}
public static class CollectionUtils {
public static V putIfAbsent<K, V>(this IDictionary<K, V> it, K key, Func<V> ifAbsent) {

11
Runtime/foundation/debug.cs


using System;
using System.Diagnostics;
using System.Linq;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;

[Serializable]
public class AssertionError : Exception {
public AssertionError(string message) : base(message) {
}
public override string StackTrace {
get {
var stackTrace = base.StackTrace;
var lines = stackTrace.Split('\n');
var strippedLines = lines.Skip(1);
return string.Join("\n", strippedLines);
}
}
}
}

12
Runtime/foundation/node.mixin.gen.cs


using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.UIWidgets.async;
namespace Unity.UIWidgets.foundation {

return this._canonical;
}
var weakReference = _canonicalObjects.putIfAbsent(this._getDependencyList(), () => {
return new WeakReference(this);
});
var weakReference = _canonicalObjects.putIfAbsent(this._getDependencyList(), () => new WeakReference(this));
if (weakReference.Target == null) {
weakReference.Target = this;
}

~CanonicalMixinDiagnosticableTree() {
if (this == this._canonical) {
_canonicalObjects.Remove(this._dependencyList);
if (object.ReferenceEquals(this, this._canonical)) {
var dependencyList = this._dependencyList;
if (dependencyList != null) {
Timer.runInMain(() => { _canonicalObjects.Remove(dependencyList); });
}
}
}

14
Runtime/foundation/node.mixin.njk


using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.UIWidgets.async;
namespace UIWidgets.foundation {
namespace Unity.UIWidgets.foundation {
{% macro AbstractNodeMixin(with) %}
{% set className = 'AbstractNode' if with == '' else 'AbstractNodeMixin' + with %}

return this._canonical;
}
var weakReference = _canonicalObjects.putIfAbsent(this._getDependencyList(), () => {
return new WeakReference(this);
});
var weakReference = _canonicalObjects.putIfAbsent(this._getDependencyList(), () => new WeakReference(this));
if (weakReference.Target == null) {
weakReference.Target = this;
}

~CanonicalMixin{{with}}() {
if (this == this._canonical) {
_canonicalObjects.Remove(this._dependencyList);
if (object.ReferenceEquals(this, this._canonical)) {
var dependencyList = this._dependencyList;
if (dependencyList != null) {
Timer.runInMain(() => { _canonicalObjects.Remove(dependencyList); });
}
}
}

3
Runtime/gestures/binding.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;

}
this._pendingPointerEvents.Enqueue(
new PointerCancelEvent(timeStamp: DateTime.Now, pointer: pointer));
new PointerCancelEvent(timeStamp: Timer.timespanSinceStartup, pointer: pointer));
}
void _flushPointerEventQueue() {

8
Runtime/gestures/drag_details.cs


public delegate void GestureDragDownCallback(DragDownDetails details);
public class DragStartDetails {
public DragStartDetails(DateTime sourceTimeStamp, Offset globalPosition = null) {
public DragStartDetails(TimeSpan sourceTimeStamp, Offset globalPosition = null) {
public readonly DateTime sourceTimeStamp;
public readonly TimeSpan sourceTimeStamp;
public readonly Offset globalPosition;

public class DragUpdateDetails {
public DragUpdateDetails(
DateTime sourceTimeStamp,
TimeSpan sourceTimeStamp,
Offset delta = null,
double? primaryDelta = null,
Offset globalPosition = null) {

|| primaryDelta == this.delta.dy && this.delta.dx == 0.0);
}
public readonly DateTime sourceTimeStamp;
public readonly TimeSpan sourceTimeStamp;
public readonly Offset delta;

22
Runtime/gestures/events.cs


namespace Unity.UIWidgets.gestures {
public abstract class PointerEvent {
public PointerEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

this.synthesized = synthesized;
}
public readonly DateTime timeStamp;
public readonly TimeSpan timeStamp;
public readonly int pointer;

public class PointerAddedEvent : PointerEvent {
public PointerAddedEvent(
DateTime timeStamp,
TimeSpan timeStamp,
PointerDeviceKind kind = PointerDeviceKind.touch,
int device = 0,
Offset position = null

public class PointerRemovedEvent : PointerEvent {
public PointerRemovedEvent(
DateTime timeStamp,
TimeSpan timeStamp,
PointerDeviceKind kind = PointerDeviceKind.touch,
int device = 0
) : base(

public class PointerHoverEvent : PointerEvent
{
public PointerHoverEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerEnterEvent : PointerEvent
{
public PointerEnterEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerLeaveEvent : PointerEvent
{
public PointerLeaveEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerDownEvent : PointerEvent {
public PointerDownEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerMoveEvent : PointerEvent {
public PointerMoveEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerUpEvent : PointerEvent {
public PointerUpEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

public class PointerCancelEvent : PointerEvent {
public PointerCancelEvent(
DateTime timeStamp,
TimeSpan timeStamp,
int pointer = 0,
PointerDeviceKind kind = PointerDeviceKind.mouse,
int device = 0,

4
Runtime/gestures/monodrag.cs


_DragState _state = _DragState.ready;
Offset _initialPosition;
protected Offset _pendingDragOffset;
DateTime _lastPendingEventTimestamp;
TimeSpan _lastPendingEventTimestamp;
protected abstract bool _isFlingGesture(VelocityEstimate estimate);
protected abstract Offset _getDeltaForDetails(Offset delta);

Offset delta = this._pendingDragOffset;
var timestamp = this._lastPendingEventTimestamp;
this._pendingDragOffset = Offset.zero;
this._lastPendingEventTimestamp = default(DateTime);
this._lastPendingEventTimestamp = default(TimeSpan);
if (this.onStart != null) {
this.invokeCallback<object>("onStart", () => {
this.onStart(new DragStartDetails(

6
Runtime/gestures/velocity_tracker.cs


}
class _PointAtTime {
internal _PointAtTime(Offset point, DateTime time) {
internal _PointAtTime(Offset point, TimeSpan time) {
D.assert(point != null);
this.point = point;
this.time = time;

public readonly DateTime time;
public readonly TimeSpan time;
public override string ToString() {
return string.Format("_PointAtTime({0} at {1})", this.point, this.time);

readonly List<_PointAtTime> _samples = Enumerable.Repeat<_PointAtTime>(null, _historySize).ToList();
int _index = 0;
public void addPosition(DateTime time, Offset position) {
public void addPosition(TimeSpan time, Offset position) {
this._index += 1;
if (this._index == _historySize) {
this._index = 0;

33
Runtime/painting/binding.cs


using Unity.UIWidgets.ui;
using Unity.UIWidgets.gestures;
public class PaintingBinding {
public PaintingBinding(Window window) {
this._window = window;
public class PaintingBinding : GestureBinding {
public static new PaintingBinding instance {
get { return (PaintingBinding) GestureBinding.instance; }
set { GestureBinding.instance = value; }
private static PaintingBinding _instance;
public readonly Window _window;
public static PaintingBinding instance {
get { return _instance; }
public PaintingBinding() {
private ImageCache _imageCache;
ImageCache _imageCache;
get { return _imageCache; }
}
get {
if (this._imageCache == null) {
this._imageCache = this.createImageCache();
}
public ImageCache createImageCache() {
return new ImageCache();
return this._imageCache;
}
public void initInstances() {
_instance = this;
_imageCache = createImageCache();
protected virtual ImageCache createImageCache() {
return new ImageCache();
}
}
}

102
Runtime/painting/decoration_image.cs


using System;
using Unity.UIWidgets.foundation;
/// How to paint any portions of a box not covered by an image.
/// Repeat the image in both the x and y directions until the box is filled.
/// Repeat the image in the x direction until the box is filled horizontally.
/// Repeat the image in the y direction until the box is filled vertically.
/// Leave uncovered portions of the box transparent.
noRepeat,
}

}
public static class DecorationImageUtil {
public static class ImageUtils {
Canvas canvas,
Rect rect,
ui.Image image,
// ColorFilter colorFileter,
BoxFit? fit,
Rect centerSlice,
Canvas canvas = null,
Rect rect = null,
Image image = null,
double scale = 1.0,
ColorFilter colorFilter = null,
BoxFit? fit = null,
ImageRepeat repeat = ImageRepeat.noRepeat
// bool flipHorizontally = false
Rect centerSlice = null,
ImageRepeat repeat = ImageRepeat.noRepeat,
bool invertColors = false,
FilterMode filterMode = FilterMode.Point
if (rect.isEmpty)
D.assert(canvas != null);
D.assert(rect != null);
D.assert(image != null);
alignment = alignment ?? Alignment.center;
if (rect.isEmpty) {
alignment = alignment ?? Alignment.center;
}
Size outputSize = rect.size;
Size inputSize = new Size(image.width, image.height);
Offset sliceBorder = null;

inputSize -= sliceBorder;
}
if (fit == null) {
fit = centerSlice == null ? BoxFit.scaleDown : BoxFit.fill;
}
FittedSizes fittedSizes = FittedSizes.applyBoxFit(fit.Value, inputSize, outputSize);
Size sourceSize = fittedSizes.source;
fit = fit ?? (centerSlice == null ? BoxFit.scaleDown : BoxFit.fill);
D.assert(centerSlice == null || (fit != BoxFit.none && fit != BoxFit.cover));
FittedSizes fittedSizes = FittedSizes.applyBoxFit(fit.Value, inputSize / scale, outputSize);
Size sourceSize = fittedSizes.source * scale;
D.assert(sourceSize == inputSize,
"centerSlice was used with a BoxFit that does not guarantee that the image is fully visible.");
}
if (repeat != ImageRepeat.noRepeat && destinationSize == outputSize) {

Paint paint = new Paint(); // ..isAntiAlias = false;
// if (colorFilter != null)
// paint.colorFilter = colorFilter;
Paint paint = new Paint();
if (colorFilter != null) {
paint.colorFilter = colorFilter;
}
// Use the "low" quality setting to scale the image, which corresponds to
// bilinear interpolation, rather than the default "none" which corresponds
// to nearest-neighbor.
// paint.filterQuality = FilterQuality.low;
paint.filterMode = filterMode;
}
double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0;

Offset destinationPosition = rect.topLeft.translate(dx, dy);
Rect destinationRect = destinationPosition & destinationSize;
bool needSave = repeat != ImageRepeat.noRepeat;
if (needSave)
if (needSave) {
if (repeat != ImageRepeat.noRepeat)
}
if (repeat != ImageRepeat.noRepeat) {
}
canvas.drawImageRect(image.texture, sourceRect, tileRect, paint);
canvas.drawImageRect(image, sourceRect, tileRect, paint);
}
else {
// todo
} else {
// canvas.drawImageNine(image, centerSlice, tileRect, paint);
canvas.drawImageNine(image, centerSlice, tileRect, paint);
if (needSave)
if (needSave) {
}
public static List<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect,
static IEnumerable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect,
List<Rect> tileRects = new List<Rect>();
tileRects.Add(fundamentalRect);
return tileRects;
yield return fundamentalRect;
yield break;
}
int startX = 0;

double strideY = fundamentalRect.height;
if (repeat == ImageRepeat.repeat || repeat == ImageRepeat.repeatX) {
startX = (int) Math.Floor((outputRect.left - fundamentalRect.left) / strideX);
stopX = (int) Math.Ceiling((outputRect.right - fundamentalRect.right) / strideX);
startX = ((outputRect.left - fundamentalRect.left) / strideX).floor();
stopX = ((outputRect.right - fundamentalRect.right) / strideX).ceil();
startY = (int) Math.Floor((outputRect.top - fundamentalRect.top) / strideY);
stopY = (int) Math.Ceiling((outputRect.bottom - fundamentalRect.bottom) / strideY);
startY = ((outputRect.top - fundamentalRect.top) / strideY).floor();
stopY = ((outputRect.bottom - fundamentalRect.bottom) / strideY).ceil();
tileRects.Add(fundamentalRect.shift(new Offset(i * strideX, j * strideY)));
yield return fundamentalRect.shift(new Offset(i * strideX, j * strideY));
return tileRects;
}
}
}

177
Runtime/painting/image_cache.cs


using System;
using Object = System.Object;
using Unity.UIWidgets.foundation;
private const int _kDefaultSize = 1000;
private const int _kDefaultSizeBytes = 20 << 20; // 20 MiB
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
public Dictionary<Object, ImageStreamCompleter> _pendingImages =
new Dictionary<Object, ImageStreamCompleter>();
readonly Dictionary<object, ImageStreamCompleter> _pendingImages =
new Dictionary<object, ImageStreamCompleter>();
public Dictionary<Object, _CachedImage> _cache = new Dictionary<Object, _CachedImage>();
public LinkedList<Object> _lruKeys = new LinkedList<Object>();
readonly Dictionary<object, _CachedImage> _cache = new Dictionary<object, _CachedImage>();
readonly LinkedList<object> _lruKeys = new LinkedList<object>();
private int _maximumSize = _kDefaultSize;
int _maximumSize = _kDefaultSize;
get { return _maximumSize; }
get => this._maximumSize;
if (value == maximumSize) {
D.assert(value >= 0);
if (value == this._maximumSize) {
_maximumSize = value;
if (maximumSize == 0) {
_cache.Clear();
_lruKeys.Clear();
_currentSizeBytes = 0;
}
else {
_checkCacheSize();
this._maximumSize = value;
if (this._maximumSize == 0) {
this.clear();
} else {
this._checkCacheSize();
public int currentSize {
get { return _cache.Count; }
}
public int currentSize => this._cache.Count;
private int _maximumSizeBytes = _kDefaultSizeBytes;
int _maximumSizeBytes = _kDefaultSizeBytes;
get { return _maximumSizeBytes; }
get { return this._maximumSizeBytes; }
if (value == _maximumSizeBytes) {
D.assert(value >= 0);
if (value == this._maximumSizeBytes) {
_maximumSizeBytes = value;
if (_maximumSizeBytes == 0) {
_cache.Clear();
_lruKeys.Clear();
_currentSizeBytes = 0;
}
else {
_checkCacheSize();
this._maximumSizeBytes = value;
if (this._maximumSizeBytes == 0) {
this.clear();
} else {
this._checkCacheSize();
private int _currentSizeBytes;
int _currentSizeBytes;
public int currentSizeBytes {
get { return _currentSizeBytes; }
}
public int currentSizeBytes => this._currentSizeBytes;
_cache.Clear();
_lruKeys.Clear();
_currentSizeBytes = 0;
this._cache.Clear();
this._lruKeys.Clear();
this._currentSizeBytes = 0;
public delegate ImageStreamCompleter Loader();
public bool evict(object key) {
D.assert(key != null);
public ImageStreamCompleter putIfAbsent(Object key, Loader loader) {
ImageStreamCompleter result;
if (_pendingImages.TryGetValue(key, out result)) {
if (this._cache.TryGetValue(key, out var image)) {
this._currentSizeBytes -= image.sizeBytes;
this._cache.Remove(image.node);
this._lruKeys.Remove(image.node);
return true;
}
return false;
}
public ImageStreamCompleter putIfAbsent(object key, Func<ImageStreamCompleter> loader) {
D.assert(key != null);
D.assert(loader != null);
if (this._pendingImages.TryGetValue(key, out var result)) {
_CachedImage image;
if (_cache.TryGetValue(key, out image)) {
if (this._cache.TryGetValue(key, out var image)) {
_lruKeys.Remove(key);
_lruKeys.AddLast(key);
}
if (image != null) {
this._lruKeys.Remove(image.node);
image.node = this._lruKeys.AddLast(key);
if (maximumSize > 0 && maximumSizeBytes > 0) {
_pendingImages[key] = result;
result.addListener((info, syncCall) => {
// int imageSize = info.image == null ? 0 : info.image.height * info.image.width * 4;
// now we use length or raw bytes array as image size
int imageSize = info.image == null ? 0 : info.image.rawData.Length;
_CachedImage cachedImage = new _CachedImage(result, imageSize);
if (maximumSizeBytes > 0 && imageSize > maximumSizeBytes) {
_maximumSize = imageSize + 1000;
if (this._maximumSize > 0 && this._maximumSizeBytes > 0) {
D.assert(!this._pendingImages.ContainsKey(key));
this._pendingImages[key] = result;
ImageListener listener = null;
listener = (info, syncCall) => {
result.removeListener(listener);
D.assert(this._pendingImages.ContainsKey(key));
this._pendingImages.Remove(key);
int imageSize = info?.image == null ? 0 : info.image.width & info.image.height * 4;
_CachedImage cachedImage = new _CachedImage {
completer = result,
sizeBytes = imageSize,
};
// If the image is bigger than the maximum cache size, and the cache size
// is not zero, then increase the cache size to the size of the image plus
// some change.
if (this._maximumSizeBytes > 0 && imageSize > this._maximumSizeBytes) {
this._maximumSizeBytes = imageSize + 1000;
_currentSizeBytes += imageSize;
_pendingImages.Remove(key);
_cache[key] = cachedImage;
_lruKeys.AddLast(key);
this._currentSizeBytes += imageSize;
D.assert(!this._cache.ContainsKey(key));
this._cache[key] = cachedImage;
cachedImage.node = this._lruKeys.AddLast(key);
}, null);
};
result.addListener(listener);
}
return result;

while (_currentSizeBytes > _maximumSizeBytes || _cache.Count > _maximumSize) {
Object key = _lruKeys.First.Value; // get the LRU item
_CachedImage image = _cache[key];
bool removed = _cache.Remove(key);
if (image != null && removed) {
_currentSizeBytes -= image.sizeBytes;
_lruKeys.Remove(key);
}
while (this._currentSizeBytes > this._maximumSizeBytes || this._cache.Count > this._maximumSize) {
var node = this._lruKeys.First;
var key = node.Value; // get the LRU item
D.assert(this._cache.ContainsKey(key));
_CachedImage image = this._cache[key];
D.assert(node == image.node);
this._currentSizeBytes -= image.sizeBytes;
this._cache.Remove(key);
this._lruKeys.Remove(image.node);
}
}
public class _CachedImage {
public _CachedImage(ImageStreamCompleter completer, int sizeBytes) {
this.completer = completer;
this.sizeBytes = sizeBytes;
D.assert(this._currentSizeBytes >= 0);
D.assert(this._cache.Count <= this.maximumSize);
D.assert(this._currentSizeBytes <= this.maximumSizeBytes);
}
class _CachedImage {
public LinkedListNode<object> node;
}
}

636
Runtime/painting/image_provider.cs


using System.Collections.Generic;
using RSG;
using System.Net;
using System.IO;
using Unity.UIWidgets.lib.cache_manager;
using System.Collections;
using System.Text;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using UnityEngine;
using UnityEngine.Networking;
public class ImageConfiguration : IEquatable<ImageConfiguration> {
public ImageConfiguration(
AssetBundle bundle = null,
double? devicePixelRatio = null,
Locale locale = null,
Size size = null,
RuntimePlatform? platform = null
) {
this.bundle = bundle;
this.devicePixelRatio = devicePixelRatio;
this.locale = locale;
this.size = size;
this.platform = platform;
}
public ImageConfiguration copyWith(
AssetBundle bundle = null,
double? devicePixelRatio = null,
Locale locale = null,
Size size = null,
RuntimePlatform? platform = null
) {
return new ImageConfiguration(
bundle: bundle ? bundle : this.bundle,
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
locale: locale ?? this.locale,
size: size ?? this.size,
platform: platform ?? this.platform
);
}
public readonly AssetBundle bundle;
public readonly double? devicePixelRatio;
public readonly Locale locale;
public readonly Size size;
public readonly RuntimePlatform? platform;
public static readonly ImageConfiguration empty = new ImageConfiguration();
public bool Equals(ImageConfiguration other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(this.bundle, other.bundle) && this.devicePixelRatio.Equals(other.devicePixelRatio) &&
Equals(this.locale, other.locale) && Equals(this.size, other.size) &&
this.platform == other.platform;
}
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((ImageConfiguration) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = (this.bundle != null ? this.bundle.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.devicePixelRatio.GetHashCode();
hashCode = (hashCode * 397) ^ (this.locale != null ? this.locale.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (this.size != null ? this.size.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.platform.GetHashCode();
return hashCode;
}
}
public static bool operator ==(ImageConfiguration left, ImageConfiguration right) {
return Equals(left, right);
}
public static bool operator !=(ImageConfiguration left, ImageConfiguration right) {
return !Equals(left, right);
}
public override string ToString() {
var result = new StringBuilder();
result.Append("ImageConfiguration(");
bool hasArguments = false;
if (this.bundle != null) {
if (hasArguments) {
result.Append(", ");
}
result.Append($"bundle: {this.bundle}");
hasArguments = true;
}
if (this.devicePixelRatio != null) {
if (hasArguments) {
result.Append(", ");
}
result.Append($"devicePixelRatio: {this.devicePixelRatio:F1}");
hasArguments = true;
}
if (this.locale != null) {
if (hasArguments) {
result.Append(", ");
}
result.Append($"locale: {this.locale}");
hasArguments = true;
}
if (this.size != null) {
if (hasArguments) {
result.Append(", ");
}
result.Append($"size: {this.size}");
hasArguments = true;
}
if (this.platform != null) {
if (hasArguments) {
result.Append(", ");
}
result.Append($"platform: {this.platform}");
hasArguments = true;
}
result.Append(")");
return result.ToString();
}
}
public abstract class ImageProvider {
public abstract ImageStream resolve(ImageConfiguration configuration);
}

D.assert(configuration != null);
T obtainedKey;
obtainedKey = obtainKey(configuration);
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(obtainedKey, () => load(obtainedKey)));
T obtainedKey = default;
this.obtainKey(configuration).Then(key => {
obtainedKey = key;
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => this.load(key)));
}).Catch(ex => {
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: ex,
library: "services library",
context: "while resolving an image",
silent: true,
informationCollector: information => {
information.AppendLine($"Image provider: {this}");
information.AppendLine($"Image configuration: {configuration}");
if (obtainedKey != null) {
information.AppendLine($"Image key: {obtainedKey}");
}
}
));
});
public abstract ImageStreamCompleter load(T key);
public IPromise<bool> evict(ImageCache cache = null, ImageConfiguration configuration = null) {
configuration = configuration ?? ImageConfiguration.empty;
cache = cache ?? PaintingBinding.instance.imageCache;
return this.obtainKey(configuration).Then(key => cache.evict(key));
}
protected abstract ImageStreamCompleter load(T key);
protected abstract IPromise<T> obtainKey(ImageConfiguration configuration);
}
public class AssetBundleImageKey : IEquatable<AssetBundleImageKey> {
public AssetBundleImageKey(
AssetBundle bundle,
string name,
double scale
) {
D.assert(bundle != null);
D.assert(name != null);
D.assert(scale >= 0.0);
this.bundle = bundle;
this.name = name;
this.scale = scale;
}
public readonly AssetBundle bundle;
public readonly string name;
public readonly double scale;
public bool Equals(AssetBundleImageKey other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(this.bundle, other.bundle) && string.Equals(this.name, other.name) &&
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((AssetBundleImageKey) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = (this.bundle != null ? this.bundle.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (this.name != null ? this.name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.scale.GetHashCode();
return hashCode;
}
}
public static bool operator ==(AssetBundleImageKey left, AssetBundleImageKey right) {
return Equals(left, right);
}
public static bool operator !=(AssetBundleImageKey left, AssetBundleImageKey right) {
return !Equals(left, right);
}
public override string ToString() {
return $"{this.GetType()}(bundle: {this.bundle}, name: \"{this.name}\", scale: {this.scale})";
}
}
public abstract class AssetBundleImageProvider : ImageProvider<AssetBundleImageKey> {
protected AssetBundleImageProvider() {
}
public abstract T obtainKey(ImageConfiguration configuration);
protected override ImageStreamCompleter load(AssetBundleImageKey key) {
return new MultiFrameImageStreamCompleter(
codec: this._loadAsync(key),
scale: key.scale,
informationCollector: information => {
information.AppendLine($"Image provider: {this}");
information.Append($"Image key: {key}");
}
);
}
protected virtual IPromise<Codec> _loadAsync(AssetBundleImageKey key) {
var coroutine = Window.instance.startCoroutine(this._loadAssetAsync(key));
return coroutine.promise.Then(result => {
if (result == null) {
if (key.bundle == null) {
throw new Exception($"Unable to find asset \"{key.name}\" from Resources folder");
}
throw new Exception($"Unable to find asset \"{key.name}\" from asset bundle \"{key.bundle}\"");
}
return CodecUtils.getCodec((Texture2D) result);
});
}
IEnumerator _loadAssetAsync(AssetBundleImageKey key) {
if (key.bundle == null) {
yield return Resources.LoadAsync<Texture2D>(key.name);
yield break;
}
yield return key.bundle.LoadAssetAsync(key.name);
}
public class NetworkImage : ImageProvider<NetworkImage> {
public NetworkImage(string url, Dictionary<string, string> headers, double scale = 1.0) {
public class NetworkImage : ImageProvider<NetworkImage>, IEquatable<NetworkImage> {
public NetworkImage(string url,
double scale = 1.0,
IDictionary<string, string> headers = null) {
D.assert(url != null);
this.headers = headers;
this.headers = headers;
/// The URL from which the image will be fetched.
string url;
public readonly string url;
/// The scale to place in the [ImageInfo] object of the image.
double scale;
public readonly double scale;
/// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
Dictionary<string, string> headers;
public readonly IDictionary<string, string> headers;
public override NetworkImage obtainKey(ImageConfiguration configuration) {
return this;
protected override IPromise<NetworkImage> obtainKey(ImageConfiguration configuration) {
return Promise<NetworkImage>.Resolved(this);
public override ImageStreamCompleter load(NetworkImage key) {
// return new OneFrameImageStreamCompleter(_loadAsync(key));
return new OneFrameImageStreamCompleter(_loadAsyncWithCache(key));
protected override ImageStreamCompleter load(NetworkImage key) {
return new MultiFrameImageStreamCompleter(
codec: this._loadAsync(key),
scale: key.scale,
informationCollector: information => {
information.AppendLine($"Image provider: {this}");
information.Append($"Image key: {key}");
}
);
public static IPromise<ImageInfo> _loadAsync(NetworkImage key) {
var promise = new Promise<ImageInfo>(); // Create promise.
using (var client = new WebClient()) {
client.DownloadDataCompleted += // Monitor event for download completed.
(s, ev) => {
if (ev.Error != null) {
promise.Reject(ev.Error); // Error during download, reject the promise.
}
else {
var bytes = ev.Result;
var imageInfo = new ImageInfo(new ui.Image(
bytes
));
promise.Resolve(imageInfo); // Downloaded completed successfully, resolve the promise.
IPromise<Codec> _loadAsync(NetworkImage key) {
var coroutine = Window.instance.startCoroutine(this._loadBytes(key));
return coroutine.promise.Then(obj => {
if (obj is byte[] bytes) {
return CodecUtils.getCodec(bytes);
}
return CodecUtils.getCodec((Texture2D) obj);
});
}
IEnumerator _loadBytes(NetworkImage key) {
D.assert(key == this);
var uri = new Uri(key.url);
if (uri.LocalPath.EndsWith(".gif")) {
using (var www = UnityWebRequest.Get(uri)) {
if (this.headers != null) {
foreach (var header in this.headers) {
www.SetRequestHeader(header.Key, header.Value);
};
}
yield return www.SendWebRequest();
client.DownloadDataAsync(new Uri(key.url)); // Initiate async op.
if (www.isNetworkError || www.isHttpError) {
throw new Exception($"Failed to load from url \"{uri}\": {www.error}");
}
var data = www.downloadHandler.data;
yield return data;
}
yield break;
return promise; // Return the promise so the caller can await resolution (or error).
using (var www = UnityWebRequestTexture.GetTexture(uri)) {
if (this.headers != null) {
foreach (var header in this.headers) {
www.SetRequestHeader(header.Key, header.Value);
}
}
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError) {
throw new Exception($"Failed to load from url \"{uri}\": {www.error}");
}
var data = ((DownloadHandlerTexture) www.downloadHandler).texture;
yield return data;
}
public static IPromise<ImageInfo> _loadAsyncWithCache(NetworkImage key) {
var cache = CacheManager.getInstance();
var result = cache.getMeta(key.url).
Then(meta => cache.downloadFileIfNeeded(meta)).
Then(meta => cache.updateMeta(meta)).
Then(path => cache.loadCacheFile(path));
return result;
public bool Equals(NetworkImage other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.url, other.url) && 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((NetworkImage) obj);
}
public override int GetHashCode() {
unchecked {
return ((this.url != null ? this.url.GetHashCode() : 0) * 397) ^ this.scale.GetHashCode();
}
}
public static bool operator ==(NetworkImage left, NetworkImage right) {
return Equals(left, right);
}
public static bool operator !=(NetworkImage left, NetworkImage right) {
return !Equals(left, right);
return "NetworkImage with Url: " + this.url;
return $"runtimeType(\"{this.url}\", scale: {this.scale})";
}
}
public class FileImage : ImageProvider<FileImage>, IEquatable<FileImage> {
public FileImage(string file, double scale = 1.0) {
D.assert(file != null);
this.file = file;
this.scale = scale;
}
public readonly string file;
public readonly double scale;
protected override IPromise<FileImage> obtainKey(ImageConfiguration configuration) {
return Promise<FileImage>.Resolved(this);
}
protected override ImageStreamCompleter load(FileImage key) {
return new MultiFrameImageStreamCompleter(this._loadAsync(key),
scale: key.scale,
informationCollector: information => {
information.AppendLine($"Path: {this.file}");
});
}
IPromise<Codec> _loadAsync(FileImage key) {
var coroutine = Window.instance.startCoroutine(this._loadBytes(key));
return coroutine.promise.Then(obj => {
if (obj is byte[] bytes) {
return CodecUtils.getCodec(bytes);
}
return CodecUtils.getCodec((Texture2D) obj);
});
public bool Equals(NetworkImage other) {
return this.url.Equals(other.url) && this.scale.Equals(other.scale);
IEnumerator _loadBytes(FileImage key) {
D.assert(key == this);
var uri = "file://" + key.file;
if (uri.EndsWith(".gif")) {
using (var www = UnityWebRequest.Get(uri)) {
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError) {
throw new Exception($"Failed to get file \"{uri}\": {www.error}");
}
var data = www.downloadHandler.data;
yield return data;
}
yield break;
}
using (var www = UnityWebRequestTexture.GetTexture(uri)) {
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError) {
throw new Exception($"Failed to get file \"{uri}\": {www.error}");
}
var data = ((DownloadHandlerTexture) www.downloadHandler).texture;
yield return data;
}
}
public bool Equals(FileImage other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.file, other.file) && this.scale.Equals(other.scale);
if (object.ReferenceEquals(null, obj)) return false;
if (object.ReferenceEquals(this, obj)) return true;
return obj is NetworkImage && this.Equals((NetworkImage) obj);
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return this.Equals((FileImage) obj);
var hashCode = this.url.GetHashCode();
hashCode = (hashCode * 397) ^ this.scale.GetHashCode();
return hashCode;
return ((this.file != null ? this.file.GetHashCode() : 0) * 397) ^ this.scale.GetHashCode();
public static bool operator ==(FileImage left, FileImage right) {
return Equals(left, right);
}
public static bool operator !=(FileImage left, FileImage right) {
return !Equals(left, right);
}
public override string ToString() {
return $"{this.GetType()}(\"{this.file}\", scale: {this.scale})";
}
public class FileImage : ImageProvider<FileImage> {
public FileImage(string path, double scale = 1.0) {
this.path = path;
public class MemoryImage : ImageProvider<MemoryImage>, IEquatable<MemoryImage> {
public MemoryImage(byte[] bytes, double scale = 1.0) {
D.assert(bytes != null);
this.bytes = bytes;
public string path;
public double scale;
public readonly byte[] bytes;
public readonly double scale;
public override FileImage obtainKey(ImageConfiguration configuration) {
return this;
protected override IPromise<MemoryImage> obtainKey(ImageConfiguration configuration) {
return Promise<MemoryImage>.Resolved(this);
}
protected override ImageStreamCompleter load(MemoryImage key) {
return new MultiFrameImageStreamCompleter(
this._loadAsync(key),
scale: key.scale);
public override ImageStreamCompleter load(FileImage key) {
return new OneFrameImageStreamCompleter(_loadAsync(key));
IPromise<Codec> _loadAsync(MemoryImage key) {
D.assert(key == this);
return CodecUtils.getCodec(this.bytes);
}
public bool Equals(MemoryImage other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(this.bytes, other.bytes) && 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((MemoryImage) obj);
}
public override int GetHashCode() {
unchecked {
return ((this.bytes != null ? this.bytes.GetHashCode() : 0) * 397) ^ this.scale.GetHashCode();
}
}
public static bool operator ==(MemoryImage left, MemoryImage right) {
return Equals(left, right);
public static IPromise<ImageInfo> _loadAsync(FileImage key) {
var promise = new Promise<ImageInfo>();
var bytes = File.ReadAllBytes(key.path);
var imageInfo = new ImageInfo(new ui.Image(
bytes
));
promise.Resolve(imageInfo);
return promise;
public static bool operator !=(MemoryImage left, MemoryImage right) {
return !Equals(left, right);
return "FileImage with Path: " + this.path;
return $"{this.GetType()}({Diagnostics.describeIdentity(this.bytes)}), scale: {this.scale}";
}
}
public class ExactAssetImage : AssetBundleImageProvider, IEquatable<ExactAssetImage> {
public ExactAssetImage(
string assetName,
double scale = 1.0,
AssetBundle bundle = null
) {
D.assert(assetName != null);
this.assetName = assetName;
this.scale = scale;
this.bundle = bundle;
}
public readonly string assetName;
public readonly double scale;
public readonly AssetBundle bundle;
protected override IPromise<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
return Promise<AssetBundleImageKey>.Resolved(new AssetBundleImageKey(
bundle: this.bundle ? this.bundle : configuration.bundle,
name: this.assetName,
scale: this.scale
));
public bool Equals(FileImage other) {
return this.path.Equals(other.path) && this.scale.Equals(other.scale);
public bool Equals(ExactAssetImage other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this.assetName, other.assetName) && this.scale.Equals(other.scale) &&
Equals(this.bundle, other.bundle);
if (object.ReferenceEquals(null, obj)) return false;
if (object.ReferenceEquals(this, obj)) return true;
return obj is FileImage && this.Equals((FileImage) obj);
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return this.Equals((ExactAssetImage) obj);
var hashCode = this.path.GetHashCode();
var hashCode = (this.assetName != null ? this.assetName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (this.bundle != null ? this.bundle.GetHashCode() : 0);
}
public class ImageConfiguration {
public ImageConfiguration(Size size = null) {
this.size = size;
public static bool operator ==(ExactAssetImage left, ExactAssetImage right) {
return Equals(left, right);
public static readonly ImageConfiguration empty = new ImageConfiguration();
public static bool operator !=(ExactAssetImage left, ExactAssetImage right) {
return !Equals(left, right);
}
public ImageConfiguration copyWith(Size size = null) {
return new ImageConfiguration(
size: size ?? this.size
);
public override string ToString() {
return $"{this.GetType()}(name: \"{this.assetName}\", scale: {this.scale}, bundle: {this.bundle})";
public readonly Size size;
}
}

385
Runtime/painting/image_stream.cs


using UnityEngine;
using System;
using System.Collections.Generic;
using System.IO;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
public delegate void ImageListener(ImageInfo image, bool synchronousCall);
public delegate void ImageErrorListerner(System.Object exception, string stackTrack);
public class ImageInfo : IEquatable<ImageInfo> {
public ImageInfo(Image image, double scale = 1.0) {
D.assert(image != null);
public class ImageInfo {
public ImageInfo(Image image, double scale = 1.0) {
public Image image;
public double scale;
}
public readonly Image image;
public readonly double scale;
public class ImageStream {
public ImageStream() {
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);
private ImageStreamCompleter _completer;
private List<_ImageListenerPair> _listeners;
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 System.Object key {
get {
return _completer != null ? (object) _completer : this;
public override int GetHashCode() {
unchecked {
return ((this.image != null ? this.image.GetHashCode() : 0) * 397) ^ this.scale.GetHashCode();
public ImageStreamCompleter completer {
get { return _completer; }
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);
internal class _ImageListenerPair {
public ImageListener listener;
public ImageErrorListener errorListener;
}
public class ImageStream : Diagnosticable {
public ImageStream() {
ImageStreamCompleter _completer;
public ImageStreamCompleter completer => this._completer;
List<_ImageListenerPair> _listeners;
_completer = value;
if (_listeners != null) {
List<_ImageListenerPair> initialListeners = _listeners;
_listeners = null;
D.assert(this._completer == null);
this._completer = value;
if (this._listeners != null) {
var initialListeners = this._listeners;
this._listeners = null;
_completer.addListener(
this._completer.addListener(
listenerPair.listener,
listenerPair.errorListener
);

public void addListener(ImageListener listener, ImageErrorListerner onError = null) {
if (_completer != null) {
_completer.addListener(listener, onError);
} else {
if (_listeners == null) {
_listeners = new List<_ImageListenerPair>();
}
_listeners.Add(new _ImageListenerPair(listener, onError));
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});
if (_completer != null) {
_completer.removeListener(listener);
} else {
_listeners.RemoveAll(listenerPair => listenerPair.listener == 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 => 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 {
public List<_ImageListenerPair> _listeners = new List<_ImageListenerPair>();
public ImageInfo _currentImgae;
public abstract class ImageStreamCompleter : Diagnosticable {
internal readonly List<_ImageListenerPair> _listeners = new List<_ImageListenerPair>();
public ImageInfo currentImage;
public UIWidgetsErrorDetails currentError;
public void addListener(ImageListener listener, ImageErrorListerner onError) {
this._listeners.Add(new _ImageListenerPair(listener, onError));
if (_currentImgae != null) {
public virtual void addListener(ImageListener listener, ImageErrorListener onError = null) {
this._listeners.Add(new _ImageListenerPair {listener = listener, errorListener = onError});
if (this.currentImage != null) {
listener(_currentImgae, true);
this.removeListener(listener);
listener(this.currentImage, true);
catch (Exception e) {
Console.WriteLine("{0} Exception caught.", e);
catch (Exception ex) {
this.reportError(
context: "by a synchronously-called image listener",
exception: ex
);
// todo call onError
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 void removeListener(ImageListener listener) {
var pairToRemove = this._listeners.Single(lp => lp.listener == listener);
this._listeners.Remove(pairToRemove);
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;
}
}
public void setImage(ImageInfo image) {
_currentImgae = image;
if (_listeners.Count == 0) {
protected void setImage(ImageInfo image) {
this.currentImage = image;
if (this._listeners.isEmpty()) {
foreach (var lp in _listeners.ToList()) {
// todo refine
var listener = lp.listener;
var localListeners = this._listeners.Select(l => l.listener).ToList();
foreach (var listener in localListeners) {
this.removeListener(listener);
catch (Exception e) {
Console.WriteLine("{0} Exception caught.", e);
catch (Exception ex) {
this.reportError(
context: "by an image listener",
exception: ex
);
}
}
// todo call onError
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 OneFrameImageStreamCompleter(IPromise<ImageInfo> image) {
image.Then(result => { setImage(result); }).Catch(err => { Debug.Log(err); });
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 _ImageListenerPair {
public _ImageListenerPair(ImageListener listener, ImageErrorListerner errorListener) {
this.listener = listener;
this.errorListener = errorListener;
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
);
});
public ImageListener listener;
public ImageErrorListerner errorListener;
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 => 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;
}
}
}
}

84
Runtime/promise/Promise.cs


/// Tracks the current state of the promise.
/// </summary>
public PromiseState CurState { get; private set; }
public bool IsSync { get; }
public Promise()
{
public Promise(bool isSync = false) {
this.IsSync = isSync;
this.CurState = PromiseState.Pending;
this.id = Promise.NextId();

}
}
public Promise(Action<Action<PromisedT>, Action<Exception>> resolver)
{
public Promise(Action<Action<PromisedT>, Action<Exception>> resolver, bool isSync = false) {
this.IsSync = isSync;
this.CurState = PromiseState.Pending;
this.id = Promise.NextId();

if (rejectHandlers != null)
{
rejectHandlers.Each(handler => InvokeHandler(handler.callback, handler.rejectable, ex));
} else {
Promise.PropagateUnhandledException(this, ex);
}
ClearHandlers();

/// <summary>
/// Reject the promise with an exception.
/// </summary>
public void Reject(Exception ex)
{
public void Reject(Exception ex) {
if (IsSync) {
RejectSync(ex);
} else {
Window.instance.run(() => RejectSync(ex));
}
}
public void RejectSync(Exception ex) {
if (CurState != PromiseState.Pending)
{
if (CurState != PromiseState.Pending) {
"Attempt to reject a promise that is already in state: " + CurState
+ ", a promise can only be rejected when it is still in state: "
"Attempt to reject a promise that is already in state: "
+ CurState
+ ", a promise can only be rejected when it is still in state: "
+ PromiseState.Pending
);
}

if (Promise.EnablePromiseTracking)
{
if (Promise.EnablePromiseTracking) {
Window.instance.scheduleMicrotask(() => {
InvokeRejectHandlers(ex);
});
InvokeRejectHandlers(ex);
public void Resolve(PromisedT value)
{
if (CurState != PromiseState.Pending)
{
public void Resolve(PromisedT value) {
if (IsSync) {
ResolveSync(value);
} else {
Window.instance.run(() => ResolveSync(value));
}
}
public void ResolveSync(PromisedT value) {
if (CurState != PromiseState.Pending) {
"Attempt to resolve a promise that is already in state: " + CurState
+ ", a promise can only be resolved when it is still in state: "
"Attempt to resolve a promise that is already in state: "
+ CurState
+ ", a promise can only be resolved when it is still in state: "
+ PromiseState.Pending
);
}

if (Promise.EnablePromiseTracking)
{
if (Promise.EnablePromiseTracking) {
Window.instance.scheduleMicrotask(() => {
InvokeResolveHandlers(value);
});
InvokeResolveHandlers(value);
}
/// <summary>

/// </summary>
public IPromise Catch(Action<Exception> onRejected)
{
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = _ => resultPromise.Resolve();

/// </summary>
public IPromise Then(Func<PromisedT, IPromise> onResolved, Action<Exception> onRejected, Action<float> onProgress)
{
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>

/// </summary>
public IPromise Then(Action<PromisedT> onResolved, Action<Exception> onRejected, Action<float> onProgress)
{
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>

private void ActionHandlers(IRejectable resultPromise, Action<PromisedT> resolveHandler, Action<Exception> rejectHandler)
{
if (CurState == PromiseState.Resolved) {
Window.instance.scheduleMicrotask(() => {
InvokeHandler(resolveHandler, resultPromise, resolveValue);
});
InvokeHandler(resolveHandler, resultPromise, resolveValue);
Window.instance.scheduleMicrotask(() => {
InvokeHandler(rejectHandler, resultPromise, rejectionException);
});
InvokeHandler(rejectHandler, resultPromise, rejectionException);
}
else {
AddResolveHandler(resolveHandler, resultPromise);

/// </summary>
public static IPromise<PromisedT> Resolved(PromisedT promisedValue)
{
var promise = new Promise<PromisedT>();
var promise = new Promise<PromisedT>(isSync: true);
promise.Resolve(promisedValue);
return promise;
}

public IPromise ContinueWith(Func<IPromise> onComplete)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.WithName(Name);
this.Then(x => promise.Resolve());

public IPromise<ConvertedT> ContinueWith<ConvertedT>(Func<IPromise<ConvertedT>> onComplete)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.WithName(Name);
this.Then(x => promise.Resolve());

95
Runtime/promise/Promise_NonGeneric.cs


using System.Linq;
using RSG.Exceptions;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace RSG
{

/// Tracks the current state of the promise.
/// </summary>
public PromiseState CurState { get; private set; }
public bool IsSync { get; }
public Promise()
{
public Promise(bool isSync = false) {
this.IsSync = isSync;
this.CurState = PromiseState.Pending;
this.id = NextId();
if (EnablePromiseTracking)

}
public Promise(Action<Action, Action<Exception>> resolver)
{
public Promise(Action<Action, Action<Exception>> resolver, bool isSync = false) {
this.IsSync = isSync;
this.CurState = PromiseState.Pending;
this.id = NextId();
if (EnablePromiseTracking)

if (rejectHandlers != null)
{
rejectHandlers.Each(handler => InvokeRejectHandler(handler.callback, handler.rejectable, ex));
} else {
PropagateUnhandledException(this, ex);
}
ClearHandlers();

/// <summary>
/// Reject the promise with an exception.
/// </summary>
public void Reject(Exception ex)
{
public void Reject(Exception ex) {
if (IsSync) {
RejectSync(ex);
} else {
Window.instance.run(() => RejectSync(ex));
}
}
public void RejectSync(Exception ex) {
if (CurState != PromiseState.Pending)
{
if (CurState != PromiseState.Pending) {
"Attempt to reject a promise that is already in state: " + CurState
+ ", a promise can only be rejected when it is still in state: "
"Attempt to reject a promise that is already in state: "
+ CurState
+ ", a promise can only be rejected when it is still in state: "
+ PromiseState.Pending
);
}

if (EnablePromiseTracking)
{
if (EnablePromiseTracking) {
Window.instance.scheduleMicrotask(() => {
InvokeRejectHandlers(ex);
});
InvokeRejectHandlers(ex);
}

public void Resolve()
{
if (CurState != PromiseState.Pending)
{
public void Resolve() {
if (IsSync) {
ResolveSync();
} else {
Window.instance.run(() => ResolveSync());
}
}
public void ResolveSync() {
if (CurState != PromiseState.Pending) {
"Attempt to resolve a promise that is already in state: " + CurState
+ ", a promise can only be resolved when it is still in state: "
"Attempt to resolve a promise that is already in state: "
+ CurState
+ ", a promise can only be resolved when it is still in state: "
+ PromiseState.Pending
);
}

if (EnablePromiseTracking)
{
if (EnablePromiseTracking) {
Window.instance.scheduleMicrotask(() => {
InvokeResolveHandlers();
});
InvokeResolveHandlers();
}

{
// Argument.NotNull(() => onRejected);
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action resolveHandler = () => resultPromise.Resolve();

/// </summary>
public IPromise Then(Func<IPromise> onResolved, Action<Exception> onRejected, Action<float> onProgress)
{
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action resolveHandler = () =>

/// </summary>
public IPromise Then(Action onResolved, Action<Exception> onRejected, Action<float> onProgress)
{
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName(Name);
Action resolveHandler = () =>

private void ActionHandlers(IRejectable resultPromise, Action resolveHandler, Action<Exception> rejectHandler)
{
if (CurState == PromiseState.Resolved) {
Window.instance.scheduleMicrotask(() => {
InvokeResolveHandler(resolveHandler, resultPromise);
});
InvokeResolveHandler(resolveHandler, resultPromise);
Window.instance.scheduleMicrotask(() => {
InvokeRejectHandler(rejectHandler, resultPromise, rejectionException);
});
InvokeRejectHandler(rejectHandler, resultPromise, rejectionException);
}
else
{

}
var remainingCount = promisesArray.Length;
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName("All");
var progress = new float[remainingCount];

/// </summary>
public static IPromise Sequence(IEnumerable<Func<IPromise>> fns)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
int count = 0;

throw new InvalidOperationException("At least 1 input promise must be provided for Race");
}
var resultPromise = new Promise();
var resultPromise = new Promise(isSync: true);
resultPromise.WithName("Race");
var progress = new float[promisesArray.Length];

/// </summary>
public static IPromise Resolved()
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.Resolve();
return promise;
}

public IPromise Finally(Action onComplete)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.WithName(Name);
this.Then(() => promise.Resolve());

public IPromise ContinueWith(Func<IPromise> onComplete)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.WithName(Name);
this.Then(() => promise.Resolve());

public IPromise<ConvertedT> ContinueWith<ConvertedT>(Func<IPromise<ConvertedT>> onComplete)
{
var promise = new Promise();
var promise = new Promise(isSync: true);
promise.WithName(Name);
this.Then(() => promise.Resolve());

if (unhandlerException != null)
{
unhandlerException(sender, new ExceptionEventArgs(ex));
} else {
Debug.LogWarning("Unhandled Exception from " + sender + ": " + ex);
}
}
}

8
Runtime/rendering/binding.cs


using System;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.painting;
public class RendererBinding : GestureBinding {
public class RendererBinding : PaintingBinding {
get { return (RendererBinding) GestureBinding.instance; }
set { GestureBinding.instance = value; }
get { return (RendererBinding) PaintingBinding.instance; }
set { PaintingBinding.instance = value; }
}
public RendererBinding() {

6
Runtime/rendering/box.mixin.njk


using System;
using System.Collections.Generic;
using UIWidgets.gestures;
using UIWidgets.ui;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.ui;
namespace UIWidgets.rendering {
namespace Unity.UIWidgets.rendering {
{% macro RenderBoxContainerDefaultsMixin(with) %}
public abstract class
RenderBoxContainerDefaultsMixin{{with}}<ChildType, ParentDataType>

289
Runtime/rendering/image.cs


using Unity.UIWidgets.foundation;
using Image = Unity.UIWidgets.ui.Image;
public RenderImage(ui.Image image,
Color color,
ui.BlendMode colorBlendMode,
BoxFit fit,
ImageRepeat repeat,
Rect centerSlice,
double? width,
double? height,
public RenderImage(
Image image = null,
double? width = null,
double? height = null,
double scale = 1.0,
Color color = null,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
double scale = 1.0
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect centerSlice = null,
bool invertColors = false,
FilterMode filterMode = FilterMode.Point
) {
this._image = image;
this._width = width;

this._repeat = repeat;
this._centerSlice = centerSlice;
this._alignment = alignment ?? Alignment.center;
this._invertColors = invertColors;
this._filterMode = filterMode;
Alignment _resolvedAlignment;
Image _image;
void _resolve() {
if (_resolvedAlignment != null)
return;
_resolvedAlignment = alignment;
}
public Image image {
get => this._image;
set {
if (value == this._image) {
return;
}
void _markNeedsResolution() {
_resolvedAlignment = null;
markNeedsPaint();
this._image = value;
this.markNeedsPaint();
if (this._width == null || this._height == null) {
this.markNeedsLayout();
}
}
private ui.Image _image;
double? _width;
public ui.Image image {
get { return this._image; }
public double? width {
get => this._width;
if (value == _image)
if (value == this._width) {
_image = value;
markNeedsPaint();
if (_width == 0.0 || _height == 0.0)
markNeedsLayout();
}
this._width = value;
this.markNeedsLayout();
private double? _width;
double? _height;
public double? width {
get { return _width; }
public double? height {
get => this._height;
if (value == _width)
if (value == this._height) {
_width = value;
markNeedsLayout();
}
this._height = value;
this.markNeedsLayout();
private double? _height;
double _scale;
public double? height {
get { return _height; }
public double scale {
get => this._scale;
if (value == _height)
if (value == this._scale) {
_height = value;
markNeedsLayout();
}
this._scale = value;
this.markNeedsLayout();
private double _scale;
Color _color;
public double scale {
get { return _scale; }
public Color color {
get => this._color;
if (value == _scale)
if (value == this._color) {
_scale = value;
markNeedsLayout();
}
this._color = value;
this.markNeedsPaint();
private Color _color;
BlendMode _colorBlendMode;
public Color color {
get { return _color; }
public BlendMode colorBlendMode {
get => this._colorBlendMode;
if (value == _color)
if (value == this._colorBlendMode) {
_color = value;
markNeedsPaint();
}
this._colorBlendMode = value;
this.markNeedsPaint();
private ui.BlendMode _colorBlendMode;
FilterMode _filterMode;
public ui.BlendMode colorBlendMode {
get { return _colorBlendMode; }
public FilterMode filterMode {
get => this._filterMode;
if (value == _colorBlendMode)
if (value == this._filterMode) {
_colorBlendMode = value;
markNeedsPaint();
}
this._filterMode = value;
this.markNeedsPaint();
private BoxFit _fit;
BoxFit? _fit;
public BoxFit fit {
get { return _fit; }
public BoxFit? fit {
get => this._fit;
if (value == _fit)
if (value == this._fit) {
_fit = value;
markNeedsPaint();
}
this._fit = value;
this.markNeedsPaint();
private Alignment _alignment;
Alignment _alignment;
get { return _alignment; }
get => this._alignment;
if (value == _alignment)
if (value == this._alignment) {
_alignment = value;
_markNeedsResolution();
}
this._alignment = value;
this.markNeedsPaint();
private ImageRepeat _repeat;
ImageRepeat _repeat;
get { return _repeat; }
get => this._repeat;
if (value == _repeat)
if (value == this._repeat) {
_repeat = value;
markNeedsPaint();
}
this._repeat = value;
this.markNeedsPaint();
private Rect _centerSlice;
Rect _centerSlice;
get { return _centerSlice; }
get => this._centerSlice;
if (value == _centerSlice)
if (value == this._centerSlice) {
_centerSlice = value;
markNeedsPaint();
}
this._centerSlice = value;
this.markNeedsPaint();
}
}
bool _invertColors;
public bool invertColors {
get => this._invertColors;
set {
if (value == this._invertColors) {
return;
}
this._invertColors = value;
this.markNeedsPaint();
// Folds the given |width| and |height| into |constraints| so they can all
// be treated uniformly.
_width,
_height
this._width,
this._height
if (_image == null)
if (this._image == null) {
}
_image.width / _scale,
_image.height / _scale
this._image.width / this._scale,
this._image.height / this._scale
protected override double computeMinIntrinsicWidth(double height) {
D.assert(height >= 0.0);
if (this._width == null && this._height == null) {
return 0.0;
}
return this._sizeForConstraints(BoxConstraints.tightForFinite(height: height)).width;
}
protected override double computeMaxIntrinsicWidth(double height) {
D.assert(height >= 0.0);
return this._sizeForConstraints(BoxConstraints.tightForFinite(height: height)).width;
}
protected override double computeMinIntrinsicHeight(double width) {
D.assert(width >= 0.0);
if (this._width == null && this._height == null) {
return 0.0;
}
return this._sizeForConstraints(BoxConstraints.tightForFinite(width: width)).height;
}
protected override double computeMaxIntrinsicHeight(double width) {
D.assert(width >= 0.0);
return this._sizeForConstraints(BoxConstraints.tightForFinite(width: width)).height;
}
protected override bool hitTestSelf(Offset position) => true;
this.size = _sizeForConstraints(constraints);
this.size = this._sizeForConstraints(this.constraints);
if (_image == null)
if (this._image == null) {
_resolve();
DecorationImageUtil.paintImage(
context.canvas,
offset & size,
_image,
_fit,
_centerSlice,
_resolvedAlignment,
_repeat
}
ImageUtils.paintImage(
canvas: context.canvas,
rect: offset & this.size,
image: this._image,
scale: this._scale,
// colorFilter: this._colorFilter,
fit: this._fit,
alignment: this._alignment,
centerSlice: this._centerSlice,
repeat: this._repeat,
invertColors: this._invertColors,
filterMode: this._filterMode
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Image>("image", this.image));
properties.add(new DoubleProperty("width", this.width, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DoubleProperty("height", this.height, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DoubleProperty("scale", this.scale, defaultValue: 1.0));
properties.add(new DiagnosticsProperty<Color>("color", this.color,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BlendMode>("colorBlendMode", this.colorBlendMode,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BoxFit?>("fit", this.fit, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<Alignment>("alignment", this.alignment,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<ImageRepeat>("repeat", this.repeat, defaultValue: ImageRepeat.noRepeat));
properties.add(new DiagnosticsProperty<Rect>("centerSlice", this.centerSlice,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<bool>("invertColors", this.invertColors));
properties.add(new EnumProperty<FilterMode>("filterMode", this.filterMode));
}
}
}

4
Runtime/rendering/object.mixin.njk


// AUTO-GENERATED, DO NOT EDIT BY HAND
using System.Collections.Generic;
using UIWidgets.foundation;
using Unity.UIWidgets.foundation;
namespace UIWidgets.rendering {
namespace Unity.UIWidgets.rendering {
{% macro RenderObjectWithChildMixin(with) %}
public abstract class RenderObjectWithChildMixin{{with}}<ChildType> : {{with}}, RenderObjectWithChildMixin<ChildType>, RenderObjectWithChildMixin where ChildType : RenderObject {
public bool debugValidateChild(RenderObject child) {

6
Runtime/rendering/proxy_box.mixin.njk


using UIWidgets.ui;
using UIWidgets.gestures;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.gestures;
namespace UIWidgets.rendering {
namespace Unity.UIWidgets.rendering {
{% macro RenderProxyBoxMixin(with) %}
public abstract class RenderProxyBoxMixin{{with}}<T> : {{with}}<T> where T : RenderBox {

17
Runtime/ui/geometry.cs


using System;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public static class MathUtils {

public static int floor(this double value) {
return (int) Math.Floor(value);
}
public static int ceil(this double value) {
return (int) Math.Ceiling(value);
}
public static int round(this float value) {
return Mathf.RoundToInt(value);
}
public static int floor(this float value) {
return Mathf.FloorToInt(value);
}
public static int ceil(this float value) {
return Mathf.CeilToInt(value);
}
}

21
Runtime/ui/painting/canvas.cs


using System;
using Unity.UIWidgets.ui.txt;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public interface Canvas {

void drawPath(Path path, Paint paint);
void drawImage(Texture image, Offset offset, Paint paint);
void drawImage(Image image, Offset offset, Paint paint);
void drawImageRect(Texture image, Rect dst, Paint paint);
void drawImageRect(Image image, Rect dst, Paint paint);
void drawImageRect(Texture image, Rect src, Rect dst, Paint paint);
void drawImageRect(Image image, Rect src, Rect dst, Paint paint);
void drawImageNine(Texture image, Rect center, Rect dst, Paint paint);
void drawImageNine(Image image, Rect center, Rect dst, Paint paint);
void drawImageNine(Texture image, Rect src, Rect center, Rect dst, Paint paint);
void drawImageNine(Image image, Rect src, Rect center, Rect dst, Paint paint);
void drawPicture(Picture picture);

});
}
public void drawImage(Texture image, Offset offset, Paint paint) {
public void drawImage(Image image, Offset offset, Paint paint) {
this._recorder.addDrawCmd(new DrawImage {
image = image,
offset = offset,

public void drawImageRect(Texture image, Rect dst, Paint paint) {
public void drawImageRect(Image image, Rect dst, Paint paint) {
this._recorder.addDrawCmd(new DrawImageRect {
image = image,
dst = dst,

public void drawImageRect(Texture image, Rect src, Rect dst, Paint paint) {
public void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
this._recorder.addDrawCmd(new DrawImageRect {
image = image,
src = src,

}
public void drawImageNine(Texture image, Rect center, Rect dst, Paint paint) {
public void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
this._recorder.addDrawCmd(new DrawImageNine {
image = image,
center = center,

}
public void drawImageNine(Texture image, Rect src, Rect center, Rect dst, Paint paint) {
public void drawImageNine(Image image, Rect src, Rect center, Rect dst, Paint paint) {
this._recorder.addDrawCmd(new DrawImageNine {
image = image,
src = src,

25
Runtime/ui/painting/canvas_impl.cs


}
}
public void drawImage(Texture image, Offset offset, Paint paint) {
D.assert(image);
public void drawImage(Image image, Offset offset, Paint paint) {
D.assert(image != null);
D.assert(offset != null);
D.assert(paint != null);

return properties;
}
public void drawImageRect(Texture image, Rect dst, Paint paint) {
public void drawImageRect(Image image, Rect dst, Paint paint) {
public void drawImageRect(Texture image, Rect src, Rect dst, Paint paint) {
D.assert(image);
public void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
D.assert(image != null);
D.assert(dst != null);
D.assert(paint != null);

}
var mat = this._getMat(paint);
var properties = this._getMatPropsForImage(image, paint);
var properties = this._getMatPropsForImage(image.texture, paint);
image = image, // to keep a reference to avoid GC.
public void drawImageNine(Texture image, Rect center, Rect dst, Paint paint) {
public void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
public void drawImageNine(Texture image, Rect src, Rect center, Rect dst, Paint paint) {
D.assert(image);
public void drawImageNine(Image image, Rect src, Rect center, Rect dst, Paint paint) {
D.assert(image != null);
D.assert(center != null);
D.assert(dst != null);
D.assert(paint != null);

}
var mat = this._getMat(paint);
var properties = this._getMatPropsForImage(image, paint);
var properties = this._getMatPropsForImage(image.texture, paint);
image = image, // to keep a reference to avoid GC.
properties = properties,
});
}

public MaterialPropertyBlock properties;
public RenderLayer layer;
public Material material;
public Image image; // just to keep a reference to avoid GC.
public void onExecute(CommandBuffer cmdBuf) {
if (this.layer != null) {

}
public void onDestroy() {
Object.DestroyImmediate(this.mesh);
this.mesh = ObjectUtils.SafeDestroy(this.mesh);
}
}

7
Runtime/ui/painting/draw_cmd.cs


using Unity.UIWidgets.ui.txt;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public abstract class DrawCmd {

}
public class DrawImage : DrawCmd {
public Texture image;
public Image image;
public Texture image;
public Image image;
public Rect src;
public Rect dst;
public Paint paint;

public Texture image;
public Image image;
public Rect src;
public Rect center;
public Rect dst;

67
Runtime/ui/painting/image.cs


using System;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
public class Image {
public Image(byte[] raw = null, Texture2D texture = null) {
this.rawData = raw ?? new byte[0];
public class Image : IEquatable<Image>, IDisposable {
Texture _texture;
readonly bool _isOwner;
public Image(Texture texture, bool isOwner = true) {
this._isOwner = isOwner;
public byte[] rawData;
public int width => this._texture != null ? this._texture.width : 0;
public int height => this._texture != null ? this._texture.height : 0;
public Texture texture => this._texture;
~Image() {
Timer.runInMain(this._dispose);
}
void _dispose() {
if (this._isOwner) {
this._texture = ObjectUtils.SafeDestroy(this._texture);
}
}
public void Dispose() {
this._dispose();
GC.SuppressFinalize(this);
}
public bool Equals(Image other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(this._texture, other._texture);
}
public int height {
get { return texture != null ? texture.height : 0; }
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((Image) obj);
public int width {
get { return texture != null ? texture.width : 0; }
public override int GetHashCode() {
return (this._texture != null ? this._texture.GetHashCode() : 0);
public Texture2D texture {
get {
if (_texture == null && rawData.Length != 0) {
_texture = new Texture2D(2, 2);
_texture.LoadImage(rawData);
}
public static bool operator ==(Image left, Image right) {
return Equals(left, right);
}
return _texture;
}
public static bool operator !=(Image left, Image right) {
return !Equals(left, right);
private Texture2D _texture;
public override string ToString() {
return $"[{this.width}\u00D7{this.height}]";
}
}
}

4
Runtime/ui/pointer.cs


public class PointerData {
public PointerData(
DateTime timeStamp,
TimeSpan timeStamp,
PointerChange change,
PointerDeviceKind kind,
int device,

this.physicalY = physicalY;
}
public readonly DateTime timeStamp;
public readonly TimeSpan timeStamp;
public PointerChange change;
public PointerDeviceKind kind;
public int device;

62
Runtime/ui/window.cs


D.assert(_instance != null, "Window.instance is null");
return _instance;
}
set {
if (value == null) {
D.assert(_instance != null, "Window.instance is already cleared.");

}
VoidCallback _onDrawFrame;
public PointerDataPacketCallback onPointerEvent {
get { return this._onPointerEvent; }
set { this._onPointerEvent = value; }

public abstract void flushMicrotasks();
public abstract Timer run(TimeSpan duration, Action callback, bool periodic = false);
public abstract IDisposable onUpdate(VoidCallback callback);
}
public class Locale : IEquatable<Locale> {
public Locale(string languageCode, string countryCode = null) {
D.assert(languageCode != null);
this._languageCode = languageCode;
this._countryCode = countryCode;
}
readonly string _languageCode;
public string languageCode => this._languageCode;
readonly string _countryCode;
public string countryCode => this._countryCode;
public bool Equals(Locale other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(this._languageCode, other._languageCode) &&
string.Equals(this._countryCode, other._countryCode);
}
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((Locale) obj);
}
public override int GetHashCode() {
unchecked {
return ((this._languageCode != null ? this._languageCode.GetHashCode() : 0) * 397) ^
(this._countryCode != null ? this._countryCode.GetHashCode() : 0);
}
}
public static bool operator ==(Locale left, Locale right) {
return Equals(left, right);
}
public static bool operator !=(Locale left, Locale right) {
return !Equals(left, right);
}
public override string ToString() {
if (this.countryCode == null) {
return this.languageCode;
}
return $"{this.languageCode}_{this.countryCode}";
}
}
}

135
Runtime/widgets/basic.cs


public class RawImage : LeafRenderObjectWidget {
public RawImage(
Key key,
ui.Image image,
double scale,
Color color,
BlendMode colorBlendMode,
BoxFit fit,
Rect centerSlice,
Key key = null,
ui.Image image = null,
double scale = 1.0,
Color color = null,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
ImageRepeat repeat = ImageRepeat.noRepeat
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect centerSlice = null,
bool invertColors = false,
FilterMode filterMode = FilterMode.Point
) : base(key) {
this.image = image;
this.width = width;

this.blendMode = colorBlendMode;
this.centerSlice = centerSlice;
this.colorBlendMode = colorBlendMode;
this.alignment = alignment == null ? Alignment.center : alignment;
this.alignment = alignment ?? Alignment.center;
this.centerSlice = centerSlice;
this.invertColors = invertColors;
this.filterMode = filterMode;
public readonly ui.Image image;
public readonly double? width;
public readonly double? height;
public readonly double scale;
public readonly Color color;
public readonly FilterMode filterMode;
public readonly BlendMode colorBlendMode;
public readonly BoxFit? fit;
public readonly Alignment alignment;
public readonly ImageRepeat repeat;
public readonly Rect centerSlice;
public readonly bool invertColors;
this.image,
this.color,
this.blendMode,
this.fit,
this.repeat,
this.centerSlice,
this.width,
this.height,
this.alignment
image: this.image,
width: this.width,
height: this.height,
scale: this.scale,
color: this.color,
colorBlendMode: this.colorBlendMode,
fit: this.fit,
alignment: this.alignment,
repeat: this.repeat,
centerSlice: this.centerSlice,
invertColors: this.invertColors,
filterMode: this.filterMode
((RenderImage) renderObject).image = this.image;
((RenderImage) renderObject).width = this.width;
((RenderImage) renderObject).height = this.height;
((RenderImage) renderObject).color = this.color;
((RenderImage) renderObject).fit = this.fit;
((RenderImage) renderObject).repeat = this.repeat;
((RenderImage) renderObject).centerSlice = this.centerSlice;
((RenderImage) renderObject).alignment = this.alignment;
var renderImage = (RenderImage) renderObject;
renderImage.image = this.image;
renderImage.width = this.width;
renderImage.height = this.height;
renderImage.scale = this.scale;
renderImage.color = this.color;
renderImage.colorBlendMode = this.colorBlendMode;
renderImage.alignment = this.alignment;
renderImage.fit = this.fit;
renderImage.repeat = this.repeat;
renderImage.centerSlice = this.centerSlice;
renderImage.invertColors = this.invertColors;
renderImage.filterMode = this.filterMode;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<ui.Image>("image", this.image));
properties.add(new DoubleProperty("width", this.width, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DoubleProperty("height", this.height, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DoubleProperty("scale", this.scale, defaultValue: 1.0));
properties.add(new DiagnosticsProperty<Color>("color", this.color,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BlendMode>("colorBlendMode", this.colorBlendMode,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BoxFit?>("fit", this.fit, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<Alignment>("alignment", this.alignment,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<ImageRepeat>("repeat", this.repeat, defaultValue: ImageRepeat.noRepeat));
properties.add(new DiagnosticsProperty<Rect>("centerSlice", this.centerSlice,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<bool>("invertColors", this.invertColors));
properties.add(new EnumProperty<FilterMode>("filterMode", this.filterMode));
}
}
public class DefaultAssetBundle : InheritedWidget {
public DefaultAssetBundle(
Key key = null,
AssetBundle bundle = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(bundle != null);
D.assert(child != null);
this.bundle = bundle;
}
public readonly AssetBundle bundle;
public static AssetBundle of(BuildContext context) {
DefaultAssetBundle result =
(DefaultAssetBundle) context.inheritFromWidgetOfExactType(typeof(DefaultAssetBundle));
return result?.bundle;
public readonly ui.Image image;
public readonly double? width;
public readonly double? height;
public readonly double scale;
public readonly Color color;
public readonly BlendMode blendMode;
public readonly BoxFit fit;
public readonly Alignment alignment;
public readonly ImageRepeat repeat;
public readonly Rect centerSlice;
public override bool updateShouldNotify(InheritedWidget oldWidget) {
return this.bundle != ((DefaultAssetBundle) oldWidget).bundle;
}
}
public class Listener : SingleChildRenderObjectWidget {

4
Runtime/widgets/container.cs


return new RenderDecoratedBox(
decoration: this.decoration,
position: this.position,
configuration: ImageUtil.createLocalImageConfiguration(context)
configuration: ImageUtils.createLocalImageConfiguration(context)
);
}

renderObject.configuration = ImageUtil.createLocalImageConfiguration(context);
renderObject.configuration = ImageUtils.createLocalImageConfiguration(context);
renderObject.position = this.position;
}

296
Runtime/widgets/image.cs


using System;
using System.Collections.Generic;
using RSG;
using Rect = Unity.UIWidgets.ui.Rect;
internal class ImageUtil {
public class ImageUtils {
size: size
bundle: DefaultAssetBundle.of(context),
//TODO: add MediaQuery & Localizations.
//devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0,
//locale: Localizations.localeOf(context, nullOk: true),
size: size,
platform: Application.platform
}
public class Image : StatefulWidget {
public ImageProvider image;
public double? width;
public double? height;
public Color color;
public BoxFit fit;
public Alignment alignment;
public BlendMode colorBlendMode;
public ImageRepeat repeat;
public ui.Rect centerSlice;
public IPromise precacheImage(
ImageProvider provider,
BuildContext context,
Size size = null,
ImageErrorListener onError = null
) {
ImageConfiguration config = createLocalImageConfiguration(context, size: size);
var completer = new Promise();
ImageStream stream = provider.resolve(config);
public bool gaplessPlayback;
void listener(ImageInfo image, bool sync) {
completer.Resolve();
}
void errorListener(Exception exception) {
completer.Resolve();
if (onError != null) {
onError(exception);
} else {
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
context: "image failed to precache",
library: "image resource service",
exception: exception,
silent: true
));
}
}
stream.addListener(listener, onError: errorListener);
completer.Then(() => { stream.removeListener(listener); });
return completer;
}
}
public class Image : StatefulWidget {
Key key,
ImageProvider image,
Color color,
BlendMode colorBlendMode,
BoxFit fit,
ui.Rect centerSlice,
Alignment alignment,
Key key = null,
ImageProvider image = null,
Color color = null,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
Alignment alignment = null,
bool gaplessPlayback = false
Rect centerSlice = null,
bool gaplessPlayback = false,
FilterMode filterMode = FilterMode.Point
D.assert(image != null);
this.image = image;
this.width = width;
this.height = height;

this.alignment = alignment == null ? Alignment.center : alignment;
this.alignment = alignment ?? Alignment.center;
this.filterMode = filterMode;
double scale = 1.0,
BlendMode colorBlendMode = BlendMode.srcOver,
BoxFit fit = BoxFit.none,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
ui.Rect centerSlice = null,
Dictionary<String, String> headers = null,
Rect centerSlice = null,
double scale = 1.0
FilterMode filterMode = FilterMode.Point,
IDictionary<String, String> headers = null
var networkImage = new NetworkImage(src, headers, scale);
var networkImage = new NetworkImage(src, scale, headers);
width,
height,
centerSlice,
width,
height,
gaplessPlayback
centerSlice,
gaplessPlayback,
filterMode
string path,
string file,
double scale = 1.0,
BlendMode colorBlendMode = BlendMode.srcOver,
BoxFit fit = BoxFit.none,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
ui.Rect centerSlice = null,
Rect centerSlice = null,
double scale = 1.0
FilterMode filterMode = FilterMode.Point
var fileImage = new FileImage(path, scale);
var fileImage = new FileImage(file, scale);
width,
height,
alignment,
repeat,
alignment,
gaplessPlayback,
filterMode
);
}
public static Image memory(
byte[] bytes,
Key key = null,
double scale = 1.0,
double? width = null,
double? height = null,
Color color = null,
BlendMode colorBlendMode = BlendMode.srcIn,
BoxFit? fit = null,
Alignment alignment = null,
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect centerSlice = null,
bool gaplessPlayback = false,
FilterMode filterMode = FilterMode.Point
) {
var memoryImage = new MemoryImage(bytes, scale);
return new Image(
key,
memoryImage,
color,
colorBlendMode,
fit,
alignment,
gaplessPlayback
centerSlice,
gaplessPlayback,
filterMode
public readonly ImageProvider image;
public readonly double? width;
public readonly double? height;
public readonly Color color;
public readonly FilterMode filterMode;
public readonly BlendMode colorBlendMode;
public readonly BoxFit? fit;
public readonly Alignment alignment;
public readonly ImageRepeat repeat;
public readonly Rect centerSlice;
public readonly bool gaplessPlayback;
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<ImageProvider>("image", this.image));
properties.add(new DoubleProperty("width", this.width, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DoubleProperty("height", this.height, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<Color>("color", this.color,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BlendMode>("colorBlendMode", this.colorBlendMode,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<BoxFit?>("fit", this.fit, defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new DiagnosticsProperty<Alignment>("alignment", this.alignment,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<ImageRepeat>("repeat", this.repeat, defaultValue: ImageRepeat.noRepeat));
properties.add(new DiagnosticsProperty<Rect>("centerSlice", this.centerSlice,
defaultValue: Diagnostics.kNullDefaultValue));
properties.add(new EnumProperty<FilterMode>("filterMode", this.filterMode, Diagnostics.kNullDefaultValue));
}
public class _ImageState : State {
public class _ImageState : State<Image> {
bool _invertColors;
_resolveImage();
if (TickerMode.of(context))
_listenToStream();
else
_stopListeningToStream();
this._invertColors = false;
this._resolveImage();
if (TickerMode.of(this.context)) {
this._listenToStream();
} else {
this._stopListeningToStream();
}
base.didChangeDependencies();
if (((Image) widget).image != ((Image) oldWidget).image)
_resolveImage();
base.didUpdateWidget(oldWidget);
if (this.widget.image != ((Image) oldWidget).image) {
this._resolveImage();
}
// public override void reassemble() {
// _resolveImage(); // in case the image cache was flushed
// }
var imageWidget = (Image) widget;
imageWidget.image.resolve(ImageUtil.createLocalImageConfiguration(
context,
size: imageWidget.width != null && imageWidget.height != null
? new Size(imageWidget.width.Value, imageWidget.height.Value)
this.widget.image.resolve(ImageUtils.createLocalImageConfiguration(
this.context,
size: this.widget.width != null && this.widget.height != null
? new Size(this.widget.width.Value, this.widget.height.Value)
_updateSourceStream(newStream);
D.assert(newStream != null);
this._updateSourceStream(newStream);
setState(() => { _imageInfo = imageInfo; });
this.setState(() => { this._imageInfo = imageInfo; });
if ((_imageStream == null ? null : _imageStream.key) == (newStream == null ? null : newStream.key))
if (this._imageStream?.key == newStream?.key) {
}
if (_isListeningToStream && _imageStream != null)
_imageStream.removeListener(_handleImageChanged);
if (this._isListeningToStream) {
this._imageStream.removeListener(this._handleImageChanged);
}
if (!((Image) widget).gaplessPlayback) {
setState(() => { _imageInfo = null; });
if (!this.widget.gaplessPlayback) {
this.setState(() => { this._imageInfo = null; });
}
_imageStream = newStream;
if (_isListeningToStream && _imageStream != null)
_imageStream.addListener(_handleImageChanged);
this._imageStream = newStream;
if (this._isListeningToStream) {
this._imageStream.addListener(this._handleImageChanged);
if (_isListeningToStream)
if (this._isListeningToStream) {
_imageStream.addListener(_handleImageChanged);
_isListeningToStream = true;
}
this._imageStream.addListener(this._handleImageChanged);
this._isListeningToStream = true;
if (!_isListeningToStream)
if (!this._isListeningToStream) {
_imageStream.removeListener(_handleImageChanged);
_isListeningToStream = false;
}
this._imageStream.removeListener(this._handleImageChanged);
this._isListeningToStream = false;
public override void dispose() {
D.assert(this._imageStream != null);
this._stopListeningToStream();
base.dispose();
}
var imageWidget = (Image) widget;
null,
_imageInfo == null ? null : _imageInfo.image,
_imageInfo == null ? 1.0 : _imageInfo.scale,
imageWidget.color,
imageWidget.colorBlendMode,
imageWidget.fit,
imageWidget.centerSlice,
imageWidget.width,
imageWidget.height,
imageWidget.alignment,
imageWidget.repeat
image: this._imageInfo?.image,
width: this.widget.width,
height: this.widget.height,
scale: this._imageInfo?.scale ?? 1.0,
color: this.widget.color,
colorBlendMode: this.widget.colorBlendMode,
fit: this.widget.fit,
alignment: this.widget.alignment,
repeat: this.widget.repeat,
centerSlice: this.widget.centerSlice,
invertColors: this._invertColors,
filterMode: this.widget.filterMode
}
public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
base.debugFillProperties(description);
description.add(new DiagnosticsProperty<ImageStream>("stream", this._imageStream));
description.add(new DiagnosticsProperty<ImageInfo>("pixels", this._imageInfo));
}
}
}

6
Runtime/widgets/scroll_activity.cs


public readonly double? motionStartDistanceThreshold;
DateTime _lastNonStationaryTimestamp;
TimeSpan _lastNonStationaryTimestamp;
bool _retainMomentum;

this._del = value;
}
void _maybeLoseMomentum(double offset, DateTime? timestamp) {
void _maybeLoseMomentum(double offset, TimeSpan? timestamp) {
if (this._retainMomentum &&
offset == 0.0 &&
(timestamp == null ||

}
double _adjustForScrollStartThreshold(double offset, DateTime? timestamp) {
double _adjustForScrollStartThreshold(double offset, TimeSpan? timestamp) {
if (timestamp == null) {
return offset;
}

4
Runtime/widgets/scroll_notification.mixin.njk


using System.Collections.Generic;
using UIWidgets.rendering;
using Unity.UIWidgets.rendering;
namespace UIWidgets.widgets {
namespace Unity.UIWidgets.widgets {
{% macro ViewportNotificationMixin(with) %}
public abstract class ViewportNotificationMixin{{with}} : {{with}} {
public int depth {

38
Tests/Editor/CanvasAndLayers.cs


private int _selected;
private PaintingBinding _paintingBinding;
private ImageStream _stream;
private RenderTexture _renderTexture;

void OnGUI() {
this._selected = EditorGUILayout.Popup("test case", this._selected, this._optionStrings);
if (this._selected == 3) {
using (this._windowAdapter.getScope()) {
if (GUI.Button(new UnityEngine.Rect(20, 50, 100, 20), "Image 1")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/h%3D300/sign=10b374237f0e0cf3bff748fb3a47f23d/adaf2edda3cc7cd90df1ede83401213fb80e9127.jpg");
}
if (_selected == 3) {
if (GUI.Button(new UnityEngine.Rect(20, 50, 100, 20), "Image 1")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/h%3D300/sign=10b374237f0e0cf3bff748fb3a47f23d/adaf2edda3cc7cd90df1ede83401213fb80e9127.jpg");
}
if (GUI.Button(new UnityEngine.Rect(20, 150, 100, 20), "Image 2")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/pic/item/cf1b9d16fdfaaf519b4aa960875494eef11f7a47.jpg");
}
if (GUI.Button(new UnityEngine.Rect(20, 150, 100, 20), "Image 2")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/pic/item/cf1b9d16fdfaaf519b4aa960875494eef11f7a47.jpg");
}
if (GUI.Button(new UnityEngine.Rect(20, 250, 100, 20), "Image 3")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/pic/item/2f738bd4b31c8701c1e721dd2a7f9e2f0708ffbc.jpg");
if (GUI.Button(new UnityEngine.Rect(20, 250, 100, 20), "Image 3")) {
LoadImage(
"http://a.hiphotos.baidu.com/image/pic/item/2f738bd4b31c8701c1e721dd2a7f9e2f0708ffbc.jpg");
}
}
}

private void OnEnable() {
this._windowAdapter = new EditorWindowAdapter(this);
this._windowAdapter.OnEnable();
this._paintingBinding = new PaintingBinding(this._windowAdapter);
this._paintingBinding.initInstances();
}
void createRenderTexture() {

private void LoadImage(string url) {
Dictionary<string, string> headers = new Dictionary<string, string>();
NetworkImage networkImage = new NetworkImage(url, headers);
NetworkImage networkImage = new NetworkImage(url, headers: headers);
ImageConfiguration imageConfig = new ImageConfiguration();
_stream = networkImage.resolve(imageConfig);
}

}
void drawImageRect() {
if (_stream == null || _stream.completer == null || _stream.completer._currentImgae == null) {
if (_stream == null || _stream.completer == null || _stream.completer.currentImage == null) {
return;
}

};
canvas.drawImageRect(
_stream.completer._currentImgae.image.texture,
_stream.completer.currentImage.image,
Rect.fromLTWH(100, 50, 250, 250),
paint
);

4
Tests/Editor/EditableTextWiget.cs


{
private WindowAdapter windowAdapter;
private PaintingBinding paintingBinding;
private Widget root;
private Widget image;

}
private void OnEnable() {
this.paintingBinding = new PaintingBinding(null);
paintingBinding.initInstances();
this.windowAdapter = new EditorWindowAdapter(this);
this.windowAdapter.OnEnable();
this.root = new Container(

7
Tests/Editor/Widgets.cs


public class Widgets : EditorWindow {
private WindowAdapter windowAdapter;
private PaintingBinding paintingBinding;
private readonly Func<Widget>[] _options;
private readonly string[] _optionStrings;

}
private void OnEnable() {
this.paintingBinding = new PaintingBinding(null);
paintingBinding.initInstances();
this.windowAdapter = new EditorWindowAdapter(this);
this.windowAdapter.OnEnable();
}

Widget localImage() {
var image = widgets.Image.file(
localImagePath
localImagePath,
filterMode: FilterMode.Bilinear
);
return image;

2
Runtime/async/coroutine.cs.meta


fileFormatVersion: 2
guid: d54df7319b8ca49cf9aba74db3698508
guid: edd74736642e341e29510a7e3dafb5f6
MonoImporter:
externalObjects: {}
serializedVersion: 2

371
Runtime/async/coroutine.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using RSG;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.async {
public class UIWidgetsCoroutine {
_WaitforSecondsProcessor _waitProcessor;
_WaitForCoroutineProcessor _waitForCoroutine;
_WaitForAsyncOPProcessor _waitForAsyncOPProcessor;
readonly IEnumerator _routine;
readonly Window _window;
readonly IDisposable _unhook;
readonly bool _isBackground;
internal volatile object lastResult;
internal volatile Exception lastError;
internal bool isDone;
readonly Promise<object> _promise = new Promise<object>();
public IPromise<object> promise => this._promise;
internal UIWidgetsCoroutine(IEnumerator routine, Window window, bool isBackground = false) {
D.assert(routine != null);
D.assert(window != null);
this._routine = routine;
this._window = window;
if (isBackground && BackgroundCallbacks.getInstance() != null) {
this._unhook = BackgroundCallbacks.getInstance().addCallback(this._moveNext);
} else {
this._unhook = this._window.onUpdate(this._moveNext);
}
this._isBackground = isBackground;
}
void _moveNext() {
D.assert(!this.isDone);
var lastError = this.lastError;
if (lastError != null) {
this._unhook.Dispose();
this.isDone = true;
this.lastResult = null;
if (this._isBackground) {
Timer.runInMain(() => { this._window.run(() => { this._promise.Reject(lastError); }); });
} else {
this._promise.Reject(lastError);
}
return;
}
bool hasNext;
try {
hasNext = this._processIEnumeratorRecursive(this._routine);
}
catch (Exception ex) {
this.stop(ex);
return;
}
if (!hasNext && !this.isDone) {
this._unhook.Dispose();
this.isDone = true;
D.assert(this.lastError == null);
if (this._isBackground) {
Timer.runInMain(() => { this._window.run(() => { this._promise.Resolve(this.lastResult); }); });
} else {
this._promise.Resolve(this.lastResult);
}
}
}
bool _processIEnumeratorRecursive(IEnumerator child) {
D.assert(child != null);
if (child.Current is IEnumerator nestedEnumerator) {
return this._processIEnumeratorRecursive(nestedEnumerator) || child.MoveNext();
}
if (child.Current is UIWidgetsCoroutine nestedCoroutine) {
if (this._isBackground) {
throw new Exception("nestedCoroutine is not supported in Background Coroutine");
}
this._waitForCoroutine.set(nestedCoroutine);
return this._waitForCoroutine.moveNext(child, this);
}
if (child.Current is UIWidgetsWaitForSeconds waitForSeconds) {
if (this._isBackground) {
throw new Exception("waitForSeconds is not supported in Background Coroutine");
}
this._waitProcessor.set(waitForSeconds);
return this._waitProcessor.moveNext(child);
}
if (child.Current is AsyncOperation waitForAsyncOP) {
if (this._isBackground) {
throw new Exception("asyncOperation is not supported in Background Coroutine");
}
this._waitForAsyncOPProcessor.set(waitForAsyncOP);
return this._waitForAsyncOPProcessor.moveNext(child);
}
this.lastResult = child.Current;
return child.MoveNext();
}
public void stop() {
this.stop(null);
}
internal void stop(Exception ex) {
if (this.lastError == null) {
this.lastError = ex ?? new CoroutineCanceledException();
}
}
}
struct _WaitforSecondsProcessor {
UIWidgetsWaitForSeconds _current;
double _targetTime;
public void set(UIWidgetsWaitForSeconds yieldStatement) {
if (this._current != yieldStatement) {
this._current = yieldStatement;
this._targetTime = Timer.timeSinceStartup + yieldStatement.waitTime;
}
}
public bool moveNext(IEnumerator enumerator) {
if (this._targetTime <= Timer.timeSinceStartup) {
this._current = null;
this._targetTime = 0;
return enumerator.MoveNext();
}
return true;
}
}
struct _WaitForCoroutineProcessor {
UIWidgetsCoroutine _current;
public void set(UIWidgetsCoroutine routine) {
if (this._current != routine) {
this._current = routine;
}
}
public bool moveNext(IEnumerator enumerator, UIWidgetsCoroutine parent) {
if (this._current.isDone) {
var current = this._current;
this._current = null;
if (current.lastError != null) {
parent.stop(current.lastError);
return false;
}
parent.lastResult = current.lastResult;
return enumerator.MoveNext();
}
return true;
}
}
struct _WaitForAsyncOPProcessor {
AsyncOperation _current;
public void set(AsyncOperation operation) {
if (this._current != operation) {
this._current = operation;
}
}
public bool moveNext(IEnumerator enumerator) {
if (this._current.isDone) {
this._current = null;
return enumerator.MoveNext();
}
return true;
}
}
public static class Coroutine {
public static UIWidgetsCoroutine startCoroutine(this Window owner, IEnumerator routine) {
return new UIWidgetsCoroutine(routine, owner);
}
public static UIWidgetsCoroutine startBackgroundCoroutine(this Window owner, IEnumerator routine) {
return new UIWidgetsCoroutine(routine, owner, isBackground: true);
}
}
public class CoroutineCanceledException : Exception {
}
public class UIWidgetsWaitForSeconds {
public double waitTime { get; }
public UIWidgetsWaitForSeconds(float time) {
this.waitTime = time;
}
}
internal class WindowCallbacks {
readonly LinkedList<VoidCallback> _callbackList;
readonly Window _window;
public WindowCallbacks() {
this._callbackList = new LinkedList<VoidCallback>();
}
public void update() {
var callbackList = this._callbackList.ToArray();
foreach (var callback in callbackList) {
try {
callback();
}
catch (Exception ex) {
Debug.LogError("Failed to execute callback in WindowCallbacks: " + ex);
}
}
}
public IDisposable onUpdate(VoidCallback callback) {
var node = this._callbackList.AddLast(callback);
return new _CallbackDisposable(this, node);
}
class _CallbackDisposable : IDisposable {
readonly WindowCallbacks _parent;
readonly LinkedListNode<VoidCallback> _node;
public _CallbackDisposable(WindowCallbacks parent, LinkedListNode<VoidCallback> node) {
this._parent = parent;
this._node = node;
}
public void Dispose() {
this._parent._callbackList.Remove(this._node);
}
}
}
internal class BackgroundCallbacks : IDisposable {
static BackgroundCallbacks _instance;
public static BackgroundCallbacks getInstance() {
#if UNITY_WEBGL
return null;
#endif
if (_instance == null) {
_instance = new BackgroundCallbacks(2);
}
return _instance;
}
readonly LinkedList<_CallbackNode> _callbackList;
readonly ManualResetEvent _event;
readonly Thread[] _threads;
volatile bool _aborted = false;
public BackgroundCallbacks(int threadCount = 1) {
this._callbackList = new LinkedList<_CallbackNode>();
this._event = new ManualResetEvent(false);
this._threads = new Thread[threadCount];
for (var i = 0; i < this._threads.Length; i++) {
this._threads[i] = new Thread(this._threadLoop);
this._threads[i].Start();
}
}
public void Dispose() {
foreach (var t in this._threads) {
this._aborted = true;
this._event.Set();
t.Join();
}
this._callbackList.Clear();
}
void _threadLoop() {
while (true) {
if (this._aborted) {
break;
}
LinkedListNode<_CallbackNode> node;
lock (this._callbackList) {
node = this._callbackList.First;
if (node != null) {
this._callbackList.Remove(node);
}
}
if (node == null) {
this._event.WaitOne();
this._event.Reset();
continue;
}
var callbackNode = node.Value;
D.assert(!callbackNode.isDone);
try {
callbackNode.callback();
}
catch (Exception ex) {
Debug.LogError("Failed to execute callback in BackgroundCallbacks: " + ex);
}
if (!callbackNode.isDone) {
lock (this._callbackList) {
this._callbackList.AddLast(node);
}
}
}
}
public IDisposable addCallback(VoidCallback callback) {
var node = new _CallbackNode {callback = callback};
lock (this._callbackList) {
this._callbackList.AddLast(node);
}
this._event.Set();
return new _CallbackDisposable(node);
}
class _CallbackDisposable : IDisposable {
readonly _CallbackNode _node;
public _CallbackDisposable(_CallbackNode node) {
this._node = node;
}
public void Dispose() {
this._node.isDone = true;
}
}
class _CallbackNode {
public VoidCallback callback;
public volatile bool isDone;
}
}
}

678
Runtime/ui/painting/GifDecoder.cs


using System;
using System.IO;
using System.Text;
namespace Unity.UIWidgets.ui {
// from https://github.com/avianbc/NGif/blob/master/Components/GifDecoder.cs
// https://gist.github.com/devunwired/4479231
// No DISPOSAL_PREVIOUS as its not actually widely used.
public class GifDecoder : IDisposable {
/**
* File read status: No errors.
*/
public const int STATUS_OK = 0;
/**
* File read status: Error decoding file (may be partially decoded)
*/
public const int STATUS_FORMAT_ERROR = 1;
/**
* File read status: Unable to open source.
*/
public const int STATUS_OPEN_ERROR = 2;
// max decoder pixel stack size
const int MAX_STACK_SIZE = 4096;
// input stream
Stream _inStream;
/**
* Global status code of GIF data parsing
*/
int _status;
// Global File Header values and parsing flags
volatile int _width; // full image width
volatile int _height; // full image height
bool _gctFlag; // global color table used
int _gctSize; // size of global color table
volatile int _loopCount = 1; // iterations; 0 = repeat forever
int[] _gct; // global color table
int[] _lct; // local color table
int[] _act; // active color table
int _bgIndex; // background color index
int _bgColor; // background color
int _lastBgColor; // previous bg color
int _pixelAspect; // pixel aspect ratio
bool _lctFlag; // local color table flag
bool _interlace; // interlace flag
int _lctSize; // local color table size
int _ix, _iy, _iw, _ih; // current image rectangle
int _lix, _liy, _liw, _lih; // last image rect
int[] _image; // current frame
byte[] _block = new byte[256]; // current data block
int _blockSize = 0; // block size
// last graphic control extension info
int _dispose = 0;
// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
int _lastDispose = 0;
bool _transparency = false; // use transparent color
int _delay = 0; // delay in milliseconds
int _transIndex; // transparent color index
// LZW decoder working arrays
short[] _prefix;
byte[] _suffix;
byte[] _pixelStack;
byte[] _pixels;
volatile GifFrame _currentFrame; // frames read from current file
volatile int _frameCount;
volatile bool _done;
public class GifFrame {
public byte[] bytes;
public int delay;
}
public int frameWidth => this._width;
public int frameHeight => this._height;
public GifFrame currentFrame => this._currentFrame;
public int frameCount => this._frameCount;
public int loopCount => this._loopCount;
public bool done => this._done;
void _setPixels() {
// fill in starting image contents based on last image's dispose code
if (this._lastDispose > 0) {
var n = this._frameCount - 1;
if (n > 0) {
if (this._lastDispose == 2) {
// fill last image rect area with background color
var fillcolor = this._transparency ? 0 : this._lastBgColor;
for (var i = 0; i < this._lih; i++) {
var line = i + this._liy;
if (line >= this._height) {
continue;
}
line = this._height - line - 1;
var dx = line * this._width + this._lix;
var endx = dx + this._liw;
while (dx < endx) {
this._image[dx++] = fillcolor;
}
}
}
}
}
// copy each source line to the appropriate place in the destination
int pass = 1;
int inc = 8;
int iline = 0;
for (int i = 0; i < this._ih; i++) {
int line = i;
if (this._interlace) {
if (iline >= this._ih) {
pass++;
switch (pass) {
case 2:
iline = 4;
break;
case 3:
iline = 2;
inc = 4;
break;
case 4:
iline = 1;
inc = 2;
break;
}
}
line = iline;
iline += inc;
}
line += this._iy;
if (line >= this._height) {
continue;
}
var sx = i * this._iw;
line = this._height - line - 1;
var dx = line * this._width + this._ix;
var endx = dx + this._iw;
for (; dx < endx; dx++) {
var c = this._act[this._pixels[sx++] & 0xff];
if (c != 0) {
this._image[dx] = c;
}
}
}
}
/**
* Reads GIF image from stream
*
* @param BufferedInputStream containing GIF file.
* @return read status code (0 = no errors)
*/
public int read(Stream inStream) {
this._init();
if (inStream != null) {
this._inStream = inStream;
this._readHeader();
} else {
this._status = STATUS_OPEN_ERROR;
}
return this._status;
}
public void Dispose() {
if (this._inStream != null) {
this._inStream.Dispose();
this._inStream = null;
}
}
/**
* Decodes LZW image data into pixel array.
* Adapted from John Cristy's ImageMagick.
*/
void _decodeImageData() {
const int NullCode = -1;
int npix = this._iw * this._ih;
int available,
clear,
code_mask,
code_size,
end_of_information,
in_code,
old_code,
bits,
code,
count,
i,
datum,
data_size,
first,
top,
bi,
pi;
if ((this._pixels == null) || (this._pixels.Length < npix)) {
this._pixels = new byte[npix]; // allocate new pixel array
}
if (this._prefix == null) {
this._prefix = new short[MAX_STACK_SIZE];
}
if (this._suffix == null) {
this._suffix = new byte[MAX_STACK_SIZE];
}
if (this._pixelStack == null) {
this._pixelStack = new byte[MAX_STACK_SIZE + 1];
}
// Initialize GIF data stream decoder.
data_size = this._read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = NullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
this._prefix[code] = 0;
this._suffix[code] = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix;) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = this._readBlock();
if (count <= 0) {
break;
}
bi = 0;
}
datum += (this._block[bi] & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information))
break;
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = NullCode;
continue;
}
if (old_code == NullCode) {
this._pixelStack[top++] = this._suffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
this._pixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
this._pixelStack[top++] = this._suffix[code];
code = this._prefix[code];
}
first = this._suffix[code] & 0xff;
// Add a new string to the string table,
if (available >= MAX_STACK_SIZE) {
break;
}
this._pixelStack[top++] = (byte) first;
this._prefix[available] = (short) old_code;
this._suffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0)
&& (available < MAX_STACK_SIZE)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
this._pixels[pi++] = this._pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
this._pixels[i] = 0; // clear missing pixels
}
}
/**
* Returns true if an error was encountered during reading/decoding
*/
bool _error() {
return this._status != STATUS_OK;
}
/**
* Initializes or re-initializes reader
*/
void _init() {
this._status = STATUS_OK;
this._currentFrame = null;
this._frameCount = 0;
this._done = false;
this._gct = null;
this._lct = null;
}
/**
* Reads a single byte from the input stream.
*/
int _read() {
int curByte = 0;
try {
curByte = this._inStream.ReadByte();
}
catch (IOException) {
this._status = STATUS_FORMAT_ERROR;
}
return curByte;
}
/**
* Reads next variable length block from input.
*
* @return number of bytes stored in "buffer"
*/
int _readBlock() {
this._blockSize = this._read();
int n = 0;
if (this._blockSize > 0) {
try {
int count = 0;
while (n < this._blockSize) {
count = this._inStream.Read(this._block, n, this._blockSize - n);
if (count == -1) {
break;
}
n += count;
}
}
catch (IOException) {
}
if (n < this._blockSize) {
this._status = STATUS_FORMAT_ERROR;
}
}
return n;
}
/**
* Reads color table as 256 RGB integer values
*
* @param ncolors int number of colors to read
* @return int array containing 256 colors (packed ARGB with full alpha)
*/
int[] _readColorTable(int ncolors) {
int nbytes = 3 * ncolors;
int[] tab = null;
byte[] c = new byte[nbytes];
int n = 0;
try {
n = this._inStream.Read(c, 0, c.Length);
}
catch (IOException) {
}
if (n < nbytes) {
this._status = STATUS_FORMAT_ERROR;
} else {
tab = new int[256]; // max size to avoid bounds checks
int i = 0;
int j = 0;
while (i < ncolors) {
int r = c[j++] & 0xff;
int g = c[j++] & 0xff;
int b = c[j++] & 0xff;
tab[i++] = (int) (0xff000000 | (r << 16) | (g << 8) | b);
}
}
return tab;
}
/**
* Main file parser. Reads GIF content blocks.
*/
public int nextFrame() {
// read GIF file content blocks
bool done = false;
while (!(done || this._error())) {
int code = this._read();
switch (code) {
case 0x2C: // image separator
this._readImage();
done = true;
break;
case 0x21: // extension
code = this._read();
switch (code) {
case 0xf9: // graphics control extension
this._readGraphicControlExt();
break;
case 0xff: // application extension
this._readBlock();
var appBuilder = new StringBuilder();
for (int i = 0; i < 11; i++) {
appBuilder.Append((char) this._block[i]);
}
String app = appBuilder.ToString();
if (app.Equals("NETSCAPE2.0")) {
this._readNetscapeExt();
} else {
this._skip(); // don't care
}
break;
default: // uninteresting extension
this._skip();
break;
}
break;
case 0x3b: // terminator
this._done = true;
done = true;
break;
case 0x00: // bad byte, but keep going and see what happens
break;
default:
this._status = STATUS_FORMAT_ERROR;
break;
}
}
return this._status;
}
/**
* Reads Graphics Control Extension values
*/
void _readGraphicControlExt() {
this._read(); // block size
int packed = this._read(); // packed fields
this._dispose = (packed & 0x1c) >> 2; // disposal method
if (this._dispose == 0) {
this._dispose = 1; // elect to keep old image if discretionary
}
this._transparency = (packed & 1) != 0;
this._delay = this._readShort() * 10; // delay in milliseconds
this._transIndex = this._read(); // transparent color index
this._read(); // block terminator
}
/**
* Reads GIF file header information.
*/
void _readHeader() {
var idBuilder = new StringBuilder();
for (int i = 0; i < 6; i++) {
idBuilder.Append((char) this._read());
}
var id = idBuilder.ToString();
if (!id.StartsWith("GIF")) {
this._status = STATUS_FORMAT_ERROR;
return;
}
this._readLSD();
if (this._gctFlag && !this._error()) {
this._gct = this._readColorTable(this._gctSize);
this._bgColor = this._gct[this._bgIndex];
}
}
/**
* Reads next frame image
*/
void _readImage() {
this._ix = this._readShort(); // (sub)image position & size
this._iy = this._readShort();
this._iw = this._readShort();
this._ih = this._readShort();
int packed = this._read();
this._lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
this._interlace = (packed & 0x40) != 0; // 2 - interlace flag
// 3 - sort flag
// 4-5 - reserved
this._lctSize = 2 << (packed & 7); // 6-8 - local color table size
if (this._lctFlag) {
this._lct = this._readColorTable(this._lctSize); // read table
this._act = this._lct; // make local table active
} else {
this._act = this._gct; // make global table active
if (this._bgIndex == this._transIndex) {
this._bgColor = 0;
}
}
int save = 0;
if (this._transparency) {
save = this._act[this._transIndex];
this._act[this._transIndex] = 0; // set transparent color if specified
}
if (this._act == null) {
this._status = STATUS_FORMAT_ERROR; // no color table defined
}
if (this._error()) {
return;
}
this._decodeImageData(); // decode pixel data
this._skip();
if (this._error()) {
return;
}
// create new image to receive frame data
// image =
// new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
this._image = this._image ?? new int[this._width * this._height];
this._setPixels(); // transfer pixel data to image
var bytes = new byte[this._width * this._height * sizeof(int)];
Buffer.BlockCopy(this._image, 0, bytes, 0, bytes.Length);
this._currentFrame = new GifFrame {bytes = bytes, delay = this._delay};
this._frameCount++;
if (this._transparency) {
this._act[this._transIndex] = save;
}
this._resetFrame();
}
/**
* Reads Logical Screen Descriptor
*/
void _readLSD() {
// logical screen size
this._width = this._readShort();
this._height = this._readShort();
// packed fields
int packed = this._read();
this._gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
this._gctSize = 2 << (packed & 7); // 6-8 : gct size
this._bgIndex = this._read(); // background color index
this._pixelAspect = this._read(); // pixel aspect ratio
}
/**
* Reads Netscape extenstion to obtain iteration count
*/
void _readNetscapeExt() {
do {
this._readBlock();
if (this._block[0] == 1) {
// loop count sub-block
int b1 = this._block[1] & 0xff;
int b2 = this._block[2] & 0xff;
this._loopCount = (b2 << 8) | b1;
}
} while (this._blockSize > 0 && !this._error());
}
/**
* Reads next 16-bit value, LSB first
*/
int _readShort() {
// read 16-bit value, LSB first
return this._read() | (this._read() << 8);
}
/**
* Resets frame state for reading next image.
*/
void _resetFrame() {
this._lastDispose = this._dispose;
this._lix = this._ix;
this._liy = this._iy;
this._liw = this._iw;
this._lih = this._ih;
this._lastBgColor = this._bgColor;
this._lct = null;
}
/**
* Skips variable length blocks up to and including
* next zero length block.
*/
void _skip() {
do {
this._readBlock();
} while ((this._blockSize > 0) && !this._error());
}
}
}

11
Runtime/ui/painting/GifDecoder.cs.meta


fileFormatVersion: 2
guid: 657b3aa341be94fc09e9c79dcc086f75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

63
Runtime/ui/painting/codec.cs


using System;
using RSG;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public class FrameInfo {
public Image image;
public TimeSpan duration;
}
public interface Codec : IDisposable {
int frameCount { get; }
int repetitionCount { get; }
IPromise<FrameInfo> getNextFrame();
}
public class ImageCodec : Codec {
Image _image;
public ImageCodec(Image image) {
D.assert(image != null);
this._image = image;
}
public int frameCount => 1;
public int repetitionCount => 0;
public IPromise<FrameInfo> getNextFrame() {
D.assert(this._image != null);
return Promise<FrameInfo>.Resolved(new FrameInfo {
duration = TimeSpan.Zero,
image = this._image
});
}
public void Dispose() {
if (this._image != null) {
this._image.Dispose();
this._image = null;
}
}
}
public static class CodecUtils {
public static IPromise<Codec> getCodec(byte[] bytes) {
if (GifCodec.isGif(bytes)) {
return Promise<Codec>.Resolved(new GifCodec(bytes));
}
var texture = new Texture2D(2, 2);
texture.LoadImage(bytes);
return Promise<Codec>.Resolved(new ImageCodec(new Image(texture)));
}
public static IPromise<Codec> getCodec(Texture2D texture) {
return Promise<Codec>.Resolved(new ImageCodec(new Image(texture)));
}
}
}

11
Runtime/ui/painting/codec.cs.meta


fileFormatVersion: 2
guid: a76c61db5ad8a4faf9c0667883dce90d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

148
Runtime/ui/painting/codec_gif.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using RSG;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using UnityEngine;
using Timer = Unity.UIWidgets.async.Timer;
namespace Unity.UIWidgets.ui {
public class GifCodec : Codec {
public static bool isGif(byte[] bytes) {
return bytes != null && bytes.Length >= 3 && bytes[0] == 'G' && bytes[1] == 'I' && bytes[2] == 'F';
}
public class FrameData {
public FrameInfo frameInfo;
public GifDecoder.GifFrame gifFrame;
}
readonly List<Promise<FrameData>> _frames;
volatile int _width;
volatile int _height;
volatile int _frameCount;
volatile int _repetitionCount;
volatile bool _isDone;
volatile int _frameIndex;
readonly UIWidgetsCoroutine _coroutine;
public GifCodec(byte[] bytes) {
D.assert(bytes != null);
D.assert(isGif(bytes));
this._frames = new List<Promise<FrameData>>();
this._width = 0;
this._height = 0;
this._frameCount = 0;
this._repetitionCount = 0;
this._isDone = false;
this._frameIndex = 0;
this._coroutine = Window.instance.startBackgroundCoroutine(
this._startDecoding(bytes, Window.instance));
}
IEnumerator _startDecoding(byte[] bytes, Window window) {
var bytesStream = new MemoryStream(bytes);
var gifDecoder = new GifDecoder();
if (gifDecoder.read(bytesStream) != GifDecoder.STATUS_OK) {
throw new Exception("Failed to decode gif.");
}
this._width = gifDecoder.frameWidth;
this._height = gifDecoder.frameHeight;
int i = 0;
while (true) {
yield return null;
if (gifDecoder.nextFrame() != GifDecoder.STATUS_OK) {
throw new Exception("Failed to decode gif.");
}
if (gifDecoder.done) {
break;
}
var frameData = new FrameData {
gifFrame = gifDecoder.currentFrame,
};
Promise<FrameData> frame;
lock (this._frames) {
if (i < this._frames.Count) {
} else {
D.assert(this._frames.Count == i);
this._frames.Add(new Promise<FrameData>());
}
frame = this._frames[i];
}
Timer.runInMain(() => { window.run(() => { frame.Resolve(frameData); }); });
i++;
}
D.assert(gifDecoder.frameCount == i);
this._frameCount = gifDecoder.frameCount;
this._repetitionCount = gifDecoder.loopCount;
this._isDone = true;
}
public int frameCount => this._frameCount;
public int repetitionCount => this._repetitionCount - 1;
void _nextFrame() {
this._frameIndex++;
if (this._isDone && this._frameIndex >= this._frameCount) {
this._frameIndex = 0;
}
}
public IPromise<FrameInfo> getNextFrame() {
Promise<FrameData> frame;
lock (this._frames) {
if (this._frameIndex == this._frames.Count) {
this._frames.Add(new Promise<FrameData>());
} else {
D.assert(this._frameIndex < this._frames.Count);
}
frame = this._frames[this._frameIndex];
}
return frame.Then(frameData => {
this._nextFrame();
if (frameData.frameInfo != null) {
return frameData.frameInfo;
}
var tex = new Texture2D(this._width, this._height, TextureFormat.BGRA32, false);
tex.LoadRawTextureData(frameData.gifFrame.bytes);
tex.Apply();
frameData.frameInfo = new FrameInfo {
image = new Image(tex),
duration = TimeSpan.FromMilliseconds(frameData.gifFrame.delay)
};
frameData.gifFrame = null; // dispose gifFrame
return frameData.frameInfo;
});
}
public void Dispose() {
this._coroutine.stop();
}
}
}

11
Runtime/ui/painting/codec_gif.cs.meta


fileFormatVersion: 2
guid: e60cc21bf138c4059a6a65888c5959e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

3
Runtime/lib.meta


fileFormatVersion: 2
guid: d5b7bf0751a64454ba399aad57ab71fc
timeCreated: 1535510135

8
Assets.meta


fileFormatVersion: 2
guid: 99b1966ad095147729a4d478ccab99c7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

/Runtime/lib/cache_manager/cache_meta.cs.meta → /Runtime/async/coroutine.cs.meta

正在加载...
取消
保存