diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index da10061fb..0ae8bf28d 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
+ using System.Buffers;
using System.IO;
///
@@ -17,6 +18,11 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel
where TPacked : struct, IEquatable
{
+ ///
+ /// The temp buffer used to reduce allocations.
+ ///
+ private readonly byte[] buffer = new byte[16];
+
///
/// The image to decode the information to.
///
@@ -32,6 +38,11 @@ namespace ImageSharp.Formats
///
private byte[] globalColorTable;
+ ///
+ /// The global color table length
+ ///
+ private int globalColorTableLength;
+
///
/// The current frame.
///
@@ -54,55 +65,65 @@ namespace ImageSharp.Formats
/// The stream containing image data.
public void Decode(Image image, Stream stream)
{
- this.decodedImage = image;
-
- this.currentStream = stream;
-
- // Skip the identifier
- this.currentStream.Skip(6);
- this.ReadLogicalScreenDescriptor();
-
- if (this.logicalScreenDescriptor.GlobalColorTableFlag)
+ try
{
- this.globalColorTable = new byte[this.logicalScreenDescriptor.GlobalColorTableSize * 3];
+ this.decodedImage = image;
+ this.currentStream = stream;
- // Read the global color table from the stream
- stream.Read(this.globalColorTable, 0, this.globalColorTable.Length);
- }
+ // Skip the identifier
+ this.currentStream.Skip(6);
+ this.ReadLogicalScreenDescriptor();
- // Loop though the respective gif parts and read the data.
- int nextFlag = stream.ReadByte();
- while (nextFlag != GifConstants.Terminator)
- {
- if (nextFlag == GifConstants.ImageLabel)
+ if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
- this.ReadFrame();
+ this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
+ this.globalColorTable = ArrayPool.Shared.Rent(this.globalColorTableLength);
+
+ // Read the global color table from the stream
+ stream.Read(this.globalColorTable, 0, this.globalColorTableLength);
}
- else if (nextFlag == GifConstants.ExtensionIntroducer)
+
+ // Loop though the respective gif parts and read the data.
+ int nextFlag = stream.ReadByte();
+ while (nextFlag != GifConstants.Terminator)
{
- int label = stream.ReadByte();
- switch (label)
+ if (nextFlag == GifConstants.ImageLabel)
{
- case GifConstants.GraphicControlLabel:
- this.ReadGraphicalControlExtension();
- break;
- case GifConstants.CommentLabel:
- this.ReadComments();
- break;
- case GifConstants.ApplicationExtensionLabel:
- this.Skip(12); // No need to read.
- break;
- case GifConstants.PlainTextLabel:
- this.Skip(13); // Not supported by any known decoder.
- break;
+ this.ReadFrame();
}
+ else if (nextFlag == GifConstants.ExtensionIntroducer)
+ {
+ int label = stream.ReadByte();
+ switch (label)
+ {
+ case GifConstants.GraphicControlLabel:
+ this.ReadGraphicalControlExtension();
+ break;
+ case GifConstants.CommentLabel:
+ this.ReadComments();
+ break;
+ case GifConstants.ApplicationExtensionLabel:
+ this.Skip(12); // No need to read.
+ break;
+ case GifConstants.PlainTextLabel:
+ this.Skip(13); // Not supported by any known decoder.
+ break;
+ }
+ }
+ else if (nextFlag == GifConstants.EndIntroducer)
+ {
+ break;
+ }
+
+ nextFlag = stream.ReadByte();
}
- else if (nextFlag == GifConstants.EndIntroducer)
+ }
+ finally
+ {
+ if (this.globalColorTable != null)
{
- break;
+ ArrayPool.Shared.Return(this.globalColorTable);
}
-
- nextFlag = stream.ReadByte();
}
}
@@ -111,16 +132,14 @@ namespace ImageSharp.Formats
///
private void ReadGraphicalControlExtension()
{
- byte[] buffer = new byte[6];
-
- this.currentStream.Read(buffer, 0, buffer.Length);
+ this.currentStream.Read(this.buffer, 0, 6);
- byte packed = buffer[1];
+ byte packed = this.buffer[1];
this.graphicsControlExtension = new GifGraphicsControlExtension
{
- DelayTime = BitConverter.ToInt16(buffer, 2),
- TransparencyIndex = buffer[4],
+ DelayTime = BitConverter.ToInt16(this.buffer, 2),
+ TransparencyIndex = this.buffer[4],
TransparencyFlag = (packed & 0x01) == 1,
DisposalMethod = (DisposalMethod)((packed & 0x1C) >> 2)
};
@@ -132,18 +151,16 @@ namespace ImageSharp.Formats
///
private GifImageDescriptor ReadImageDescriptor()
{
- byte[] buffer = new byte[9];
+ this.currentStream.Read(this.buffer, 0, 9);
- this.currentStream.Read(buffer, 0, buffer.Length);
-
- byte packed = buffer[8];
+ byte packed = this.buffer[8];
GifImageDescriptor imageDescriptor = new GifImageDescriptor
{
- Left = BitConverter.ToInt16(buffer, 0),
- Top = BitConverter.ToInt16(buffer, 2),
- Width = BitConverter.ToInt16(buffer, 4),
- Height = BitConverter.ToInt16(buffer, 6),
+ Left = BitConverter.ToInt16(this.buffer, 0),
+ Top = BitConverter.ToInt16(this.buffer, 2),
+ Width = BitConverter.ToInt16(this.buffer, 4),
+ Height = BitConverter.ToInt16(this.buffer, 6),
LocalColorTableFlag = ((packed & 0x80) >> 7) == 1,
LocalColorTableSize = 2 << (packed & 0x07),
InterlaceFlag = ((packed & 0x40) >> 6) == 1
@@ -157,26 +174,23 @@ namespace ImageSharp.Formats
///
private void ReadLogicalScreenDescriptor()
{
- byte[] buffer = new byte[7];
-
- this.currentStream.Read(buffer, 0, buffer.Length);
+ this.currentStream.Read(this.buffer, 0, 7);
- byte packed = buffer[4];
+ byte packed = this.buffer[4];
this.logicalScreenDescriptor = new GifLogicalScreenDescriptor
{
- Width = BitConverter.ToInt16(buffer, 0),
- Height = BitConverter.ToInt16(buffer, 2),
- BackgroundColorIndex = buffer[5],
- PixelAspectRatio = buffer[6],
+ Width = BitConverter.ToInt16(this.buffer, 0),
+ Height = BitConverter.ToInt16(this.buffer, 2),
+ BackgroundColorIndex = this.buffer[5],
+ PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
GlobalColorTableSize = 2 << (packed & 0x07)
};
if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4)
{
- throw new ImageFormatException(
- $"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
+ throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
}
if (this.logicalScreenDescriptor.Width > this.decodedImage.MaxWidth || this.logicalScreenDescriptor.Height > this.decodedImage.MaxHeight)
@@ -216,11 +230,17 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Gif comment length '{flag}' exceeds max '{GifConstants.MaxCommentLength}'");
}
- byte[] buffer = new byte[flag];
-
- this.currentStream.Read(buffer, 0, flag);
+ byte[] flagBuffer = ArrayPool.Shared.Rent(flag);
- this.decodedImage.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(buffer)));
+ try
+ {
+ this.currentStream.Read(flagBuffer, 0, flag);
+ this.decodedImage.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(flagBuffer, 0, flag)));
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(flagBuffer);
+ }
}
}
@@ -231,18 +251,32 @@ namespace ImageSharp.Formats
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
- byte[] localColorTable = this.ReadFrameLocalColorTable(imageDescriptor);
-
- byte[] indices = this.ReadFrameIndices(imageDescriptor);
+ byte[] localColorTable = null;
- // Determine the color table for this frame. If there is a local one, use it
- // otherwise use the global color table.
- byte[] colorTable = localColorTable ?? this.globalColorTable;
+ try
+ {
+ // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
+ int length = this.globalColorTableLength;
+ if (imageDescriptor.LocalColorTableFlag)
+ {
+ length = imageDescriptor.LocalColorTableSize * 3;
+ localColorTable = ArrayPool.Shared.Rent(length);
+ this.currentStream.Read(localColorTable, 0, length);
+ }
- this.ReadFrameColors(indices, colorTable, imageDescriptor);
+ byte[] indices = this.ReadFrameIndices(imageDescriptor);
+ this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, length, imageDescriptor);
- // Skip any remaining blocks
- this.Skip(0);
+ // Skip any remaining blocks
+ this.Skip(0);
+ }
+ finally
+ {
+ if (localColorTable != null)
+ {
+ ArrayPool.Shared.Return(localColorTable);
+ }
+ }
}
///
@@ -259,32 +293,14 @@ namespace ImageSharp.Formats
}
}
- ///
- /// Reads the local color table from the current frame.
- ///
- /// The .
- /// The
- private byte[] ReadFrameLocalColorTable(GifImageDescriptor imageDescriptor)
- {
- byte[] localColorTable = null;
-
- if (imageDescriptor.LocalColorTableFlag)
- {
- localColorTable = new byte[imageDescriptor.LocalColorTableSize * 3];
-
- this.currentStream.Read(localColorTable, 0, localColorTable.Length);
- }
-
- return localColorTable;
- }
-
///
/// Reads the frames colors, mapping indices to colors.
///
/// The indexed pixels.
/// The color table containing the available colors.
+ /// The color table length.
/// The
- private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor)
+ private void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor)
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
@@ -301,7 +317,12 @@ namespace ImageSharp.Formats
{
lastFrame = new TColor[imageWidth * imageHeight];
- Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
+ // Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
+ using (PixelAccessor lastPixels = lastFrame.Lock(imageWidth, imageHeight))
+ using (PixelAccessor currentPixels = this.currentFrame.Lock(imageWidth, imageHeight))
+ {
+ currentPixels.CopyImage(lastPixels);
+ }
}
int offset, i = 0;
@@ -368,7 +389,12 @@ namespace ImageSharp.Formats
TColor[] pixels = new TColor[imageWidth * imageHeight];
- Array.Copy(this.currentFrame, pixels, pixels.Length);
+ // Array.Copy(this.currentFrame, pixels, pixels.Length);
+ using (PixelAccessor newPixels = pixels.Lock(imageWidth, imageHeight))
+ using (PixelAccessor currentPixels = this.currentFrame.Lock(imageWidth, imageHeight))
+ {
+ currentPixels.CopyImage(newPixels);
+ }
ImageBase currentImage;
@@ -376,7 +402,7 @@ namespace ImageSharp.Formats
{
currentImage = this.decodedImage;
currentImage.SetPixels(imageWidth, imageHeight, pixels);
- currentImage.Quality = colorTable.Length / 3;
+ currentImage.Quality = colorTableLength / 3;
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
@@ -389,7 +415,7 @@ namespace ImageSharp.Formats
currentImage = frame;
currentImage.SetPixels(imageWidth, imageHeight, pixels);
- currentImage.Quality = colorTable.Length / 3;
+ currentImage.Quality = colorTableLength / 3;
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
@@ -421,4 +447,4 @@ namespace ImageSharp.Formats
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index d5b0fccd6..b968256b7 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -19,7 +19,7 @@ namespace ImageSharp.Formats
internal sealed class GifEncoderCore
{
///
- /// The pixel buffer, used to reduce allocations.
+ /// The temp buffer used to reduce allocations.
///
private readonly byte[] buffer = new byte[16];