Skip to content

Commit 2572cb2

Browse files
author
Roman Shapiro
committed
Closes #7
1 parent 55e833e commit 2572cb2

10 files changed

Lines changed: 278 additions & 96 deletions

File tree

src/AnimatedFrameResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#endif
88
class AnimatedFrameResult : ImageResult
99
{
10-
public int Delay
10+
public int DelayInMs
1111
{
1212
get; set;
1313
}

src/AnimatedGifEnumerator.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Runtime.InteropServices;
6+
7+
namespace StbImageSharp
8+
{
9+
internal class AnimatedGifEnumerator: IEnumerator<AnimatedFrameResult>
10+
{
11+
private readonly StbImage.stbi__context _context;
12+
private StbImage.stbi__gif _gif;
13+
private AnimatedFrameResult _current;
14+
15+
public ColorComponents ColorComponents { get; private set; }
16+
17+
public AnimatedFrameResult Current => _current;
18+
19+
object IEnumerator.Current => _current;
20+
21+
public AnimatedGifEnumerator(Stream input, ColorComponents colorComponents)
22+
{
23+
if (input == null)
24+
{
25+
throw new ArgumentNullException(nameof(input));
26+
}
27+
28+
_context = new StbImage.stbi__context(input);
29+
30+
if (StbImage.stbi__gif_test(_context) == 0)
31+
{
32+
throw new Exception("Input stream is not GIF file.");
33+
}
34+
35+
_gif = new StbImage.stbi__gif();
36+
ColorComponents = colorComponents;
37+
}
38+
39+
~AnimatedGifEnumerator()
40+
{
41+
Dispose(false);
42+
}
43+
44+
protected unsafe virtual void Dispose(bool disposing)
45+
{
46+
if (disposing)
47+
{
48+
if (_gif != null)
49+
{
50+
_gif.Dispose();
51+
_gif = null;
52+
}
53+
}
54+
}
55+
56+
public void Dispose()
57+
{
58+
Dispose(true);
59+
GC.SuppressFinalize(this);
60+
}
61+
62+
public unsafe bool MoveNext()
63+
{
64+
// Read next frame
65+
int ccomp;
66+
byte two_back;
67+
var result = StbImage.stbi__gif_load_next(_context, _gif, &ccomp, (int)ColorComponents, &two_back);
68+
if (result == null)
69+
{
70+
return false;
71+
}
72+
73+
if (_current == null)
74+
{
75+
_current = new AnimatedFrameResult
76+
{
77+
Width = _gif.w,
78+
Height = _gif.h,
79+
SourceComp = (ColorComponents)ccomp,
80+
Comp = ColorComponents == ColorComponents.Default ? (ColorComponents)ccomp : ColorComponents
81+
};
82+
83+
_current.Data = new byte[_current.Width * _current.Height * (int)_current.Comp];
84+
}
85+
86+
_current.DelayInMs = _gif.delay;
87+
88+
Marshal.Copy(new IntPtr(result), _current.Data, 0, _current.Data.Length);
89+
90+
return true;
91+
}
92+
93+
public void Reset()
94+
{
95+
throw new NotImplementedException();
96+
}
97+
}
98+
99+
internal class AnimatedGifEnumerable : IEnumerable<AnimatedFrameResult>
100+
{
101+
private readonly Stream _input;
102+
103+
public ColorComponents ColorComponents { get; private set; }
104+
105+
public AnimatedGifEnumerable(Stream input, ColorComponents colorComponents)
106+
{
107+
_input = input;
108+
ColorComponents = colorComponents;
109+
}
110+
111+
public IEnumerator<AnimatedFrameResult> GetEnumerator()
112+
{
113+
return new AnimatedGifEnumerator(_input, ColorComponents);
114+
}
115+
116+
IEnumerator IEnumerable.GetEnumerator()
117+
{
118+
return GetEnumerator();
119+
}
120+
}
121+
}

src/ImageResult.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Runtime.InteropServices;
45

@@ -82,5 +83,11 @@ public static ImageResult FromMemory(byte[] data, ColorComponents requiredCompon
8283
return FromStream(stream, requiredComponents);
8384
}
8485
}
86+
87+
public static IEnumerable<AnimatedFrameResult> AnimatedGifFramesFromStream(Stream stream,
88+
ColorComponents requiredComponents = ColorComponents.Default)
89+
{
90+
return new AnimatedGifEnumerable(stream, requiredComponents);
91+
}
8592
}
8693
}

src/StbImage.Generated.Gif.cs

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,20 @@ public static int stbi__gif_header(stbi__context s, stbi__gif g, int* comp, int
5252

5353
public static int stbi__gif_info_raw(stbi__context s, int* x, int* y, int* comp)
5454
{
55-
var g = new stbi__gif();
56-
if (stbi__gif_header(s, g, comp, 1) == 0)
57-
{
58-
stbi__rewind(s);
59-
return 0;
55+
using (var g = new stbi__gif())
56+
{
57+
if (stbi__gif_header(s, g, comp, 1) == 0)
58+
{
59+
stbi__rewind(s);
60+
return 0;
61+
}
62+
63+
if (x != null)
64+
*x = g.w;
65+
if (y != null)
66+
*y = g.h;
6067
}
6168

62-
if (x != null)
63-
*x = g.w;
64-
if (y != null)
65-
*y = g.h;
66-
6769
return 1;
6870
}
6971

@@ -362,61 +364,6 @@ public static void stbi__out_gif_code(stbi__gif g, ushort code)
362364
}
363365
}
364366

365-
public static void* stbi__load_gif_main(stbi__context s, int** delays, int* x, int* y, int* z, int* comp,
366-
int req_comp)
367-
{
368-
if (stbi__gif_test(s) != 0)
369-
{
370-
var layers = 0;
371-
byte* u = null;
372-
byte* _out_ = null;
373-
byte* two_back = null;
374-
var g = new stbi__gif();
375-
var stride = 0;
376-
if (delays != null)
377-
*delays = null;
378-
do
379-
{
380-
u = stbi__gif_load_next(s, g, comp, req_comp, two_back);
381-
if (u != null)
382-
{
383-
*x = g.w;
384-
*y = g.h;
385-
++layers;
386-
stride = g.w * g.h * 4;
387-
if (_out_ != null)
388-
{
389-
_out_ = (byte*)CRuntime.realloc(_out_, (ulong)(layers * stride));
390-
if (delays != null)
391-
*delays = (int*)CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers));
392-
}
393-
else
394-
{
395-
_out_ = (byte*)stbi__malloc((ulong)(layers * stride));
396-
if (delays != null)
397-
*delays = (int*)stbi__malloc((ulong)(layers * sizeof(int)));
398-
}
399-
400-
CRuntime.memcpy(_out_ + (layers - 1) * stride, u, (ulong)stride);
401-
if (layers >= 2)
402-
two_back = _out_ - 2 * stride;
403-
if (delays != null)
404-
(*delays)[layers - 1U] = g.delay;
405-
}
406-
} while (u != null);
407-
408-
CRuntime.free(g._out_);
409-
CRuntime.free(g.history);
410-
CRuntime.free(g.background);
411-
if (req_comp != 0 && req_comp != 4)
412-
_out_ = stbi__convert_format(_out_, 4, req_comp, (uint)(layers * g.w), (uint)g.h);
413-
*z = layers;
414-
return _out_;
415-
}
416-
417-
return (byte*)(ulong)(stbi__err("not GIF") != 0 ? (byte*)null : null);
418-
}
419-
420367
public static void* stbi__gif_load(stbi__context s, int* x, int* y, int* comp, int req_comp,
421368
stbi__result_info* ri)
422369
{

src/StbImage.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ public struct stbi__gif_lzw
154154

155155
public class stbi__gif : IDisposable
156156
{
157-
public byte* _out_;
158-
public byte* background;
157+
public byte* _out_ = null;
158+
public byte* background = null;
159159
public int bgindex;
160160
public stbi__gif_lzw* codes = (stbi__gif_lzw*)stbi__malloc(8192 * sizeof(stbi__gif_lzw));
161161
public byte* color_table;
@@ -165,13 +165,13 @@ public class stbi__gif : IDisposable
165165
public int eflags;
166166
public int flags;
167167
public int h;
168-
public byte* history;
168+
public byte* history = null;
169169
public int lflags;
170170
public int line_size;
171-
public byte* lpal;
171+
public byte* lpal = (byte*)stbi__malloc(256 * 4 * sizeof(byte));
172172
public int max_x;
173173
public int max_y;
174-
public byte* pal;
174+
public byte* pal = (byte*)stbi__malloc(256 * 4 * sizeof(byte));
175175
public int parse;
176176
public int ratio;
177177
public int start_x;
@@ -180,12 +180,6 @@ public class stbi__gif : IDisposable
180180
public int transparent;
181181
public int w;
182182

183-
public stbi__gif()
184-
{
185-
pal = (byte*)stbi__malloc(256 * 4 * sizeof(byte));
186-
lpal = (byte*)stbi__malloc(256 * 4 * sizeof(byte));
187-
}
188-
189183
public void Dispose()
190184
{
191185
if (pal != null)
@@ -205,6 +199,24 @@ public void Dispose()
205199
CRuntime.free(codes);
206200
codes = null;
207201
}
202+
203+
if (_out_ != null)
204+
{
205+
CRuntime.free(_out_);
206+
_out_ = null;
207+
}
208+
209+
if (background != null)
210+
{
211+
CRuntime.free(background);
212+
background = null;
213+
}
214+
215+
if (history != null)
216+
{
217+
CRuntime.free(history);
218+
history = null;
219+
}
208220
}
209221

210222
~stbi__gif()

src/StbImageSharp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<Description>C# port of the stb_image.h</Description>
88
<PackageLicenseUrl>Public Domain</PackageLicenseUrl>
99
<PackageProjectUrl>https://github.com/StbSharp/StbImageSharp</PackageProjectUrl>
10-
<Version>2.22.5</Version>
10+
<Version>2.22.6</Version>
1111
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1212
</PropertyGroup>
1313

5.69 MB
Loading

tests/StbImageSharp.Tests/Tests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,30 @@ public void Info(string filename, int headerSize, int width, int height, ColorCo
8787
Assert.AreEqual(info.ColorComponents, colorComponents);
8888
Assert.AreEqual(info.BitsPerChannel, is16bit ? 16 : 8);
8989
}
90+
91+
[TestCase("somersault.gif", 384, 480, ColorComponents.RedGreenBlueAlpha, 43)]
92+
public void AnimatedGifFrames(string fileName, int width, int height, ColorComponents colorComponents, int originalFrameCount)
93+
{
94+
using (var stream = _assembly.OpenResourceStream(fileName))
95+
{
96+
var frameCount = 0;
97+
foreach(var frame in ImageResult.AnimatedGifFramesFromStream(stream))
98+
{
99+
Assert.AreEqual(frame.Width, width);
100+
Assert.AreEqual(frame.Height, height);
101+
Assert.AreEqual(frame.Comp, colorComponents);
102+
Assert.IsNotNull(frame.Data);
103+
Assert.AreEqual(frame.Data.Length, frame.Width * frame.Height * (int)frame.Comp);
104+
105+
++frameCount;
106+
}
107+
108+
Assert.AreEqual(frameCount, originalFrameCount);
109+
110+
stream.Seek(0, SeekOrigin.Begin);
111+
}
112+
113+
Assert.AreEqual(MemoryStats.Allocations, 0);
114+
}
90115
}
91116
}

tests/StbImageSharp.Viewer/Program.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ static void Main(string[] args)
1515
{
1616
if (args.Length == 0)
1717
{
18-
Console.WriteLine("Usage: StbImageSharp.Viewer <path_to_image_file>");
18+
Console.WriteLine("Usage: StbImageSharp.Viewer <path_to_image_file> [-animated-gif]");
1919
return;
2020
}
2121

2222
try
2323
{
24-
using (var game = new ViewerGame(args[0]))
24+
var isAnimatedGif = false;
25+
if (args.Length > 1 && args[1] == "-animated-gif")
26+
{
27+
isAnimatedGif = true;
28+
}
29+
30+
using (var game = new ViewerGame(args[0], isAnimatedGif))
2531
game.Run();
2632
}
2733
catch(Exception ex)

0 commit comments

Comments
 (0)