Gyö*_*zeg 5 c# animation drawing gdi+ winforms
我想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,如果可能,我想避免使用 COM。更不用说最后我会有一个IWICBitmapSource,我仍然需要将其转换为Bitmap
.
更新 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?
}
Run Code Online (Sandbox Code Playgroud)
public void Bitmap CreateAnimation(Bitmap[] frames, int[] delays)
Run Code Online (Sandbox Code Playgroud)
像这样对预期的实现设置严格的限制并不是很明智。利用 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;
}
Run Code Online (Sandbox Code Playgroud)
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(); };
}
Run Code Online (Sandbox Code Playgroud)
示例用法:
public Form1() {
InitializeComponent();
pictureBox1.Image = CreateAnimation(pictureBox1,
new Image[] { Properties.Resources.Frame1, Properties.Resources.Frame2, Properties.Resources.Frame3 },
new int[] { 1000, 2000, 300 });
}
Run Code Online (Sandbox Code Playgroud)
一个更聪明的方法是完全放弃返回值要求,这样你就不必生成 TIFF。只需使用带有Action<Image>
参数的 Animate() 方法即可更新控件的属性。但不是你所要求的。