Browse Source

Optimized the GIF decoder.

af/merge-core
dirk 10 years ago
committed by Dirk Lemstra
parent
commit
b27a85ad5f
  1. 215
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs

215
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -33,9 +33,14 @@ namespace ImageSharp.Formats
private byte[] globalColorTable; private byte[] globalColorTable;
/// <summary> /// <summary>
/// The current frame. /// The next frame.
/// </summary> /// </summary>
private TColor[] currentFrame; private ImageBase<TColor, TPacked> nextFrame;
/// <summary>
/// The area to restore.
/// </summary>
private Rectangle? restoreArea;
/// <summary> /// <summary>
/// The logical screen descriptor. /// The logical screen descriptor.
@ -285,141 +290,165 @@ namespace ImageSharp.Formats
/// <param name="indices">The indexed pixels.</param> /// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor)
{ {
int imageWidth = this.logicalScreenDescriptor.Width; int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.logicalScreenDescriptor.Height;
if (this.currentFrame == null) ImageBase<TColor, TPacked> previousFrame = null;
ImageBase<TColor, TPacked> currentFrame;
if (this.nextFrame == null)
{ {
this.currentFrame = new TColor[imageWidth * imageHeight]; currentFrame = this.decodedImage;
}
TColor[] lastFrame = null; currentFrame.Quality = colorTable.Length / 3;
if (this.graphicsControlExtension != null && // This initializes the image to become fully transparent because the alpha channel is zero.
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) currentFrame.InitPixels(imageWidth, imageHeight);
}
else
{ {
lastFrame = new TColor[imageWidth * imageHeight]; if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
previousFrame = this.nextFrame;
}
ImageFrame<TColor, TPacked> frame = this.nextFrame.ToFrame();
currentFrame = frame;
RestoreToBackground(currentFrame);
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); this.decodedImage.Frames.Add(frame);
}
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
currentFrame.FrameDelay = this.graphicsControlExtension.DelayTime;
} }
int offset, i = 0; int i = 0;
int interlacePass = 0; // The interlace pass int interlacePass = 0; // The interlace pass
int interlaceIncrement = 8; // The interlacing line increment int interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line int interlaceY = 0; // The current interlaced line
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) using (PixelAccessor<TColor, TPacked> pixelAccessor = currentFrame.Lock())
{ {
// Check if this image is interlaced. using (PixelRow<TColor, TPacked> pixelRow = new PixelRow<TColor, TPacked>(imageWidth, ComponentOrder.XYZW))
int writeY; // the target y offset to write to
if (descriptor.InterlaceFlag)
{ {
// If so then we read lines at predetermined offsets. for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
// When an entire image height worth of offset lines has been read we consider this a pass.
// With each pass the number of offset lines changes and the starting line changes.
if (interlaceY >= descriptor.Height)
{ {
interlacePass++; // Check if this image is interlaced.
switch (interlacePass) int writeY; // the target y offset to write to
if (descriptor.InterlaceFlag)
{ {
case 1: // If so then we read lines at predetermined offsets.
interlaceY = 4; // When an entire image height worth of offset lines has been read we consider this a pass.
break; // With each pass the number of offset lines changes and the starting line changes.
case 2: if (interlaceY >= descriptor.Height)
interlaceY = 2; {
interlaceIncrement = 4; interlacePass++;
break; switch (interlacePass)
case 3: {
interlaceY = 1; case 1:
interlaceIncrement = 2; interlaceY = 4;
break; break;
case 2:
interlaceY = 2;
interlaceIncrement = 4;
break;
case 3:
interlaceY = 1;
interlaceIncrement = 2;
break;
}
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
{
writeY = y;
} }
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement; pixelRow.Reset();
}
else
{
writeY = y;
}
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) byte* pixelBase = pixelRow.PixelBase;
{ for (int x = 0; x < descriptor.Width; x++)
offset = (writeY * imageWidth) + x; {
int index = indices[i]; int index = indices[i];
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
int indexOffset = index * 3;
*(pixelBase + 0) = colorTable[indexOffset];
*(pixelBase + 1) = colorTable[indexOffset + 1];
*(pixelBase + 2) = colorTable[indexOffset + 2];
*(pixelBase + 3) = 255;
}
i++;
pixelBase += 4;
}
if (this.graphicsControlExtension == null || pixelAccessor.CopyFrom(pixelRow, writeY, descriptor.Left);
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
// Stored in r-> g-> b-> a order.
int indexOffset = index * 3;
TColor pixel = default(TColor);
pixel.PackFromVector4(new Color(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2]).ToVector4());
this.currentFrame[offset] = pixel;
} }
i++;
} }
} }
TColor[] pixels = new TColor[imageWidth * imageHeight]; if (previousFrame != null)
{
Array.Copy(this.currentFrame, pixels, pixels.Length); this.nextFrame = previousFrame;
return;
}
ImageBase<TColor, TPacked> currentImage; this.nextFrame = currentFrame;
if (this.decodedImage.Pixels == null) if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
{ {
currentImage = this.decodedImage; this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
currentImage.SetPixels(imageWidth, imageHeight, pixels);
currentImage.Quality = colorTable.Length / 3;
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
this.decodedImage.FrameDelay = this.graphicsControlExtension.DelayTime;
}
} }
else }
{
ImageFrame<TColor, TPacked> frame = new ImageFrame<TColor, TPacked>();
currentImage = frame; private void RestoreToBackground(ImageBase<TColor, TPacked> frame)
currentImage.SetPixels(imageWidth, imageHeight, pixels); {
currentImage.Quality = colorTable.Length / 3; if (this.restoreArea == null)
{
return;
}
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) // Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.decodedImage.Width &&
this.restoreArea.Value.Height == this.decodedImage.Height)
{
using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock())
{ {
currentImage.FrameDelay = this.graphicsControlExtension.DelayTime; pixelAccessor.Reset();
} }
this.decodedImage.Frames.Add(frame);
} }
else
if (this.graphicsControlExtension != null)
{ {
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) using (PixelRow<TColor, TPacked> emptyRow = new PixelRow<TColor, TPacked>(this.restoreArea.Value.Width, ComponentOrder.XYZW))
{ {
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock())
{ {
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
{ {
offset = (y * imageWidth) + x; pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
// Stored in r-> g-> b-> a order.
this.currentFrame[offset] = default(TColor);
} }
} }
} }
else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
this.currentFrame = lastFrame;
}
} }
this.restoreArea = null;
} }
} }
} }

Loading…
Cancel
Save