Skip to content

Commit

Permalink
Now support GIF
Browse files Browse the repository at this point in the history
  • Loading branch information
jarvisniu committed Mar 3, 2016
1 parent ff67ef2 commit dd2d295
Show file tree
Hide file tree
Showing 24 changed files with 1,928 additions and 4 deletions.
21 changes: 21 additions & 0 deletions Niv.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@
<Compile Include="src\ImageInfo.cs" />
<Compile Include="src\Controller.cs" />
<Compile Include="src\Transformer.cs" />
<Compile Include="src\WpfAnimatedGif\AnimationCache.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifApplicationExtension.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifBlock.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifBlockKind.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifColor.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifCommentExtension.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifDecoderException.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifExtension.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifFile.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifFrame.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifGraphicControlExtension.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifHeader.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifHelpers.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifImageData.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifImageDescriptor.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifLogicalScreenDescriptor.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifPlainTextExtension.cs" />
<Compile Include="src\WpfAnimatedGif\Docoding\GifTrailer.cs" />
<Compile Include="src\WpfAnimatedGif\ImageAnimationController.cs" />
<Compile Include="src\WpfAnimatedGif\ImageBehavior.cs" />
<Compile Include="xaml\AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -166,5 +186,6 @@
<ItemGroup>
<Resource Include="res\theme-light\icon-help.png" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Binary file modified exe/Niv.exe
Binary file not shown.
4 changes: 2 additions & 2 deletions prop/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
ResourceDictionaryLocation.SourceAssembly
)]

[assembly: AssemblyVersion("0.2.2.0")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyVersion("0.3.0.0")]
[assembly: AssemblyFileVersion("0.3.0.0")]
151 changes: 151 additions & 0 deletions src/WpfAnimatedGif/AnimationCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace WpfAnimatedGif
{
static class AnimationCache
{
private class CacheKey
{
private readonly ImageSource _source;
private readonly RepeatBehavior _repeatBehavior;

public CacheKey(ImageSource source, RepeatBehavior repeatBehavior)
{
_source = source;
_repeatBehavior = repeatBehavior;
}

private bool Equals(CacheKey other)
{
return ImageEquals(_source, other._source)
&& Equals(_repeatBehavior, other._repeatBehavior);
}

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 Equals((CacheKey)obj);
}

public override int GetHashCode()
{
unchecked
{
return (ImageGetHashCode(_source) * 397) ^ _repeatBehavior.GetHashCode();
}
}

private static int ImageGetHashCode(ImageSource image)
{
if (image != null)
{
var uri = GetUri(image);
if (uri != null)
return uri.GetHashCode();
}
return 0;
}

private static bool ImageEquals(ImageSource x, ImageSource y)
{
if (Equals(x, y))
return true;
if ((x == null) != (y == null))
return false;
// They can't both be null or Equals would have returned true
// and if any is null, the previous would have detected it
// ReSharper disable PossibleNullReferenceException
if (x.GetType() != y.GetType())
return false;
// ReSharper restore PossibleNullReferenceException
var xUri = GetUri(x);
var yUri = GetUri(y);
return xUri != null && xUri == yUri;
}

private static Uri GetUri(ImageSource image)
{
var bmp = image as BitmapImage;
if (bmp != null && bmp.UriSource != null)
{
if (bmp.UriSource.IsAbsoluteUri)
return bmp.UriSource;
if (bmp.BaseUri != null)
return new Uri(bmp.BaseUri, bmp.UriSource);
}
var frame = image as BitmapFrame;
if (frame != null)
{
string s = frame.ToString();
if (s != frame.GetType().FullName)
{
Uri fUri;
if (Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out fUri))
{
if (fUri.IsAbsoluteUri)
return fUri;
if (frame.BaseUri != null)
return new Uri(frame.BaseUri, fUri);
}
}
}
return null;
}
}

private static readonly Dictionary<CacheKey, ObjectAnimationUsingKeyFrames> _animationCache = new Dictionary<CacheKey, ObjectAnimationUsingKeyFrames>();
private static readonly Dictionary<CacheKey, int> _referenceCount = new Dictionary<CacheKey, int>();

public static void IncrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior)
{
var cacheKey = new CacheKey(source, repeatBehavior);
int count;
_referenceCount.TryGetValue(cacheKey, out count);
count++;
_referenceCount[cacheKey] = count;
}

public static void DecrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior)
{
var cacheKey = new CacheKey(source, repeatBehavior);
int count;
_referenceCount.TryGetValue(cacheKey, out count);
if (count > 0)
{
count--;
_referenceCount[cacheKey] = count;
}
if (count == 0)
{
_animationCache.Remove(cacheKey);
_referenceCount.Remove(cacheKey);
}
}

public static void AddAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation)
{
var key = new CacheKey(source, repeatBehavior);
_animationCache[key] = animation;
}

public static void RemoveAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation)
{
var key = new CacheKey(source, repeatBehavior);
_animationCache.Remove(key);
}

public static ObjectAnimationUsingKeyFrames GetAnimation(ImageSource source, RepeatBehavior repeatBehavior)
{
var key = new CacheKey(source, repeatBehavior);
ObjectAnimationUsingKeyFrames animation;
_animationCache.TryGetValue(key, out animation);
return animation;
}
}
}
50 changes: 50 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifApplicationExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Text;

namespace WpfAnimatedGif.Decoding
{
// label 0xFF
internal class GifApplicationExtension : GifExtension
{
internal const int ExtensionLabel = 0xFF;

public int BlockSize { get; private set; }
public string ApplicationIdentifier { get; private set; }
public byte[] AuthenticationCode { get; private set; }
public byte[] Data { get; private set; }

private GifApplicationExtension()
{
}

internal override GifBlockKind Kind
{
get { return GifBlockKind.SpecialPurpose; }
}

internal static GifApplicationExtension ReadApplication(Stream stream)
{
var ext = new GifApplicationExtension();
ext.Read(stream);
return ext;
}

private void Read(Stream stream)
{
// Note: at this point, the label (0xFF) has already been read

byte[] bytes = new byte[12];
stream.ReadAll(bytes, 0, bytes.Length);
BlockSize = bytes[0]; // should always be 11
if (BlockSize != 11)
throw GifHelpers.InvalidBlockSizeException("Application Extension", 11, BlockSize);

ApplicationIdentifier = Encoding.ASCII.GetString(bytes, 1, 8);
byte[] authCode = new byte[3];
Array.Copy(bytes, 9, authCode, 0, 3);
AuthenticationCode = authCode;
Data = GifHelpers.ReadDataBlocks(stream, false);
}
}
}
28 changes: 28 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.IO;

namespace WpfAnimatedGif.Decoding
{
internal abstract class GifBlock
{
internal static GifBlock ReadBlock(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly)
{
int blockId = stream.ReadByte();
if (blockId < 0)
throw GifHelpers.UnexpectedEndOfStreamException();
switch (blockId)
{
case GifExtension.ExtensionIntroducer:
return GifExtension.ReadExtension(stream, controlExtensions, metadataOnly);
case GifFrame.ImageSeparator:
return GifFrame.ReadFrame(stream, controlExtensions, metadataOnly);
case GifTrailer.TrailerByte:
return GifTrailer.ReadTrailer();
default:
throw GifHelpers.UnknownBlockTypeException(blockId);
}
}

internal abstract GifBlockKind Kind { get; }
}
}
10 changes: 10 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifBlockKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace WpfAnimatedGif.Decoding
{
internal enum GifBlockKind
{
Control,
GraphicRendering,
SpecialPurpose,
Other
}
}
25 changes: 25 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifColor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace WpfAnimatedGif.Decoding
{
internal struct GifColor
{
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;

internal GifColor(byte r, byte g, byte b)
{
_r = r;
_g = g;
_b = b;
}

public byte R { get { return _r; } }
public byte G { get { return _g; } }
public byte B { get { return _b; } }

public override string ToString()
{
return string.Format("#{0:x2}{1:x2}{2:x2}", _r, _g, _b);
}
}
}
37 changes: 37 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifCommentExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.IO;
using System.Text;

namespace WpfAnimatedGif.Decoding
{
internal class GifCommentExtension : GifExtension
{
internal const int ExtensionLabel = 0xFE;

public string Text { get; private set; }

private GifCommentExtension()
{
}

internal override GifBlockKind Kind
{
get { return GifBlockKind.SpecialPurpose; }
}

internal static GifCommentExtension ReadComment(Stream stream)
{
var comment = new GifCommentExtension();
comment.Read(stream);
return comment;
}

private void Read(Stream stream)
{
// Note: at this point, the label (0xFE) has already been read

var bytes = GifHelpers.ReadDataBlocks(stream, false);
if (bytes != null)
Text = Encoding.ASCII.GetString(bytes);
}
}
}
16 changes: 16 additions & 0 deletions src/WpfAnimatedGif/Docoding/GifDecoderException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace WpfAnimatedGif.Decoding
{
[Serializable]
internal class GifDecoderException : Exception
{
internal GifDecoderException() { }
internal GifDecoderException(string message) : base(message) { }
internal GifDecoderException(string message, Exception inner) : base(message, inner) { }
protected GifDecoderException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
}
Loading

0 comments on commit dd2d295

Please sign in to comment.