WPFのImageでアニメーション
こうすれば出来ますが、トリッキーかも知れません。
for(int i=0; i < bitmapFrames.Length;i++)
{
DiscreteObjectKeyFrame key = new DiscreteObjectKeyFrame();
key.KeyTime = new TimeSpan(time);
key.Value = bitmapFrames[i];
animation.KeyFrames.Add(key);
time += delay * span;
}
animation.RepeatBehavior = RepeatBehavior.Forever;
animation.Duration = new TimeSpan(time);
image.BeginAnimation(Image.SourceProperty, animation);
ImageでアニメーションさせるにはBeginAnimationを使えばできたりします。その際、Image.SourcePropertyを使って良いのかよく分かりません。
アニメーションを消したいときは、
image.BeginAnimation(Image.SourceProperty, null);
で出来ます。これで、動画と静止画を行き来する事が出来るかと。
DPIの罠
Imageにそのまま貼り付けるとイメージのDPIをみて変換してしまうので、WFPだけではDPIが取得出来ません。ここの架空の関数は、96.0しか返しません(実装方法はいくつかあります)
scaleX = bmp.DpiX / Util.GetDpiX();
scaleY = bmp.DpiY / Util.GetDpiY();
image.RenderTransform = new ScaleTransform(scaleX, scaleY);
GIFアニメーションの再生
BitmapDecorderから読み出したアニメーションGIFのアニメする為に必要なデータ、フレームごとにGetQueryで取得します。
取得出来る情報:
BitmapSource fbmp = bitmapFrames[i];
int transpearentColor = 0;
BitmapMetadata metadata = fbmp.Metadata as BitmapMetadata;
bool transpearent = false, hasLocalPallet = false;
int delay = 0,startX=0,startY=0,w=imgWidth,h=imgHeight;
if (metadata != null)
{
try
{
startX = (UInt16)metadata.GetQuery("/imgdesc/Left");
startY = (UInt16)metadata.GetQuery("/imgdesc/Top");
w = (UInt16)metadata.GetQuery("/imgdesc/Width");
h = (UInt16)metadata.GetQuery("/imgdesc/Height");
delay = (UInt16)metadata.GetQuery("/grctlext/Delay") * 10;
transpearent = (Boolean)metadata.GetQuery("/grctlext/TransparencyFlag");
if (transpearent) transpearentColor = (Byte)metadata.GetQuery("/grctlext/TransparentColorIndex");
hasLocalPalette = (Boolean)metadata.GetQuery("/imgdesc/LocalColorTableFlag");
}
catch
{
LogWritter.write("Query Error");
//no data
}
}
差分や透過GIFの実装
ここが一番ヤバイ。
フレームを重ねるだけのGIFアニメならそのまま書き出すだけですが、フレームが矩形や透過データの場合、自力で合成後のBitmapを用意する必要があります。
Pixel Bufferを用意して、Indexed8で取得し、そのままアクセスするのが楽です。1pixel = 1byteなので計算が楽。
if (bmp.Format != PixelFormats.Indexed8)
{
bmp = new FormatConvertedBitmap(bmp,PixelFormats.Indexed8,null,0);
bmp.Freeze();
}
byte[] buf = new byte[imgWidth * imgHeight];
bmp.CopyPixels(buf, imgWidth, 0);
wbmp.WritePixels(new Int32Rect(0, 0, imgWidth, imgHeight), buf, imgWidth, 0);
FrameBitmapで返すPixelWidthとPixelHeightは親と同じですが、実際に入っているのは矩形データです。
実際の大きさはメタタグを読まないと分からないので割と面倒になっています(以下バグだらけなので一度削除)