我想“手动”创建一个System.Drawing.Bitmap实例,其中包含一个动画.
要创建的Bitmap实例应满足以下条件:
>这是一个动画(image.FrameDimensionsLists具有一个时间维度)
>它具有多个帧(image.GetFrameCount(dimension)> 1)
>我可以获得帧之间的延迟(image.GetPropertyItem(0x5100).Value)
我很确定可以通过某些WinApi创建这样的图像.这也是GIF解码器实际上所做的.
我知道如果可以手动制作任何来源的帧,就可以播放动画,但是我想以兼容的方式来制作动画:如果我可以生成这样的位图,则可以在Button,Label上简单地使用它,PictureBox或任何其他现有控件,以及内置的ImageAnimator也可以自动处理它.
大多数类似的主题建议将帧转换为动画GIF.但是,这不是一个好的解决方案,因为它不能处理真实的颜色和半透明性(例如APNG动画).
更新:经过一番探索,我了解到我可以使用WIC来实现解码器.但是,我不想在Windows中注册新的解码器,而是使用COM,因此请尽量避免使用.更不用说最后我将拥有一个IWICBitmapSource,我仍然需要将其转换为位图.
更新2:我设置了赏金.如果可以实现以下方法,则您是赢家:
public void Bitmap CreateAnimation(Bitmap[] frames, int[] delays)
{
// Any WinApi is allowed. WIC is also allowed, but not preferred.
// Creating an animated GIF is not an acceptable answer. What if frames are from an APNG?
}
解决方法:
public void Bitmap CreateAnimation(Bitmap[] frames, int[] delays)
像这样对预期的实现设置严格的限制不是很明智.通过利用TIFF图像格式在技术上是可能的,它能够存储多个帧.但是,它们不是基于时间的,只有GIF编解码器支持.需要一个额外的参数,以便可以在需要渲染下一幅图像时更新控件.像这样:
public static Image CreateAnimation(Control ctl, Image[] frames, int[] delays) {
var ms = new System.IO.MemoryStream();
var codec = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff");
EncoderParameters encoderParameters = new EncoderParameters(2);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
encoderParameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)EncoderValue.CompressionLZW);
frames[0].Save(ms, codec, encoderParameters);
encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
for (int i = 1; i < frames.Length; i++) {
frames[0].SaveAdd(frames[i], encoderParameters);
}
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.Flush);
frames[0].SaveAdd(encoderParameters);
ms.Position = 0;
var img = Image.FromStream(ms);
Animate(ctl, img, delays);
return img;
}
Animate()方法需要一个Timer来选择下一帧并更新控件:
private static void Animate(Control ctl, Image img, int[] delays) {
int frame = 0;
var tmr = new Timer() { Interval = delays[0], Enabled = true };
tmr.Tick += delegate {
frame++;
if (frame >= delays.Length) frame = 0;
img.SelectActiveFrame(FrameDimension.Page, frame);
tmr.Interval = delays[frame];
ctl.Invalidate();
};
ctl.Disposed += delegate { tmr.Dispose(); };
}
用法示例:
public Form1() {
InitializeComponent();
pictureBox1.Image = CreateAnimation(pictureBox1,
new Image[] { Properties.Resources.Frame1, Properties.Resources.Frame2, Properties.Resources.Frame3 },
new int[] { 1000, 2000, 300 });
}
一种更明智的解决方法是完全降低返回值的要求,因此您不必生成TIFF.只需将Animate()方法与Action< Image>参数以获取控件的属性更新.但不是您要求的.