Skip to content

Commit

Permalink
support animatedgif and image sizing; refs #60
Browse files Browse the repository at this point in the history
  • Loading branch information
whistyun committed May 6, 2023
1 parent 84d4291 commit 3a7d87a
Show file tree
Hide file tree
Showing 16 changed files with 738 additions and 145 deletions.
212 changes: 212 additions & 0 deletions MdXaml.AnimatedGif/AnimatedGifLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
using MdXaml.Plugins;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using WpfAnimatedGif;

namespace MdXaml.AnimatedGif
{
public class AnimatedGifLoader : IElementLoader, IPreferredLoader
{
private static readonly byte[] G87AMagic = Encoding.ASCII.GetBytes("GIF87a");
private static readonly byte[] G89AMagic = Encoding.ASCII.GetBytes("GIF89a");
private static readonly byte[] NetscapeMagic = Encoding.ASCII.GetBytes("NETSCAPE2.0");
private static readonly int MagicLength = G87AMagic.Length;

public FrameworkElement? Load(Stream stream)
{
if (!CheckGifAFormat(stream))
return null;

stream.Position = 0;

var memstr = new MemoryStream();
stream.CopyTo(memstr);

var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = memstr;
bitmap.EndInit();

var image = new Image();
ImageBehavior.SetRepeatBehavior(image, RepeatBehavior.Forever);
ImageBehavior.SetAnimatedSource(image, bitmap);

return image;
}

public bool CheckGifAFormat(Stream stream)
{
byte[] buffer = new byte[768];

if (stream.Read(buffer, 0, MagicLength) != MagicLength)
return false;

if (!SeqEq(buffer, G87AMagic, MagicLength)
&& !SeqEq(buffer, G89AMagic, MagicLength))
return false;

if (!TryReadUShortS(stream, buffer, out var width))
return false;

if (!TryReadUShortS(stream, buffer, out var height))
return false;

if (!TryReadByteS(stream, buffer, out var packed))
return false;

if (!TryReadByteS(stream, buffer, out var bgIndex))
return false;

stream.Position++;

var noTrailer = true;
while (noTrailer)
{

if (!TryReadByteS(stream, buffer, out var blockType))
return false;

switch ((int)blockType)
{
case 0: // Empty
break;

case 0x21: // EXTENSION
if (!TryReadByteS(stream, buffer, out var extType))
return false;

switch ((int)extType)
{
case 0xF9: //GRAPHICS_CONTROL
if (!TryReadBlock(stream, buffer, out var _))
return false;
break;

case 0xFF: //APPLICATION
if (!TryReadBlock(stream, buffer, out var blockLen))
return false;

if (blockLen < NetscapeMagic.Length)
return false;

if (SeqEq(buffer, NetscapeMagic, NetscapeMagic.Length))
{
var count = 0;

while (count > 0)
if (!TryReadBlock(stream, buffer, out count))
return false;
}
else if (!TryReadBlock(stream, buffer, out var _))
return false;

break;
}
break;


case 0x2C:// IMAGE_DESCRIPTOR

// frame bounds ( X, Y, W, H)
foreach (var _ in Enumerable.Range(0, 4))
if (!TryReadUShortS(stream, buffer, out var _))
return false;

if (!TryReadByteS(stream, buffer, out var descPack))
return false;

if ((descPack & 0x80) != 0)
{
var colorSize = 2 << (descPack & 7);
if (stream.Read(buffer, 0, colorSize * 3) < colorSize * 3)
return false;
}

if (!TryReadByteS(stream, buffer, out var _))
return false;

if (!TryReadBlock(stream, buffer, out var _))
return false;

break;

case 0x3B: // TRAILER
noTrailer = false;
break;

default:
if (!TryReadBlock(stream, buffer, out var _))
return false;

break;
}
}

var globalColorSz = 2 << (packed & 7);
if (stream.Read(buffer, 0, globalColorSz * 3) < globalColorSz * 3)
return false;

return true;
}

private static bool SeqEq(byte[] a, byte[] b, int len)
{
if (a.Length < len || b.Length < len)
return false;

for (int i = 0; i < len; ++i)
if (!a[i].Equals(b[i]))
return false;

return true;
}

private static bool TryReadUShortS(Stream stream, byte[] buffer, out ushort read)
{
if (stream.Read(buffer, 0, 2) < 2)
{
read = default;
return false;
}

read = (ushort)(buffer[0] | (buffer[1] << 8));
return true;
}

private static bool TryReadByteS(Stream stream, byte[] buffer, out byte read)
{
if (stream.Read(buffer, 0, 1) < 1)
{
read = default;
return false;
}

read = buffer[0];
return true;
}

private static bool TryReadBlock(Stream stream, byte[] buffer, out int blockSize)
{
if (!TryReadByteS(stream, buffer, out var len))
{
blockSize = -1;
return false;
}

blockSize = (int)len;
var readLen = stream.Read(buffer, 0, blockSize);

if (readLen < blockSize)
return false;

return true;
}
}
}
14 changes: 14 additions & 0 deletions MdXaml.AnimatedGif/AnimatedGifPluginSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@


using MdXaml.Plugins;

namespace MdXaml.AnimatedGif
{
public class AnimatedGifPluginSetup : IPluginSetup
{
public void Setup(MdXamlPlugins plugins)
{
plugins.ElementLoader.Add(new AnimatedGifLoader());
}
}
}
30 changes: 30 additions & 0 deletions MdXaml.AnimatedGif/MdXaml.AnimatedGif.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(PackageTargetFrameworks)</TargetFrameworks>
<PackageId>MdXaml.AniatedGif</PackageId>
<Version>$(PackageVersion)</Version>
<Authors>whistyun</Authors>
<Company />
<Description>Displays AniatedGif for MdXaml</Description>
<Copyright>whistyun 2023</Copyright>
<PackageProjectUrl>https://github.com/whistyun/MdXaml</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>MdXaml.Html.md</PackageReadmeFile>
<PackageTags>Markdown WPF Xaml FlowDocument</PackageTags>
<Configurations>Debug;Release</Configurations>

<UseWPF>true</UseWPF>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="WpfAnimatedGif" Version="2.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\MdXaml.Plugins\MdXaml.Plugins.csproj" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions MdXaml.Html/MdXaml.Html.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Version>$(PackageVersion)</Version>
<Authors>whistyun</Authors>
<Company />
<Description>Markdown XAML processor</Description>
<Description>cheap html processor for MdXalml</Description>
<Copyright>© Simon Baynes 2013; whistyun 2022</Copyright>
<PackageProjectUrl>https://github.com/whistyun/MdXaml</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -23,7 +23,7 @@
</PropertyGroup>

<ItemGroup>
<None Include="..\pack_readme\MdXaml.Html.md" Pack="true" PackagePath="\"/>
<None Include="..\pack_readme\MdXaml.Html.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
Expand Down
17 changes: 17 additions & 0 deletions MdXaml.Plugins/IElementLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace MdXaml.Plugins
{
public interface IElementLoader
{
public FrameworkElement? Load(Stream stream);
}
}
2 changes: 1 addition & 1 deletion MdXaml.Plugins/IMarkdown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface IMarkdown

public InlineUIContainer LoadImage(
string? tag, string urlTxt, string? tooltipTxt,
Action<InlineUIContainer, Image, ImageSource>? onSuccess = null);
Action<InlineUIContainer, Image?, ImageSource?>? onSuccess = null);

FlowDocument Transform(string text);

Expand Down
4 changes: 4 additions & 0 deletions MdXaml.Plugins/IPreferredLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace MdXaml.Plugins
{
public interface IPreferredLoader { }
}
10 changes: 7 additions & 3 deletions MdXaml.Plugins/MdXamlPlugins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ public class MdXamlPlugins
public ObservableCollection<IBlockParser> Block { get; }
public ObservableCollection<IInlineParser> Inline { get; }
public ObservableCollection<IImageLoader> ImageLoader { get; }
public ObservableCollection<IElementLoader> ElementLoader { get; }

public MdXamlPlugins() : this(new SyntaxManager())
{
}

public MdXamlPlugins(SyntaxManager manager) : this(manager, new(), new(), new(), new(), new())
public MdXamlPlugins(SyntaxManager manager) : this(manager, new(), new(), new(), new(), new(), new())
{
}

Expand All @@ -35,14 +36,16 @@ private MdXamlPlugins(
ObservableCollection<IBlockParser> topBlock,
ObservableCollection<IBlockParser> block,
ObservableCollection<IInlineParser> inline,
ObservableCollection<IImageLoader> imageLoader)
ObservableCollection<IImageLoader> imageLoader,
ObservableCollection<IElementLoader> elementLoader)
{
Syntax = manager;
Setups = setups;
TopBlock = topBlock;
Block = block;
Inline = inline;
ImageLoader = imageLoader;
ElementLoader = elementLoader;

Setups.CollectionChanged += Setups_CollectionChanged;
}
Expand All @@ -61,7 +64,8 @@ public MdXamlPlugins Clone()
new(TopBlock),
new(Block),
new(Inline),
new(ImageLoader));
new(ImageLoader),
new(ElementLoader));

}
}
6 changes: 6 additions & 0 deletions MdXaml.Plugins/SyntaxManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class SyntaxManager
EnableStrikethrough = false,
EnableListMarkerExt = false,
EnableTextileInline = false,
EnableImageResizeExt = false,
};
public static readonly SyntaxManager Standard = new()
{
Expand All @@ -26,6 +27,7 @@ public class SyntaxManager
EnableStrikethrough = true,
EnableListMarkerExt = false,
EnableTextileInline = false,
EnableImageResizeExt = false,
};
public static readonly SyntaxManager MdXaml = new();

Expand All @@ -39,6 +41,8 @@ public class SyntaxManager
public bool EnableListMarkerExt { set; get; } = true;
public bool EnableTextileInline { get; set; } = true;

public bool EnableImageResizeExt { get; set; } = true;

public void And(SyntaxManager manager)
{
EnableNoteBlock &= manager.EnableNoteBlock;
Expand All @@ -48,6 +52,7 @@ public void And(SyntaxManager manager)
EnableStrikethrough &= manager.EnableStrikethrough;
EnableListMarkerExt &= manager.EnableListMarkerExt;
EnableTextileInline &= manager.EnableTextileInline;
EnableImageResizeExt &= manager.EnableImageResizeExt;
}

public SyntaxManager Clone()
Expand All @@ -60,6 +65,7 @@ public SyntaxManager Clone()
EnableStrikethrough = EnableStrikethrough,
EnableListMarkerExt = EnableListMarkerExt,
EnableTextileInline = EnableTextileInline,
EnableImageResizeExt = EnableImageResizeExt,
};
}
}
Loading

0 comments on commit 3a7d87a

Please sign in to comment.