Browse Source

Cleanup.

Former-commit-id: 24c3536a9f7e9e6e09b2aba5dc20613deb5480c6
Former-commit-id: 74ae512bde698c21b490426a0df6ecb7029f4f0d
Former-commit-id: 25ae344fe041dfdb12ec44a58f844248adcc25cd
pull/17/head
James Jackson-South 11 years ago
parent
commit
7e7fd78624
  1. 34
      src/ImageProcessor/Common/Helpers/Utils.cs
  2. 43
      src/ImageProcessor/Formats/Gif/BitEncoder.cs
  3. 12
      src/ImageProcessor/Formats/Gif/DisposalMethod.cs
  4. 20
      src/ImageProcessor/Formats/Gif/GifConstants.cs
  5. 19
      src/ImageProcessor/Formats/Gif/GifDecoder.cs
  6. 232
      src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
  7. 22
      src/ImageProcessor/Formats/Gif/GifEncoder.cs
  8. 57
      src/ImageProcessor/Formats/Gif/LzwDecoder.cs
  9. 20
      src/ImageProcessor/Formats/Gif/LzwEncoder.cs
  10. 11
      src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs
  11. 12
      src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
  12. 11
      src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
  13. 11
      src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
  14. 4
      src/ImageProcessor/Formats/Gif/README.md
  15. 12
      src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs
  16. 14
      src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs
  17. 45
      src/ImageProcessor/Formats/Png/PngDecoderCore.cs
  18. 2
      src/ImageProcessor/ImageProcessor.csproj

34
src/ImageProcessor/Common/Helpers/Utils.cs

@ -1,34 +0,0 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Utils.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// General utility methods.
// TODO: I don't like having classes like this as they turn into a dumping ground. Investigate method usage.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor
{
/// <summary>
/// General utility methods.
/// TODO: I don't like having classes like this as they turn into a dumping ground. Investigate method usage.
/// </summary>
internal static class Utils
{
/// <summary>
/// Swaps two references.
/// </summary>
/// <typeparam name="TRef">The type of the references to swap.</typeparam>
/// <param name="lhs">The first reference.</param>
/// <param name="rhs">The second reference.</param>
public static void Swap<TRef>(ref TRef lhs, ref TRef rhs) where TRef : class
{
TRef tmp = lhs;
lhs = rhs;
rhs = tmp;
}
}
}

43
src/ImageProcessor/Formats/Gif/BitEncoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BitEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="BitEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Handles the encoding of bits for compression.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -18,29 +13,19 @@ namespace ImageProcessor.Formats
internal class BitEncoder
{
/// <summary>
/// The current working bit.
/// </summary>
private int currentBit;
/// <summary>
/// The current value.
/// </summary>
private int currentValue;
/// <summary>
/// The inner list for collecting the bits.
/// The inner list for collecting the bits.
/// </summary>
private readonly List<byte> list = new List<byte>();
/// <summary>
/// The number of bytes in the encoder.
/// The current working bit.
/// </summary>
internal int Length => this.list.Count;
private int currentBit;
/// <summary>
/// Gets or sets the intitial bit.
/// The current value.
/// </summary>
public int IntitialBit { get; set; }
private int currentValue;
/// <summary>
/// Initializes a new instance of the <see cref="BitEncoder"/> class.
@ -53,6 +38,16 @@ namespace ImageProcessor.Formats
this.IntitialBit = initial;
}
/// <summary>
/// Gets or sets the intitial bit.
/// </summary>
public int IntitialBit { get; set; }
/// <summary>
/// The number of bytes in the encoder.
/// </summary>
public int Length => this.list.Count;
/// <summary>
/// Adds the current byte to the end of the encoder.
/// </summary>
@ -78,7 +73,7 @@ namespace ImageProcessor.Formats
/// Adds the collection of bytes to the end of the encoder.
/// </summary>
/// <param name="collection">
/// 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.</param>
public void AddRange(byte[] collection)
{

12
src/ImageProcessor/Formats/Gif/DisposalMethod.cs

@ -1,13 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DisposalMethod.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="DisposalMethod.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides enumeration for instructing the decoder what to do with the last image
// in an animation sequence.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

20
src/ImageProcessor/Formats/Gif/GifConstants.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifConstants.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="GifConstants.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Constants that define specific points within a gif.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -30,11 +25,6 @@ namespace ImageProcessor.Formats
/// </summary>
public const byte ExtensionIntroducer = 0x21;
/// <summary>
/// The end introducer trailer <value>;</value>.
/// </summary>
public const byte EndIntroducer = 0x3B;
/// <summary>
/// The graphic control label.
/// </summary>
@ -85,5 +75,9 @@ namespace ImageProcessor.Formats
/// </summary>
public const byte Terminator = 0;
/// <summary>
/// The end introducer trailer <value>;</value>.
/// </summary>
public const byte EndIntroducer = 0x3B;
}
}

19
src/ImageProcessor/Formats/Gif/GifDecoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifDecoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="GifDecoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encoder for generating an image out of a gif encoded stream.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -14,7 +9,7 @@ namespace ImageProcessor.Formats
using System.IO;
/// <summary>
/// Encoder for generating an image out of a gif encoded stream.
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public class GifDecoder : IImageDecoder
{
@ -24,9 +19,6 @@ namespace ImageProcessor.Formats
/// <value>The size of the header.</value>
public int HeaderSize => 6;
internal GifDecoderCore CoreDecoder { get; private set; }
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
@ -69,8 +61,7 @@ namespace ImageProcessor.Formats
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
{
this.CoreDecoder = new GifDecoderCore();
this.CoreDecoder.Decode(image, stream);
new GifDecoderCore().Decode(image, stream);
}
}
}

232
src/ImageProcessor/Formats/Gif/GifDecoderCore.cs

@ -1,39 +1,56 @@
namespace ImageProcessor.Formats
// <copyright file="GifDecoderCore.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
using System;
using System.IO;
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
internal class GifDecoderCore
{
/// <summary>
/// The maximum comment length.
/// The image to decode the information to.
/// </summary>
private Image decodedImage;
/// <summary>
/// The currently loaded stream.
/// </summary>
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;
/// <summary>
/// The global color table.
/// </summary>
private byte[] globalColorTable;
private byte[] currentFrame;
internal GifLogicalScreenDescriptor LogicalScreenDescriptor { get; set; }
/// <summary>
/// The current frame.
/// </summary>
private byte[] currentFrame;
internal GifGraphicsControlExtension GraphicsControlExtension { get; set; }
/// <summary>
/// The logical screen descriptor.
/// </summary>
private GifLogicalScreenDescriptor logicalScreenDescriptor;
internal byte Quality { get; set; }
/// <summary>
/// The graphics control extension.
/// </summary>
private GifGraphicsControlExtension graphicsControlExtension;
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param>
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 @@
}
}
/// <summary>
/// Reads the graphic control extension.
/// </summary>
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();
}
/// <summary>
/// Reads the image descriptor
/// </summary>
/// <returns><see cref="GifImageDescriptor"/></returns>
private GifImageDescriptor ReadImageDescriptor()
{
byte[] buffer = new byte[9];
@ -130,6 +148,9 @@
return imageDescriptor;
}
/// <summary>
/// Reads the logical screen descriptor.
/// </summary>
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}'");
}
}
/// <summary>
/// Skips the designated number of bytes in the stream.
/// </summary>
/// <param name="length">The number of bytes to skip.</param>
private void Skip(int length)
{
this.currentStream.Seek(length, SeekOrigin.Current);
@ -173,25 +198,31 @@
}
}
/// <summary>
/// Reads the gif comments.
/// </summary>
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)));
}
}
/// <summary>
/// Reads an individual gif frame.
/// </summary>
private void ReadFrame()
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
@ -210,6 +241,11 @@
this.Skip(0);
}
/// <summary>
/// Reads the frame indices marking the color to use for each pixel.
/// </summary>
/// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] ReadFrameIndices(GifImageDescriptor imageDescriptor)
{
int dataSize = this.currentStream.ReadByte();
@ -220,6 +256,11 @@
return indices;
}
/// <summary>
/// Reads the local color table from the current frame.
/// </summary>
/// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] ReadFrameLocalColorTable(GifImageDescriptor imageDescriptor)
{
byte[] localColorTable = null;
@ -234,10 +275,16 @@
return localColorTable;
}
/// <summary>
/// Reads the frames colors, mapping indices to colors.
/// </summary>
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
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;
}

22
src/ImageProcessor/Formats/Gif/GifEncoder.cs

@ -10,7 +10,7 @@ namespace ImageProcessor.Formats
using System.Linq;
/// <summary>
/// The Gif encoder
/// Image encoder for writing image data to a stream in gif format.
/// </summary>
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);
}
/// <summary>
@ -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);
}
/// <summary>

57
src/ImageProcessor/Formats/Gif/LzwDecoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LzwDecoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="LzwDecoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Decompresses data using the LZW algorithms.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -19,9 +14,9 @@ namespace ImageProcessor.Formats
internal sealed class LzwDecoder
{
/// <summary>
/// The stack size.
/// One more than the maximum value 12 bit integer.
/// </summary>
private const int StackSize = 4096;
private const int MaxStackSize = 4096;
/// <summary>
/// The null code.
@ -37,18 +32,19 @@ namespace ImageProcessor.Formats
/// Initializes a new instance of the <see cref="LzwDecoder"/> class
/// and sets the stream, where the compressed data should be read from.
/// </summary>
/// <param name="stream">The stream. where to read from.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null
/// (Nothing in Visual Basic).</exception>
/// <param name="stream">The stream to read from.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
public LzwDecoder(Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
this.stream = stream;
}
/// <summary>
/// Decodes and decompresses all pixel indices from the stream.
/// <remarks>
/// </remarks>
/// </summary>
/// <param name="width">The width of the pixel index array.</param>
/// <param name="height">The height of the pixel index array.</param>
@ -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;
}

20
src/ImageProcessor/Formats/Gif/LzwEncoder.cs

@ -1,16 +1,11 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LzwEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="LzwEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encodes an image pixels used on a method based on LZW compression.
// <see href="http://matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp"/>
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
@ -52,8 +47,15 @@ namespace ImageProcessor.Formats
this.initDataSize = this.colorDepth;
}
/// <summary>
/// Encodes the compressed indexed pixel data to the given stream.
/// </summary>
/// <param name="stream">The stream to add the data to.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
public void Encode(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
// Whether it is a first step.
bool first = true;

11
src/ImageProcessor/Formats/Gif/Quantizer/IQuantizer.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="IQuantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="IQuantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides methods for allowing quantization of images pixels.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

12
src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs

@ -1,13 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="OctreeQuantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="OctreeQuantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to calculate the color palette of an image using an Octree pattern.
// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

11
src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="QuantizedImage.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="QuantizedImage.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Provides methods for allowing quantization of images pixels.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

11
src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Quantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="Quantizer.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates methods to calculate the color palette of an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

4
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/

12
src/ImageProcessor/Formats/Gif/Sections/GifGraphicsControlExtension.cs

@ -1,13 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifGraphicsControlExtension.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="GifGraphicsControlExtension.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The Graphic Control Extension contains parameters used when
// processing a graphic rendering block.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

14
src/ImageProcessor/Formats/Gif/Sections/GifImageDescriptor.cs

@ -1,15 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifImageDescriptor.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="GifImageDescriptor.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// 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.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{

45
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)));
}
/// <summary>
@ -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;
}
/// <summary>
/// Swaps two references.
/// </summary>
/// <typeparam name="TRef">The type of the references to swap.</typeparam>
/// <param name="lhs">The first reference.</param>
/// <param name="rhs">The second reference.</param>
private void Swap<TRef>(ref TRef lhs, ref TRef rhs)
where TRef : class
{
TRef tmp = lhs;
lhs = rhs;
rhs = tmp;
}
}
}

2
src/ImageProcessor/ImageProcessor.csproj

@ -45,7 +45,6 @@
<Compile Include="Colors\YCbCr.cs" />
<Compile Include="Common\Extensions\ByteExtensions.cs" />
<Compile Include="Common\Extensions\ComparableExtensions.cs" />
<Compile Include="Common\Helpers\Utils.cs" />
<Compile Include="Formats\Bmp\BmpCompression.cs" />
<Compile Include="Formats\Bmp\BmpFileHeader.cs" />
<Compile Include="Formats\Bmp\BmpInfoHeader.cs" />
@ -176,6 +175,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="Formats\Gif\README.md" />
<None Include="Formats\Jpg\README.md" />
<None Include="packages.config" />
<AdditionalFiles Include="stylecop.json" />

Loading…
Cancel
Save