К сожалению, мы можем расширить ни System.Darwing.Image
, ни System.Darwing.Bitmap
так главенствующими image.FrameDimensionsLists
и подобные члены не может быть и речи, и, как Hans Passant упоминалось, ни один из датчиков изображения Windows, не поддерживают измерение времени само по себе. Однако я считаю, в данном конкретном случае на основе
Я знаю, что я могу играть анимацию, если у меня есть кадры из любого источника, делая это вручную, но я хочу, чтобы это сделать совместимым способом: Если Я мог бы создать такой Bitmap, я мог бы просто использовать его на кнопке, Ярлык, PictureBox или любой другой существующий элемент управления, а встроенный ImageAnimator также мог бы обрабатывать его автоматически.
Мы можем уйти от реализации нового класса, который может обрабатывать анимация, а затем неявно брось в Bitmap, мы можем использовать методы расширения для автоматического включения анимации для управления, я знаю, что этот метод является Hacky, но я, хотя, возможно, это стоит упомянуть об этом.
здесь грубое внедрение и использование образца
AnimatedBitmap: ручки кадров и анимации на основе базового времени на предоставленных последовательностей:
public class Sequence
{
public Image Image { get; set; }
public int Delay { get; set; }
}
public class AnimatedBitmap:IDisposable
{
private readonly Bitmap _buffer;
private readonly Graphics _g;
private readonly Sequence[] _sequences;
private readonly CancellationTokenSource _cancelToken;
public event EventHandler FrameUpdated;
protected void OnFrameUpdated()
{
if (FrameUpdated != null)
FrameUpdated(this, EventArgs.Empty);
}
public AnimatedBitmap(int width, int height, params Sequence[] sequences)
{
_buffer = new Bitmap(width, height, PixelFormat.Format32bppArgb) {Tag = this};
_sequences = sequences;
_g=Graphics.FromImage(_buffer);
_g.CompositingMode=CompositingMode.SourceCopy;
_cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(Animate
, TaskCreationOptions.LongRunning
, _cancelToken.Token);
}
private void Animate(object obj)
{
while (!_cancelToken.IsCancellationRequested)
foreach (var sequence in _sequences)
{
if (_cancelToken.IsCancellationRequested)
break;
_g.Clear(Color.Transparent);
_g.DrawImageUnscaled(sequence.Image,0,0);
_g.Flush(FlushIntention.Flush);
OnFrameUpdated();
Thread.Sleep(sequence.Delay);
}
_g.Dispose();
_buffer.Dispose();
}
public AnimatedBitmap(params Sequence[] sequences)
: this(sequences.Max(s => s.Image.Width), sequences.Max(s => s.Image.Height), sequences)
{
}
public void Dispose()
{
_cancelToken.Cancel();
}
public static implicit operator Bitmap(AnimatedBitmap animatedBitmap)
{
return animatedBitmap._buffer;
}
public static explicit operator AnimatedBitmap(Bitmap bitmap)
{
var tag = bitmap.Tag as AnimatedBitmap;
if (tag != null)
return tag;
throw new InvalidCastException();
}
public static AnimatedBitmap CreateAnimation(Image[] frames, int[] delays)
{
var sequences = frames.Select((t, i) => new Sequence {Image = t, Delay = delays[i]}).ToArray();
var animated=new AnimatedBitmap(sequences);
return animated;
}
}
AnimationController: ручки обновления управления анимацией
public static class AnimationController
{
private static readonly List<Control> Controls =new List<Control>();
private static CancellationTokenSource _cancelToken;
static AnimationController()
{
_cancelToken = new CancellationTokenSource();
_cancelToken.Cancel();
}
private static void Animate(object arg)
{
while (!_cancelToken.IsCancellationRequested)
{
Controls.RemoveAll(c => !(c.BackgroundImage.Tag is AnimatedBitmap));
foreach (var c in Controls)
{
var control = c;
if (!control.Disposing)
control.Invoke(new Action(() => control.Refresh()));
}
Thread.Sleep(40);
}
}
public static void StartAnimation(this Control control)
{
if (_cancelToken.IsCancellationRequested)
{
_cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(Animate
, TaskCreationOptions.LongRunning
, _cancelToken.Token);
}
Controls.Add(control);
control.Disposed += Disposed;
}
private static void Disposed(object sender, EventArgs e)
{
(sender as Control).StopAnimation();
}
public static void StopAnimation(this Control control)
{
Controls.Remove(control);
if(Controls.Count==0)
_cancelToken.Cancel();
}
public static void SetAnimatedBackground(this Control control, AnimatedBitmap bitmap)
{
control.BackgroundImage = bitmap;
control.StartAnimation();
}
}
и здесь образец Использование:
public Form1()
{
InitializeComponent();
var frame1 = Image.FromFile(@"1.png");
var frame2 = Image.FromFile(@"2.png");
var animatedBitmap= new AnimatedBitmap(
new Sequence {Image = frame1, Delay = 33},
new Sequence {Image = frame2, Delay = 33}
);
// or we can do
//animatedBitmap = AnimatedBitmap.CreateAnimation(new[] {frame1, frame2}, new[] {1000, 2000});
pictureBox1.SetAnimatedBackground(animatedBitmap);
button1.SetAnimatedBackground(animatedBitmap);
label1.SetAnimatedBackground(animatedBitmap);
checkBox1.SetAnimatedBackground(animatedBitmap);
//or we can do
//pictureBox1.BackgroundImage = animatedBitmap;
//pictureBox1.StartAnimation();
}
Благодарим вас за ответ. Как я уже сказал, я могу программно имитировать любую анимацию, и это то, что я делаю, например, когда я извлекаю кадры из анимации APNG. Тем не менее, GIF-декодер способен создать один битмап, который содержит всю необходимую информацию для анимации изображения, и я также хочу как-то добиться этого. Здесь вы делаете многостраничное изображение TIFF с помощью TIFF-кодировщика (и декодера при загрузке обратно из потока), что приводит к тому, что экземпляр Bitmap имеет размер страницы вместо Time. Таким образом, встроенный ImageAnimator не будет воспроизводить анимацию на кнопке, например. – taffer
Ну, да, это все совершенно очевидно. Ни один из кодировщиков изображений Windows не поддерживает измерение времени, это означает, что доллар останавливается. Вам нужен этот метод Animate(). Хорошо работает, я рекомендую вам его использовать. –
Спасибо, я уже внедрил подобный аниматор. Но ключ - это совместимость здесь (и любопытство, конечно). Я знаю, что среди встроенных декодеров GIF-декодер является единственным, который использует эту функцию Bitmap и создает битмап с критериями, указанными в вопросе. Я мог бы переформулировать вопрос следующим образом: как это делает GIF-декодер и как я могу достичь того же результата независимо от исходного формата? – taffer