diff --git a/src/ImageProcessor/Common/Helpers/Utils.cs b/src/ImageProcessor/Common/Helpers/Utils.cs deleted file mode 100644 index c56129d3e..000000000 --- a/src/ImageProcessor/Common/Helpers/Utils.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// -// General utility methods. -// TODO: I don't like having classes like this as they turn into a dumping ground. Investigate method usage. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace ImageProcessor -{ - /// - /// General utility methods. - /// TODO: I don't like having classes like this as they turn into a dumping ground. Investigate method usage. - /// - internal static class Utils - { - /// - /// Swaps two references. - /// - /// The type of the references to swap. - /// The first reference. - /// The second reference. - public static void Swap(ref TRef lhs, ref TRef rhs) where TRef : class - { - TRef tmp = lhs; - - lhs = rhs; - rhs = tmp; - } - } -} diff --git a/src/ImageProcessor/Formats/Gif/BitEncoder.cs b/src/ImageProcessor/Formats/Gif/BitEncoder.cs index 0ae595098..faf37108a 100644 --- a/src/ImageProcessor/Formats/Gif/BitEncoder.cs +++ b/src/ImageProcessor/Formats/Gif/BitEncoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Handles the encoding of bits for compression. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -18,29 +13,19 @@ namespace ImageProcessor.Formats internal class BitEncoder { /// - /// The current working bit. - /// - private int currentBit; - - /// - /// The current value. - /// - private int currentValue; - - /// - /// The inner list for collecting the bits. + /// The inner list for collecting the bits. /// private readonly List list = new List(); /// - /// The number of bytes in the encoder. + /// The current working bit. /// - internal int Length => this.list.Count; + private int currentBit; /// - /// Gets or sets the intitial bit. + /// The current value. /// - public int IntitialBit { get; set; } + private int currentValue; /// /// Initializes a new instance of the class. @@ -53,6 +38,16 @@ namespace ImageProcessor.Formats this.IntitialBit = initial; } + /// + /// Gets or sets the intitial bit. + /// + public int IntitialBit { get; set; } + + /// + /// The number of bytes in the encoder. + /// + public int Length => this.list.Count; + /// /// Adds the current byte to the end of the encoder. /// @@ -78,7 +73,7 @@ namespace ImageProcessor.Formats /// Adds the collection of bytes to the end of the encoder. /// /// - /// The collection of bytes to add. + /// The collection of bytes to add. /// The collection itself cannot be null but can contain elements that are null. public void AddRange(byte[] collection) { diff --git a/src/ImageProcessor/Formats/Gif/DisposalMethod.cs b/src/ImageProcessor/Formats/Gif/DisposalMethod.cs index 2ce4a0523..055435f93 100644 --- a/src/ImageProcessor/Formats/Gif/DisposalMethod.cs +++ b/src/ImageProcessor/Formats/Gif/DisposalMethod.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Provides enumeration for instructing the decoder what to do with the last image -// in an animation sequence. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/GifConstants.cs b/src/ImageProcessor/Formats/Gif/GifConstants.cs index eebbc5479..bae71dd2b 100644 --- a/src/ImageProcessor/Formats/Gif/GifConstants.cs +++ b/src/ImageProcessor/Formats/Gif/GifConstants.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Constants that define specific points within a gif. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -30,11 +25,6 @@ namespace ImageProcessor.Formats /// public const byte ExtensionIntroducer = 0x21; - /// - /// The end introducer trailer ;. - /// - public const byte EndIntroducer = 0x3B; - /// /// The graphic control label. /// @@ -85,5 +75,9 @@ namespace ImageProcessor.Formats /// public const byte Terminator = 0; + /// + /// The end introducer trailer ;. + /// + public const byte EndIntroducer = 0x3B; } } diff --git a/src/ImageProcessor/Formats/Gif/GifDecoder.cs b/src/ImageProcessor/Formats/Gif/GifDecoder.cs index d4c33fd2b..9757137d2 100644 --- a/src/ImageProcessor/Formats/Gif/GifDecoder.cs +++ b/src/ImageProcessor/Formats/Gif/GifDecoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encoder for generating an image out of a gif encoded stream. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -14,7 +9,7 @@ namespace ImageProcessor.Formats using System.IO; /// - /// Encoder for generating an image out of a gif encoded stream. + /// Decoder for generating an image out of a gif encoded stream. /// public class GifDecoder : IImageDecoder { @@ -24,9 +19,6 @@ namespace ImageProcessor.Formats /// The size of the header. public int HeaderSize => 6; - internal GifDecoderCore CoreDecoder { get; private set; } - - /// /// Returns a value indicating whether the supports the specified /// file header. @@ -69,8 +61,7 @@ namespace ImageProcessor.Formats /// The containing image data. public void Decode(Image image, Stream stream) { - this.CoreDecoder = new GifDecoderCore(); - this.CoreDecoder.Decode(image, stream); + new GifDecoderCore().Decode(image, stream); } } } diff --git a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs index 3ae9e2628..41d9a3773 100644 --- a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs @@ -1,39 +1,56 @@ -namespace ImageProcessor.Formats +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { using System; using System.IO; + /// + /// Performs the gif decoding operation. + /// internal class GifDecoderCore { /// - /// The maximum comment length. + /// The image to decode the information to. + /// + private Image decodedImage; + + /// + /// The currently loaded stream. /// - private const int MaxCommentLength = 1024 * 8; - - private const byte ExtensionIntroducer = 0x21; - private const byte Terminator = 0; - private const byte ImageLabel = 0x2C; - private const byte EndIntroducer = 0x3B; - private const byte ApplicationExtensionLabel = 0xFF; - private const byte CommentLabel = 0xFE; - private const byte ImageDescriptorLabel = 0x2C; - private const byte PlainTextLabel = 0x01; - private const byte GraphicControlLabel = 0xF9; - - private Image image; private Stream currentStream; + + /// + /// The global color table. + /// private byte[] globalColorTable; - private byte[] currentFrame; - internal GifLogicalScreenDescriptor LogicalScreenDescriptor { get; set; } + /// + /// The current frame. + /// + private byte[] currentFrame; - internal GifGraphicsControlExtension GraphicsControlExtension { get; set; } + /// + /// The logical screen descriptor. + /// + private GifLogicalScreenDescriptor logicalScreenDescriptor; - internal byte Quality { get; set; } + /// + /// The graphics control extension. + /// + private GifGraphicsControlExtension graphicsControlExtension; + /// + /// Decodes the stream to the image. + /// + /// The image to decode to. + /// The stream containing image data. public void Decode(Image image, Stream stream) { - this.image = image; + this.decodedImage = image; this.currentStream = stream; @@ -41,42 +58,42 @@ this.currentStream.Seek(6, SeekOrigin.Current); this.ReadLogicalScreenDescriptor(); - if (this.LogicalScreenDescriptor.GlobalColorTableFlag) + if (this.logicalScreenDescriptor.GlobalColorTableFlag) { - this.globalColorTable = new byte[this.LogicalScreenDescriptor.GlobalColorTableSize * 3]; + this.globalColorTable = new byte[this.logicalScreenDescriptor.GlobalColorTableSize * 3]; // Read the global color table from the stream stream.Read(this.globalColorTable, 0, this.globalColorTable.Length); } + // Loop though the respective gif parts and read the data. int nextFlag = stream.ReadByte(); - while (nextFlag != Terminator) + while (nextFlag != GifConstants.Terminator) { - if (nextFlag == ImageLabel) + if (nextFlag == GifConstants.ImageLabel) { this.ReadFrame(); } - else if (nextFlag == ExtensionIntroducer) + else if (nextFlag == GifConstants.ExtensionIntroducer) { int label = stream.ReadByte(); switch (label) { - case GraphicControlLabel: + case GifConstants.GraphicControlLabel: this.ReadGraphicalControlExtension(); break; - case CommentLabel: + case GifConstants.CommentLabel: this.ReadComments(); break; - case ApplicationExtensionLabel: - // TODO: Read Application extension - this.Skip(12); + case GifConstants.ApplicationExtensionLabel: + this.Skip(12); // No need to read. break; - case PlainTextLabel: - this.Skip(13); + case GifConstants.PlainTextLabel: + this.Skip(13); // Not supported by any known decoder. break; } } - else if (nextFlag == EndIntroducer) + else if (nextFlag == GifConstants.EndIntroducer) { break; } @@ -85,6 +102,9 @@ } } + /// + /// Reads the graphic control extension. + /// private void ReadGraphicalControlExtension() { byte[] buffer = new byte[6]; @@ -93,7 +113,7 @@ byte packed = buffer[1]; - this.GraphicsControlExtension = new GifGraphicsControlExtension + this.graphicsControlExtension = new GifGraphicsControlExtension { DelayTime = BitConverter.ToInt16(buffer, 2), TransparencyIndex = buffer[4], @@ -102,12 +122,10 @@ }; } - private void ReadApplicationBlockExtension() - { - // TODO: Implement - throw new NotImplementedException(); - } - + /// + /// Reads the image descriptor + /// + /// private GifImageDescriptor ReadImageDescriptor() { byte[] buffer = new byte[9]; @@ -130,6 +148,9 @@ return imageDescriptor; } + /// + /// Reads the logical screen descriptor. + /// private void ReadLogicalScreenDescriptor() { byte[] buffer = new byte[7]; @@ -138,7 +159,7 @@ byte packed = buffer[4]; - this.LogicalScreenDescriptor = new GifLogicalScreenDescriptor + this.logicalScreenDescriptor = new GifLogicalScreenDescriptor { Width = BitConverter.ToInt16(buffer, 0), Height = BitConverter.ToInt16(buffer, 2), @@ -148,19 +169,23 @@ GlobalColorTableSize = 2 << (packed & 0x07) }; - if (this.LogicalScreenDescriptor.GlobalColorTableSize > 255 * 4) + if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4) { throw new ImageFormatException( - $"Invalid gif colormap size '{this.LogicalScreenDescriptor.GlobalColorTableSize}'"); + $"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); } - if (this.LogicalScreenDescriptor.Width > ImageBase.MaxWidth || this.LogicalScreenDescriptor.Height > ImageBase.MaxHeight) + if (this.logicalScreenDescriptor.Width > ImageBase.MaxWidth || this.logicalScreenDescriptor.Height > ImageBase.MaxHeight) { throw new ArgumentOutOfRangeException( - $"The input gif '{this.LogicalScreenDescriptor.Width}x{this.LogicalScreenDescriptor.Height}' is bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); + $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); } } + /// + /// Skips the designated number of bytes in the stream. + /// + /// The number of bytes to skip. private void Skip(int length) { this.currentStream.Seek(length, SeekOrigin.Current); @@ -173,25 +198,31 @@ } } + /// + /// Reads the gif comments. + /// private void ReadComments() { int flag; while ((flag = this.currentStream.ReadByte()) != 0) { - if (flag > MaxCommentLength) + if (flag > GifConstants.MaxCommentLength) { - throw new ImageFormatException($"Gif comment length '{flag}' exceeds max '{MaxCommentLength}'"); + throw new ImageFormatException($"Gif comment length '{flag}' exceeds max '{GifConstants.MaxCommentLength}'"); } byte[] buffer = new byte[flag]; this.currentStream.Read(buffer, 0, flag); - this.image.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(buffer))); + this.decodedImage.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(buffer))); } } + /// + /// Reads an individual gif frame. + /// private void ReadFrame() { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); @@ -210,6 +241,11 @@ this.Skip(0); } + /// + /// Reads the frame indices marking the color to use for each pixel. + /// + /// The . + /// The private byte[] ReadFrameIndices(GifImageDescriptor imageDescriptor) { int dataSize = this.currentStream.ReadByte(); @@ -220,6 +256,11 @@ return indices; } + /// + /// Reads the local color table from the current frame. + /// + /// The . + /// The private byte[] ReadFrameLocalColorTable(GifImageDescriptor imageDescriptor) { byte[] localColorTable = null; @@ -234,10 +275,16 @@ return localColorTable; } + /// + /// Reads the frames colors, mapping indices to colors. + /// + /// The indexed pixels. + /// The color table containing the available colors. + /// The private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) { - int imageWidth = this.LogicalScreenDescriptor.Width; - int imageHeight = this.LogicalScreenDescriptor.Height; + int imageWidth = this.logicalScreenDescriptor.Width; + int imageHeight = this.logicalScreenDescriptor.Height; if (this.currentFrame == null) { @@ -246,51 +293,50 @@ byte[] lastFrame = null; - if (this.GraphicsControlExtension != null && - this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) + if (this.graphicsControlExtension != null && + this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { lastFrame = new byte[imageWidth * imageHeight * 4]; Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); } - int offset = 0, i = 0, index = -1; - - int iPass = 0; // the interlace pass - int iInc = 8; // the interlacing line increment - int iY = 0; // the current interlaced line - int writeY = 0; // the target y offset to write to + int offset, 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++) { // Check if this image is interlaced. + int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // 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 (iY >= descriptor.Height) + if (interlaceY >= descriptor.Height) { - iPass++; - switch (iPass) + interlacePass++; + switch (interlacePass) { case 1: - iY = 4; + interlaceY = 4; break; case 2: - iY = 2; - iInc = 4; + interlaceY = 2; + interlaceIncrement = 4; break; case 3: - iY = 1; - iInc = 2; + interlaceY = 1; + interlaceIncrement = 2; break; } } - writeY = iY + descriptor.Top; + writeY = interlaceY + descriptor.Top; - iY += iInc; + interlaceY += interlaceIncrement; } else { @@ -299,18 +345,18 @@ for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { - offset = (writeY * imageWidth) + x; - - index = indices[i]; + offset = ((writeY * imageWidth) + x) * 4; + int index = indices[i]; - if (this.GraphicsControlExtension == null || - this.GraphicsControlExtension.TransparencyFlag == false || - this.GraphicsControlExtension.TransparencyIndex != index) + if (this.graphicsControlExtension == null || + this.graphicsControlExtension.TransparencyFlag == false || + this.graphicsControlExtension.TransparencyIndex != index) { - this.currentFrame[offset * 4 + 0] = colorTable[index * 3 + 2]; - this.currentFrame[offset * 4 + 1] = colorTable[index * 3 + 1]; - this.currentFrame[offset * 4 + 2] = colorTable[index * 3 + 0]; - this.currentFrame[offset * 4 + 3] = (byte)255; + int indexOffset = index * 3; + this.currentFrame[offset + 0] = colorTable[indexOffset + 2]; + this.currentFrame[offset + 1] = colorTable[indexOffset + 1]; + this.currentFrame[offset + 2] = colorTable[indexOffset + 0]; + this.currentFrame[offset + 3] = 255; } i++; @@ -323,15 +369,15 @@ ImageBase currentImage; - if (this.image.Pixels == null) + if (this.decodedImage.Pixels == null) { - currentImage = this.image; + currentImage = this.decodedImage; currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.Quality = colorTable.Length / 3; - if (this.GraphicsControlExtension != null && this.GraphicsControlExtension.DelayTime > 0) + if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { - this.image.FrameDelay = this.GraphicsControlExtension.DelayTime; + this.decodedImage.FrameDelay = this.graphicsControlExtension.DelayTime; } } else @@ -342,32 +388,32 @@ currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.Quality = colorTable.Length / 3; - if (this.GraphicsControlExtension != null && this.GraphicsControlExtension.DelayTime > 0) + if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { - currentImage.FrameDelay = this.GraphicsControlExtension.DelayTime; + currentImage.FrameDelay = this.graphicsControlExtension.DelayTime; } - this.image.Frames.Add(frame); + this.decodedImage.Frames.Add(frame); } - if (this.GraphicsControlExtension != null) + if (this.graphicsControlExtension != null) { - if (this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) + if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { - offset = (y * imageWidth) + x; + offset = ((y * imageWidth) + x) * 4; - this.currentFrame[offset * 4 + 0] = 0; - this.currentFrame[offset * 4 + 1] = 0; - this.currentFrame[offset * 4 + 2] = 0; - this.currentFrame[offset * 4 + 3] = 0; + this.currentFrame[offset + 0] = 0; + this.currentFrame[offset + 1] = 0; + this.currentFrame[offset + 2] = 0; + this.currentFrame[offset + 3] = 0; } } } - else if (this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) + else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { this.currentFrame = lastFrame; } diff --git a/src/ImageProcessor/Formats/Gif/GifEncoder.cs b/src/ImageProcessor/Formats/Gif/GifEncoder.cs index 46bb8dd31..1870b1151 100644 --- a/src/ImageProcessor/Formats/Gif/GifEncoder.cs +++ b/src/ImageProcessor/Formats/Gif/GifEncoder.cs @@ -10,7 +10,7 @@ namespace ImageProcessor.Formats using System.Linq; /// - /// The Gif encoder + /// Image encoder for writing image data to a stream in gif format. /// public class GifEncoder : IImageEncoder { @@ -63,12 +63,12 @@ namespace ImageProcessor.Formats quality = quality > 0 ? quality.Clamp(1, 256) : 256; // Get the number of bits. - int bitdepth = this.GetBitsNeededForColorDepth(quality); + int bitDepth = this.GetBitsNeededForColorDepth(quality); // Write the LSD and check to see if we need a global color table. // Always true just now. - bool globalColor = this.WriteGlobalLogicalScreenDescriptor(image, stream, bitdepth); - QuantizedImage quantized = this.WriteColorTable(imageBase, stream, quality, bitdepth); + bool globalColor = this.WriteGlobalLogicalScreenDescriptor(image, stream, bitDepth); + QuantizedImage quantized = this.WriteColorTable(imageBase, stream, quality, bitDepth); this.WriteGraphicalControlExtension(imageBase, stream); this.WriteImageDescriptor(quantized, quality, stream); @@ -238,13 +238,13 @@ namespace ImageProcessor.Formats this.WriteShort(stream, image.Height); // Calculate the quality. - int bitdepth = this.GetBitsNeededForColorDepth(quality); + int bitDepth = this.GetBitsNeededForColorDepth(quality); // No LCT use GCT. this.WriteByte(stream, 0); - // Write the image data.. - this.WriteImageData(image, stream, bitdepth); + // Write the image data. + this.WriteImageData(image, stream, bitDepth); } /// @@ -264,19 +264,19 @@ namespace ImageProcessor.Formats // Calculate the quality. int quality = this.Quality > 0 ? this.Quality : image.Quality; quality = quality > 0 ? quality.Clamp(1, 256) : 256; - int bitdepth = this.GetBitsNeededForColorDepth(quality); + int bitDepth = this.GetBitsNeededForColorDepth(quality); int packed = 0x80 | // 1: Local color table flag = 1 (LCT used) 0x00 | // 2: Interlace flag 0 0x00 | // 3: Sort flag 0 0 | // 4-5: Reserved - bitdepth - 1; + bitDepth - 1; this.WriteByte(stream, packed); // Now immediately follow with the color table. - QuantizedImage quantized = this.WriteColorTable(image, stream, quality, bitdepth); - this.WriteImageData(quantized, stream, bitdepth); + QuantizedImage quantized = this.WriteColorTable(image, stream, quality, bitDepth); + this.WriteImageData(quantized, stream, bitDepth); } /// diff --git a/src/ImageProcessor/Formats/Gif/LzwDecoder.cs b/src/ImageProcessor/Formats/Gif/LzwDecoder.cs index 4d6cfd4fb..ce28d36d4 100644 --- a/src/ImageProcessor/Formats/Gif/LzwDecoder.cs +++ b/src/ImageProcessor/Formats/Gif/LzwDecoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Decompresses data using the LZW algorithms. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -19,9 +14,9 @@ namespace ImageProcessor.Formats internal sealed class LzwDecoder { /// - /// The stack size. + /// One more than the maximum value 12 bit integer. /// - private const int StackSize = 4096; + private const int MaxStackSize = 4096; /// /// The null code. @@ -37,18 +32,19 @@ namespace ImageProcessor.Formats /// Initializes a new instance of the class /// and sets the stream, where the compressed data should be read from. /// - /// The stream. where to read from. - /// is null - /// (Nothing in Visual Basic). + /// The stream to read from. + /// is null. public LzwDecoder(Stream stream) { - Guard.NotNull(stream, "stream"); + Guard.NotNull(stream, nameof(stream)); this.stream = stream; } /// /// Decodes and decompresses all pixel indices from the stream. + /// + /// /// /// The width of the pixel index array. /// The height of the pixel index array. @@ -72,7 +68,7 @@ namespace ImageProcessor.Formats // Calculate the available code. int availableCode = clearCode + 2; - #region Jillzhangs Code (Not From Me) see: http://giflib.codeplex.com/ + // Jillzhangs Code (Not From Me) see: http://giflib.codeplex.com/ // TODO: It's imperative that this close is cleaned up and commented properly. // TODO: Unfortunately I can't figure out the character encoding to translate from the original Chinese. int code; // ÓÃÓÚ´æ´¢µ±Ç°µÄ±àÂëÖµ @@ -80,10 +76,9 @@ namespace ImageProcessor.Formats int codeMask = (1 << codeSize) - 1; // ±íʾ±àÂëµÄ×î´óÖµ£¬Èç¹ûcodeSize=5,Ôòcode_mask=31 int bits = 0; // ÔÚ±àÂëÁ÷ÖÐÊý¾ÝµÄ±£´æÐÎʽΪbyte£¬¶øÊµ¼Ê±àÂë¹ý³ÌÖÐÊÇÕÒʵ¼Ê±àÂëλÀ´´æ´¢µÄ£¬±ÈÈçµ±codeSize=5µÄʱºò£¬ÄÇôʵ¼ÊÉÏ5bitµÄÊý¾Ý¾ÍÓ¦¸Ã¿ÉÒÔ±íʾһ¸ö±àÂ룬ÕâÑùÈ¡³öÀ´µÄ1¸ö×ֽھ͸»ÓàÁË3¸öbit£¬Õâ3¸öbitÓÃÓں͵ڶþ¸ö×ֽڵĺóÁ½¸öbit½øÐÐ×éºÏ£¬ÔÙ´ÎÐγɱàÂëÖµ£¬Èç´ËÀàÍÆ - - int[] prefix = new int[StackSize]; // ÓÃÓÚ±£´æÇ°×ºµÄ¼¯ºÏ - int[] suffix = new int[StackSize]; // ÓÃÓÚ±£´æºó׺ - int[] pixelStatck = new int[StackSize + 1]; // ÓÃÓÚÁÙʱ±£´æÊý¾ÝÁ÷ + int[] prefix = new int[MaxStackSize]; // ÓÃÓÚ±£´æÇ°×ºµÄ¼¯ºÏ + int[] suffix = new int[MaxStackSize]; // ÓÃÓÚ±£´æºó׺ + int[] pixelStatck = new int[MaxStackSize + 1]; // ÓÃÓÚÁÙʱ±£´æÊý¾ÝÁ÷ int top = 0; int count = 0; // ÔÚÏÂÃæµÄÑ­»·ÖУ¬Ã¿´Î»á»ñȡһ¶¨Á¿µÄ±àÂëµÄ×Ö½ÚÊý×飬¶ø´¦ÀíÕâЩÊý×éµÄʱºòÐèÒª1¸ö¸ö×Ö½ÚÀ´´¦Àí£¬count¾ÍÊDZíʾ»¹Òª´¦ÀíµÄ×Ö½ÚÊýÄ¿ @@ -92,7 +87,6 @@ namespace ImageProcessor.Formats int data = 0; // ±íʾµ±Ç°´¦ÀíµÄÊý¾ÝµÄÖµ int first = 0; // Ò»¸ö×Ö·û´®ÖصĵÚÒ»¸ö×Ö½Ú - int inCode; // ÔÚlzwÖУ¬Èç¹ûÈÏʶÁËÒ»¸ö±àÂëËù´ú±íµÄÊý¾Ýentry£¬Ôò½«±àÂë×÷ΪÏÂÒ»´ÎµÄprefix£¬´Ë´¦inCode´ú±í´«µÝ¸øÏÂÒ»´Î×÷Ϊǰ׺µÄ±àÂëÖµ // ÏÈÉú³ÉÔªÊý¾ÝµÄǰ׺¼¯ºÏºÍºó׺¼¯ºÏ£¬ÔªÊý¾ÝµÄǰ׺¾ùΪ0£¬¶øºó׺ÓëÔªÊý¾ÝÏàµÈ£¬Í¬Ê±±àÂëÒ²ÓëÔªÊý¾ÝÏàµÈ for (code = 0; code < clearCode; code++) @@ -129,8 +123,12 @@ namespace ImageProcessor.Formats } // »ñÈ¡±¾´ÎÒª´¦ÀíµÄÊý¾ÝµÄÖµ - data += buffer[bi] << bits; // ´Ë´¦ÎªºÎÒªÒÆÎ»ÄØ£¬±ÈÈçµÚÒ»´Î´¦ÀíÁË1¸ö×Ö½ÚΪ176£¬µÚÒ»´ÎÖ»Òª´¦Àí5bit¾Í¹»ÁË£¬Ê£ÏÂ3bitÁô¸øÏ¸ö×Ö½Ú½øÐÐ×éºÏ¡£Ò²¾ÍÊǵڶþ¸ö×ֽڵĺóÁ½Î»+µÚÒ»¸ö×Ö½ÚµÄǰÈýλ×é³ÉµÚ¶þ´ÎÊä³öÖµ - bits += 8; // ±¾´ÎÓÖ´¦ÀíÁËÒ»¸ö×Ö½Ú£¬ËùÒÔÐèÒª+8 + if (buffer != null) + { + data += buffer[bi] << bits; // ´Ë´¦ÎªºÎÒªÒÆÎ»ÄØ£¬±ÈÈçµÚÒ»´Î´¦ÀíÁË1¸ö×Ö½ÚΪ176£¬µÚÒ»´ÎÖ»Òª´¦Àí5bit¾Í¹»ÁË£¬Ê£ÏÂ3bitÁô¸øÏ¸ö×Ö½Ú½øÐÐ×éºÏ¡£Ò²¾ÍÊǵڶþ¸ö×ֽڵĺóÁ½Î»+µÚÒ»¸ö×Ö½ÚµÄǰÈýλ×é³ÉµÚ¶þ´ÎÊä³öÖµ + } + + bits += 8; // ±¾´ÎÓÖ´¦ÀíÁËÒ»¸ö×Ö½Ú£¬ËùÒÔÐèÒª+8 bi++; // ½«´¦ÀíÏÂÒ»¸ö×Ö½Ú count--; // ÒѾ­´¦Àí¹ýµÄ×Ö½ÚÊý+1 continue; @@ -145,7 +143,7 @@ namespace ImageProcessor.Formats // ÏÂÃæ¸ù¾Ý»ñÈ¡µÄcodeÖµÀ´½øÐд¦Àí if (code > availableCode || code == endCode) { - // µ±±àÂëÖµ´óÓÚ×î´ó±àÂëÖµ»òÕßΪ½áÊø±ê¼ÇµÄʱºò£¬Í£Ö¹´¦Àí + // µ±±àÂëÖµ´óÓÚ×î´ó±àÂëÖµ»òÕßΪ½áÊø±ê¼ÇµÄʱºò£¬Í£Ö¹´¦Àí break; } @@ -179,7 +177,7 @@ namespace ImageProcessor.Formats continue; } - inCode = code; + int inCode = code; // ÔÚlzwÖУ¬Èç¹ûÈÏʶÁËÒ»¸ö±àÂëËù´ú±íµÄÊý¾Ýentry£¬Ôò½«±àÂë×÷ΪÏÂÒ»´ÎµÄprefix£¬´Ë´¦inCode´ú±í´«µÝ¸øÏÂÒ»´Î×÷Ϊǰ׺µÄ±àÂëÖµ if (code == availableCode) { // Èç¹ûµ±Ç°±àÂëºÍ±¾´ÎÓ¦¸ÃÉú³ÉµÄ±àÂëÏàͬ @@ -201,8 +199,9 @@ namespace ImageProcessor.Formats // »ñÈ¡ÏÂÒ»¸öÊý¾Ý pixelStatck[top++] = suffix[code]; - // Fix for Gifs that have "deferred clear code" as per here : https://bugzilla.mozilla.org/show_bug.cgi?id=55918 - if (availableCode < StackSize) + // Fix for Gifs that have "deferred clear code" as per here : + // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 + if (availableCode < MaxStackSize) { // ÉèÖõ±Ç°Ó¦¸Ã±àÂëλÖõÄǰ׺ prefix[availableCode] = oldCode; @@ -212,7 +211,7 @@ namespace ImageProcessor.Formats // Ï´ÎÓ¦¸ÃµÃµ½µÄ±àÂëÖµ availableCode++; - if (availableCode == codeMask + 1 && availableCode < StackSize) + if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { // Ôö¼Ó±àÂëλÊý codeSize++; @@ -229,12 +228,10 @@ namespace ImageProcessor.Formats // »ØËݵ½ÉÏÒ»¸ö´¦ÀíλÖà top--; - // »ñȡԪÊý¾Ý + // »ñȡԪÊý¾Ý pixels[xyz++] = (byte)pixelStatck[top]; } - #endregion - return pixels; } diff --git a/src/ImageProcessor/Formats/Gif/LzwEncoder.cs b/src/ImageProcessor/Formats/Gif/LzwEncoder.cs index e4e8b08d5..27f33389c 100644 --- a/src/ImageProcessor/Formats/Gif/LzwEncoder.cs +++ b/src/ImageProcessor/Formats/Gif/LzwEncoder.cs @@ -1,16 +1,11 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encodes an image pixels used on a method based on LZW compression. -// -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { + using System; using System.Collections.Generic; using System.IO; @@ -52,8 +47,15 @@ namespace ImageProcessor.Formats this.initDataSize = this.colorDepth; } + /// + /// Encodes the compressed indexed pixel data to the given stream. + /// + /// The stream to add the data to. + /// is null. public void Encode(Stream stream) { + Guard.NotNull(stream, nameof(stream)); + // Whether it is a first step. bool first = true; diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs index face314ae..26f4f8bf4 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Provides methods for allowing quantization of images pixels. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs index 1cf697589..bdf9cf767 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encapsulates methods to calculate the color palette of an image using an Octree pattern. -// -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs index 6dc5dec03..e2ed9273d 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Provides methods for allowing quantization of images pixels. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs index 9efe62ec3..cfbd23223 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encapsulates methods to calculate the color palette of an image. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/README.md b/src/ImageProcessor/Formats/Gif/README.md new file mode 100644 index 000000000..d47a4c683 --- /dev/null +++ b/src/ImageProcessor/Formats/Gif/README.md @@ -0,0 +1,4 @@ +Encoder/Decoder adapted and extended from: + +https://github.com/yufeih/Nine.Imaging/ +https://imagetools.codeplex.com/ diff --git a/src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs b/src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs index 7b7bfc153..006d8e291 100644 --- a/src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs +++ b/src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// The Graphic Control Extension contains parameters used when -// processing a graphic rendering block. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs b/src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs index cdd768250..199424893 100644 --- a/src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs +++ b/src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs @@ -1,15 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Each image in the Data Stream is composed of an Image Descriptor, -// an optional Local Color Table, and the image data. -// Each image must fit within the boundaries of the -// Logical Screen, as defined in the Logical Screen Descriptor. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { diff --git a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs index e1a75c825..aa0f37990 100644 --- a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs +++ b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs @@ -57,38 +57,22 @@ namespace ImageProcessor.Formats { ColorTypes.Add( 0, - new PngColorTypeInformation( - 1, - new[] { 1, 2, 4, 8 }, - (p, a) => new GrayscaleReader(false))); + new PngColorTypeInformation(1, new[] { 1, 2, 4, 8 }, (p, a) => new GrayscaleReader(false))); ColorTypes.Add( 2, - new PngColorTypeInformation( - 3, - new[] { 8 }, - (p, a) => new TrueColorReader(false))); + new PngColorTypeInformation(3, new[] { 8 }, (p, a) => new TrueColorReader(false))); ColorTypes.Add( 3, - new PngColorTypeInformation( - 1, - new[] { 1, 2, 4, 8 }, - (p, a) => new PaletteIndexReader(p, a))); + new PngColorTypeInformation(1, new[] { 1, 2, 4, 8 }, (p, a) => new PaletteIndexReader(p, a))); ColorTypes.Add( 4, - new PngColorTypeInformation( - 2, - new[] { 8 }, - (p, a) => new GrayscaleReader(true))); + new PngColorTypeInformation(2, new[] { 8 }, (p, a) => new GrayscaleReader(true))); - ColorTypes.Add( - 6, - new PngColorTypeInformation( - 4, - new[] { 8 }, - (p, a) => new TrueColorReader(true))); + ColorTypes.Add(6, + new PngColorTypeInformation(4, new[] { 8 }, (p, a) => new TrueColorReader(true))); } /// @@ -334,7 +318,7 @@ namespace ImageProcessor.Formats colorReader.ReadScanline(currentScanline, pixels, this.header); column = -1; - Utils.Swap(ref currentScanline, ref lastScanline); + this.Swap(ref currentScanline, ref lastScanline); } } } @@ -544,5 +528,20 @@ namespace ImageProcessor.Formats return numBytes; } + + /// + /// Swaps two references. + /// + /// The type of the references to swap. + /// The first reference. + /// The second reference. + private void Swap(ref TRef lhs, ref TRef rhs) + where TRef : class + { + TRef tmp = lhs; + + lhs = rhs; + rhs = tmp; + } } } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 066022679..4fc9b385e 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -45,7 +45,6 @@ - @@ -176,6 +175,7 @@ +