diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 44bb5553b..784d05da2 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -33,9 +33,14 @@ namespace ImageSharp.Formats
private byte[] globalColorTable;
///
- /// The current frame.
+ /// The next frame.
///
- private TColor[] currentFrame;
+ private ImageBase nextFrame;
+
+ ///
+ /// The area to restore.
+ ///
+ private Rectangle? restoreArea;
///
/// The logical screen descriptor.
@@ -285,141 +290,165 @@ namespace ImageSharp.Formats
/// The indexed pixels.
/// The color table containing the available colors.
/// The
- 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 imageHeight = this.logicalScreenDescriptor.Height;
- if (this.currentFrame == null)
+ ImageBase previousFrame = null;
+
+ ImageBase 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.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
+ // This initializes the image to become fully transparent because the alpha channel is zero.
+ currentFrame.InitPixels(imageWidth, imageHeight);
+ }
+ else
{
- lastFrame = new TColor[imageWidth * imageHeight];
+ if (this.graphicsControlExtension != null &&
+ this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
+ {
+ previousFrame = this.nextFrame;
+ }
+
+ ImageFrame 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 interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line
- for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
+ using (PixelAccessor pixelAccessor = currentFrame.Lock())
{
- // Check if this image is interlaced.
- int writeY; // the target y offset to write to
- if (descriptor.InterlaceFlag)
+ using (PixelRow pixelRow = new PixelRow(imageWidth, ComponentOrder.XYZW))
{
- // If so then we read lines at predetermined offsets.
- // 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)
+ for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
{
- interlacePass++;
- switch (interlacePass)
+ // Check if this image is interlaced.
+ int writeY; // the target y offset to write to
+ if (descriptor.InterlaceFlag)
{
- case 1:
- interlaceY = 4;
- break;
- case 2:
- interlaceY = 2;
- interlaceIncrement = 4;
- break;
- case 3:
- interlaceY = 1;
- interlaceIncrement = 2;
- break;
+ // If so then we read lines at predetermined offsets.
+ // 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++;
+ switch (interlacePass)
+ {
+ case 1:
+ interlaceY = 4;
+ 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;
- }
- else
- {
- writeY = y;
- }
+ pixelRow.Reset();
- for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
- {
- offset = (writeY * imageWidth) + x;
- int index = indices[i];
+ byte* pixelBase = pixelRow.PixelBase;
+ for (int x = 0; x < descriptor.Width; x++)
+ {
+ 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 ||
- 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;
+ pixelAccessor.CopyFrom(pixelRow, writeY, descriptor.Left);
}
-
- i++;
}
}
- TColor[] pixels = new TColor[imageWidth * imageHeight];
-
- Array.Copy(this.currentFrame, pixels, pixels.Length);
+ if (previousFrame != null)
+ {
+ this.nextFrame = previousFrame;
+ return;
+ }
- ImageBase currentImage;
+ this.nextFrame = currentFrame;
- if (this.decodedImage.Pixels == null)
+ if (this.graphicsControlExtension != null &&
+ this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
{
- currentImage = this.decodedImage;
- currentImage.SetPixels(imageWidth, imageHeight, pixels);
- currentImage.Quality = colorTable.Length / 3;
-
- if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
- {
- this.decodedImage.FrameDelay = this.graphicsControlExtension.DelayTime;
- }
+ this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
}
- else
- {
- ImageFrame frame = new ImageFrame();
+ }
- currentImage = frame;
- currentImage.SetPixels(imageWidth, imageHeight, pixels);
- currentImage.Quality = colorTable.Length / 3;
+ private void RestoreToBackground(ImageBase frame)
+ {
+ 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 pixelAccessor = frame.Lock())
{
- currentImage.FrameDelay = this.graphicsControlExtension.DelayTime;
+ pixelAccessor.Reset();
}
-
- this.decodedImage.Frames.Add(frame);
}
-
- if (this.graphicsControlExtension != null)
+ else
{
- if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
+ using (PixelRow emptyRow = new PixelRow(this.restoreArea.Value.Width, ComponentOrder.XYZW))
{
- for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
+ using (PixelAccessor 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;
-
- // Stored in r-> g-> b-> a order.
- this.currentFrame[offset] = default(TColor);
+ pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
}
}
}
- else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
- {
- this.currentFrame = lastFrame;
- }
}
+
+ this.restoreArea = null;
}
}
}