Изменение размера анимированного GIF с dotimage
У меня есть кусок кода, который изменяет размеры анимационных картинок. если это поможет, код всегда будет изменять размеры изображений до меньшего размера. (пока нет необходимости делать их больше)
Я использую библиотеку Atalasoft dotimage и их пример кода для фактической повторной выборки. Код должен читать анимированный GIF-диск с диска, перебирать кадры и изменять размер каждого кадра до нового размера. Это прекрасно работает, когда анимация gif содержит кадры одинакового размера, но изменение размера анимации с кадрами разного размера нарушает анимацию (кадры не корректно перекрывают друг друга после изменения размера), я думаю, это потому, что код не вычисляет новые смещения правильно.
Я думаю, что эта строка кода не вычисляет правильные смещения: точка точка = новая точка ((int)(frame.Location.X * ratio), (int)(frame.Location.Y * ratio));
Вот полная процедура изменения размера:
static private void GenerateGifImage(FileStream fileStream, int OutputWidth, int OutputHeight)
{
// MemoryStream InputStream = new MemoryStream();
FileStream InputStream = fileStream;
// fileStream.Write(InputStream.GetBuffer(), 0, (int)InputStream.Position);
// InputStream.Seek(0, SeekOrigin.Begin);
Image InputImage = Image.FromStream(InputStream, true, false);
// this will invalidate the underlying image object in InputImage but the class properties
// will still accessible until the object is disposed
InputStream.Seek(0, SeekOrigin.Begin);
ImageInfo imageInfo = RegisteredDecoders.GetImageInfo(InputStream);
InputStream.Seek(0, SeekOrigin.Begin);
GifDecoder gifDecoder = new GifDecoder();
int count = gifDecoder.GetFrameCount(InputStream);
GifFrameCollection gifFrameCollection = new GifFrameCollection();
gifFrameCollection.Height = OutputHeight;
gifFrameCollection.Width = OutputWidth;
// gifFrameCollection.Height = gifDecoder.Frames.Height;
// gifFrameCollection.Width = gifDecoder.Frames.Width;
double ratio;
if (InputImage.Height > InputImage.Width)
{
ratio = (double)OutputHeight / (double)InputImage.Height;
}
else
{
ratio = (double)OutputWidth / (double)InputImage.Width;
}
for (int i = 0; i < count; i++)
{
GifFrame frame = gifDecoder.Frames[i];
Rectangle rectangle = new Rectangle(Point.Empty, frame.Image.Size);
int frameWidth = (int)(frame.Image.Width * ratio);
int frameHeight = (int)(frame.Image.Height * ratio);
// account for erratic rounding, seems illogical but has happened earlier when using floats instead of doubles
if (frameWidth > OutputWidth)
{
frameWidth = OutputWidth;
}
if (frameHeight > OutputHeight)
{
frameHeight = OutputHeight;
}
Size size = new Size(frameWidth, frameHeight);
// only resize if we have a measureable dimension
if (size.Width > 0 && size.Height > 0)
{
// ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.NearestNeighbor);
ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.NearestNeighbor);
AtalaImage atalaImage = resampleCommand.Apply(frame.Image).Image;
// save the image for debugging
// atalaImage.Save("frame" + i.ToString() + ".gif", ImageType.Gif, null);
// frame.Image.Save("frame-orig" + i.ToString() + ".gif", ImageType.Gif, null);
// AtalaImage atalaImage = frame.Image;
Point point = new Point((int)(frame.Location.X * ratio), (int)(frame.Location.Y * ratio));
// Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
gifFrameCollection.Add(new GifFrame(atalaImage, point, frame.DelayTime, frame.Interlaced, frame.FrameDisposal, frame.TransparentIndex, frame.UseLocalPalette));
}
}
FileStream saveStream = new FileStream("resized.gif", FileMode.Create, FileAccess.Write, FileShare.Write);
GifEncoder gifSave = new GifEncoder();
gifSave.Save(saveStream, gifFrameCollection, null);
saveStream.Close();
}
2 ответа
Я работаю в Atalasoft
Я посмотрел на это - ваш код абсолютно прав и будет отлично работать с кадрами неравного размера. Точка, которую вы рассчитываете, верна.
Проблема состоит в том, что в вашем 3-кадровом GIF ваш второй кадр и третий кадр точно сделаны так, чтобы они накладывались поверх первого и используют очень сложную прозрачную маску, чтобы показать первый кадр через них. Когда ваше изображение обновляется до нового размера, маска может быть не точной - так как вы изменяете размеры до разницы в ширину и высоту всего в один пиксель, эта маска никак не может сравниться.
Есть несколько решений этой проблемы
- Наложите кадр 2 на кадр 1, затем измените выборку и используйте вместо этого изображение
- Сделайте #1, но затем извлеките прямоугольник кадра 2
- Используйте обрезку вместо повторной выборки - это кажется лучшим, так как это всего 1 пиксель.
Я кодировал #3 для вас - это работает хорошо
static private void GenerateGifImage(FileStream fileStream, int OutputWidth, int OutputHeight)
{
// MemoryStream InputStream = new MemoryStream();
FileStream InputStream = fileStream;
// fileStream.Write(InputStream.GetBuffer(), 0, (int)InputStream.Position);
// InputStream.Seek(0, SeekOrigin.Begin);
Image InputImage = Image.FromStream(InputStream, true, false);
// this will invalidate the underlying image object in InputImage but the class properties
// will still accessible until the object is disposed
InputStream.Seek(0, SeekOrigin.Begin);
ImageInfo imageInfo = RegisteredDecoders.GetImageInfo(InputStream);
InputStream.Seek(0, SeekOrigin.Begin);
GifDecoder gifDecoder = new GifDecoder();
int count = gifDecoder.GetFrameCount(InputStream);
GifFrameCollection gifFrameCollection = new GifFrameCollection();
gifFrameCollection.Height = OutputHeight;
gifFrameCollection.Width = OutputWidth;
double ratio;
if (InputImage.Height > InputImage.Width)
{
ratio = (double)OutputHeight / (double)InputImage.Height;
}
else
{
ratio = (double)OutputWidth / (double)InputImage.Width;
}
for (int i = 0; i < count; i++)
{
GifFrame frame = gifDecoder.Frames[i];
Rectangle rectangle = new Rectangle(Point.Empty, frame.Image.Size);
int newframeWidth = frame.Image.Width;
int newframeHeight = frame.Image.Height;
if (newframeWidth > OutputWidth || newframeHeight > OutputHeight)
{
newframeWidth = (int)(frame.Image.Width * ratio);
newframeHeight = (int)(frame.Image.Height * ratio);
}
// account for erratic rounding, seems illogical but has happened earlier when using floats instead of doubles
if (newframeWidth > OutputWidth)
{
newframeWidth = OutputWidth;
}
if (newframeHeight > OutputHeight)
{
newframeHeight = OutputHeight;
}
Size size = new Size(newframeWidth, newframeHeight);
// only resize if we have a measureable dimension
if (size.Width > 0 && size.Height > 0)
{
//ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.);
AtalaImage atalaImage = frame.Image;
if (newframeWidth != frame.Image.Width || newframeHeight != frame.Image.Height)
{
CropCommand command = new CropCommand(new Rectangle(new Point(0, 0), size));
atalaImage = command.Apply(frame.Image).Image;
}
// AtalaImage atalaImage = frame.Image;
Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
// Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
gifFrameCollection.Add(new GifFrame(atalaImage, point, frame.DelayTime, frame.Interlaced, frame.FrameDisposal, frame.TransparentIndex, frame.UseLocalPalette));
}
}
FileStream saveStream = new FileStream("resized.gif", FileMode.Create, FileAccess.Write, FileShare.Write);
GifEncoder gifSave = new GifEncoder();
gifSave.Save(saveStream, gifFrameCollection, null);
saveStream.Close();
}
Рассчитанные значения отношения неверны, если вы работаете с разными размерами кадра. Вы должны рассчитать соотношение для каждого отдельного кадра, чтобы линия, в которой вы заинтересованы, использовала правильное соотношение. Я не знаком с фреймворком, поэтому не могу предоставить вам точный пример; но это должно выглядеть примерно так:
GifFrame frame = gifDecoder.Frames[i];
double frameRatio;
if (frame.Height > frame.Width)
{
frameRatio = (double)OutputHeight / (double)frame.Height;
}
else
{
frameRatio = (double)OutputWidth / (double)frame.Width;
}
...
Point point = new Point((int)(frame.Location.X * frameRatio), (int)(frame.Location.Y * frameRatio));