Skip to content
This repository was archived by the owner on Apr 29, 2021. It is now read-only.

Commit 3f950f9

Browse files
authored
Merge pull request #221 from UnityTech/gif
Fix GIF memory overflow problem.
2 parents 0da2258 + f2df773 commit 3f950f9

File tree

4 files changed

+64
-104
lines changed

4 files changed

+64
-104
lines changed

Runtime/painting/image_stream.cs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -363,24 +363,15 @@ bool _hasFrameDurationPassed(TimeSpan timestamp) {
363363
}
364364

365365
void _decodeNextFrameAndSchedule() {
366-
this._codec.getNextFrame().Then(frame => {
367-
this._nextFrame = frame;
366+
var frame = this._codec.getNextFrame();
367+
this._nextFrame = frame;
368368

369-
if (this._codec.frameCount == 1) {
370-
this._emitFrame(new ImageInfo(image: this._nextFrame.image, scale: this._scale));
371-
return;
372-
}
369+
if (this._codec.frameCount == 1) {
370+
this._emitFrame(new ImageInfo(image: this._nextFrame.image, scale: this._scale));
371+
return;
372+
}
373373

374-
SchedulerBinding.instance.scheduleFrameCallback(this._handleAppFrame);
375-
},
376-
ex => {
377-
this.reportError(
378-
context: "resolving an image frame",
379-
exception: ex,
380-
informationCollector: this._informationCollector,
381-
silent: true
382-
);
383-
});
374+
SchedulerBinding.instance.scheduleFrameCallback(this._handleAppFrame);
384375
}
385376

386377
void _emitFrame(ImageInfo imageInfo) {

Runtime/ui/painting/GifDecoder.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,11 @@ void _readHeader() {
558558
this._gct = this._readColorTable(this._gctSize);
559559
this._bgColor = this._gct[this._bgIndex];
560560
}
561+
562+
this._currentFrame = new GifFrame {
563+
bytes = new byte[this._width * this._height * sizeof(int)],
564+
delay = 0
565+
};
561566
}
562567

563568
/**
@@ -616,9 +621,8 @@ void _readImage() {
616621

617622
this._setPixels(); // transfer pixel data to image
618623

619-
var bytes = new byte[this._width * this._height * sizeof(int)];
620-
Buffer.BlockCopy(this._image, 0, bytes, 0, bytes.Length);
621-
this._currentFrame = new GifFrame {bytes = bytes, delay = this._delay};
624+
Buffer.BlockCopy(this._image, 0, this._currentFrame.bytes, 0, this._currentFrame.bytes.Length);
625+
this._currentFrame.delay = this._delay;
622626
this._frameCount++;
623627

624628
if (this._transparency) {

Runtime/ui/painting/codec.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class FrameInfo {
1212
public interface Codec : IDisposable {
1313
int frameCount { get; }
1414
int repetitionCount { get; }
15-
IPromise<FrameInfo> getNextFrame();
15+
FrameInfo getNextFrame();
1616
}
1717

1818
public class ImageCodec : Codec {
@@ -31,13 +31,13 @@ public int repetitionCount {
3131
get { return 0; }
3232
}
3333

34-
public IPromise<FrameInfo> getNextFrame() {
34+
public FrameInfo getNextFrame() {
3535
D.assert(this._image != null);
3636

37-
return Promise<FrameInfo>.Resolved(new FrameInfo {
37+
return new FrameInfo {
3838
duration = TimeSpan.Zero,
3939
image = this._image
40-
});
40+
};
4141
}
4242

4343
public void Dispose() {

Runtime/ui/painting/codec_gif.cs

Lines changed: 46 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using System;
22
using System.Collections;
3-
using System.Collections.Generic;
43
using System.IO;
54
using RSG;
6-
using Unity.UIWidgets.async;
75
using Unity.UIWidgets.foundation;
86
using UnityEngine;
97

@@ -18,80 +16,75 @@ public class FrameData {
1816
public GifDecoder.GifFrame gifFrame;
1917
}
2018

21-
readonly List<Promise<FrameData>> _frames;
19+
volatile byte[] _bytes;
2220
volatile int _width;
2321
volatile int _height;
2422
volatile int _frameCount;
2523
volatile int _repetitionCount;
2624
volatile bool _isDone;
2725
volatile int _frameIndex;
2826
volatile Texture2D _texture;
29-
readonly UIWidgetsCoroutine _coroutine;
27+
volatile FrameData _frameData;
28+
volatile Image _image;
29+
volatile GifDecoder _decoder;
30+
volatile MemoryStream _stream;
31+
IEnumerator _coroutine;
3032

3133
public GifCodec(byte[] bytes) {
3234
D.assert(bytes != null);
3335
D.assert(isGif(bytes));
3436

35-
this._frames = new List<Promise<FrameData>>();
36-
this._width = 0;
37-
this._height = 0;
3837
this._frameCount = 0;
3938
this._repetitionCount = 0;
4039
this._isDone = false;
4140
this._frameIndex = 0;
42-
43-
this._coroutine = Window.instance.startBackgroundCoroutine(
44-
this._startDecoding(bytes, Window.instance));
41+
this._bytes = bytes;
42+
this._coroutine = this._startDecoding();
43+
this._decoder = new GifDecoder();
44+
this._stream = new MemoryStream(this._bytes);
45+
this._frameData = new FrameData() {
46+
frameInfo = new FrameInfo()
47+
};
4548
}
4649

47-
IEnumerator _startDecoding(byte[] bytes, Window window) {
48-
var bytesStream = new MemoryStream(bytes);
50+
IEnumerator _startDecoding() {
51+
this._stream.Seek(0, SeekOrigin.Begin);
4952

50-
var gifDecoder = new GifDecoder();
51-
if (gifDecoder.read(bytesStream) != GifDecoder.STATUS_OK) {
53+
if (this._decoder.read(this._stream) != GifDecoder.STATUS_OK) {
5254
throw new Exception("Failed to decode gif.");
5355
}
5456

55-
this._width = gifDecoder.frameWidth;
56-
this._height = gifDecoder.frameHeight;
57+
this._width = this._decoder.frameWidth;
58+
this._height = this._decoder.frameHeight;
59+
60+
if (this._texture == null) {
61+
this._texture = new Texture2D(this._width, this._height, TextureFormat.BGRA32, false);
62+
this._texture.hideFlags = HideFlags.HideAndDontSave;
63+
this._image = new Image(this._texture);
64+
this._frameData.frameInfo.image = this._image;
65+
}
66+
this._frameData.gifFrame = this._decoder.currentFrame;
67+
D.assert(this._frameData.gifFrame != null);
5768

5869
int i = 0;
5970
while (true) {
60-
yield return null;
61-
62-
if (gifDecoder.nextFrame() != GifDecoder.STATUS_OK) {
71+
if (this._decoder.nextFrame() != GifDecoder.STATUS_OK) {
6372
throw new Exception("Failed to decode gif.");
6473
}
6574

66-
if (gifDecoder.done) {
75+
if (this._decoder.done) {
6776
break;
6877
}
6978

70-
var frameData = new FrameData {
71-
gifFrame = gifDecoder.currentFrame,
72-
};
73-
74-
Promise<FrameData> frame;
75-
76-
lock (this._frames) {
77-
if (i < this._frames.Count) {
78-
}
79-
else {
80-
D.assert(this._frames.Count == i);
81-
this._frames.Add(new Promise<FrameData>());
82-
}
83-
84-
frame = this._frames[i];
85-
}
86-
87-
window.runInMain(() => { frame.Resolve(frameData); });
8879

8980
i++;
81+
82+
yield return null;
9083
}
9184

92-
D.assert(gifDecoder.frameCount == i);
93-
this._frameCount = gifDecoder.frameCount;
94-
this._repetitionCount = gifDecoder.loopCount;
85+
D.assert(this._decoder.frameCount == i);
86+
this._frameCount = this._decoder.frameCount;
87+
this._repetitionCount = this._decoder.loopCount;
9588
this._isDone = true;
9689
}
9790

@@ -107,54 +100,26 @@ public int repetitionCount {
107100
void _nextFrame() {
108101
this._frameIndex++;
109102

103+
this._coroutine.MoveNext();
104+
110105
if (this._isDone && this._frameIndex >= this._frameCount) {
111106
this._frameIndex = 0;
107+
this._isDone = false;
108+
this._coroutine = this._startDecoding();
109+
this._coroutine.MoveNext();
112110
}
113111
}
114112

115-
public IPromise<FrameInfo> getNextFrame() {
116-
Promise<FrameData> frame;
117-
118-
lock (this._frames) {
119-
if (this._frameIndex == this._frames.Count) {
120-
this._frames.Add(new Promise<FrameData>());
121-
}
122-
else {
123-
D.assert(this._frameIndex < this._frames.Count);
124-
}
125-
126-
frame = this._frames[this._frameIndex];
127-
}
128-
129-
130-
return frame.Then(frameData => {
131-
this._nextFrame();
132-
133-
if (this._texture == null) {
134-
this._texture = new Texture2D(this._width, this._height, TextureFormat.BGRA32, false);
135-
this._texture.hideFlags = HideFlags.HideAndDontSave;
136-
}
137-
138-
var tex = this._texture;
139-
tex.LoadRawTextureData(frameData.gifFrame.bytes);
140-
tex.Apply();
141-
142-
if (frameData.frameInfo != null) {
143-
return frameData.frameInfo;
144-
}
145-
146-
frameData.frameInfo = new FrameInfo {
147-
image = new Image(tex),
148-
duration = TimeSpan.FromMilliseconds(frameData.gifFrame.delay)
149-
};
150-
// frameData.gifFrame = null; // dispose gifFrame
151-
152-
return frameData.frameInfo;
153-
});
113+
public FrameInfo getNextFrame() {
114+
this._nextFrame();
115+
this._texture.LoadRawTextureData(this._frameData.gifFrame.bytes);
116+
this._texture.Apply();
117+
this._frameData.frameInfo.duration = TimeSpan.FromMilliseconds(this._frameData.gifFrame.delay);
118+
return this._frameData.frameInfo;
154119
}
155120

156121
public void Dispose() {
157-
this._coroutine.stop();
122+
this._decoder.Dispose();
158123
}
159124
}
160125
}

0 commit comments

Comments
 (0)