From 0852c4c66cab54f666fc03336e1d5318a6872c33 Mon Sep 17 00:00:00 2001 From: James South Date: Wed, 29 Apr 2015 20:58:34 +0100 Subject: [PATCH] Rename Colors, Begin Gif encoder Former-commit-id: b08ac8597fa0f3ee4025b2a8814b34dd008c5291 Former-commit-id: 9bb7361434f7c87ed463a67c52b563c0d0b1b72b Former-commit-id: c8fa576e7a251a9a4954c6b605ef4eea4104368e --- .../Colors/{Color.cs => Bgra.cs} | 138 +++---- .../Colors/{YCbCrColor.cs => YCbCr.cs} | 91 ++-- src/ImageProcessor/Formats/Gif/GifDecoder.cs | 389 ++---------------- .../Formats/Gif/GifDecoderCore.cs | 359 ++++++++++++++++ src/ImageProcessor/Formats/Gif/GifEncoder.cs | 139 +++++++ .../Formats/Gif/GifLogicalScreenDescriptor.cs | 7 +- src/ImageProcessor/Formats/Png/PngDecoder.cs | 4 +- src/ImageProcessor/Formats/Png/PngEncoder.cs | 9 +- src/ImageProcessor/ImageBase.cs | 6 +- src/ImageProcessor/ImageProcessor.csproj | 6 +- .../Colors/ColorConversionTests.cs | 29 +- .../ImageProcessor.Tests/Colors/ColorTests.cs | 36 +- 12 files changed, 710 insertions(+), 503 deletions(-) rename src/ImageProcessor/Colors/{Color.cs => Bgra.cs} (64%) rename src/ImageProcessor/Colors/{YCbCrColor.cs => YCbCr.cs} (69%) create mode 100644 src/ImageProcessor/Formats/Gif/GifDecoderCore.cs create mode 100644 src/ImageProcessor/Formats/Gif/GifEncoder.cs diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Bgra.cs similarity index 64% rename from src/ImageProcessor/Colors/Color.cs rename to src/ImageProcessor/Colors/Bgra.cs index 0b7785d22..8022558ee 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Bgra.cs @@ -1,5 +1,5 @@ // -------------------------------------------------------------------------------------------------------------------- -// +// // Copyright © James South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -18,27 +18,27 @@ namespace ImageProcessor /// Represents an BGRA (blue, green, red, alpha) color. /// [StructLayout(LayoutKind.Explicit)] - public struct Color : IEquatable + public struct Bgra : IEquatable { /// - /// Represents a that has B, G, R, and A values set to zero. + /// Represents a that has B, G, R, and A values set to zero. /// - public static readonly Color Empty; + public static readonly Bgra Empty; /// - /// Represents a transparent that has B, G, R, and A values set to 255, 255, 255, 0. + /// Represents a transparent that has B, G, R, and A values set to 255, 255, 255, 0. /// - public static readonly Color Transparent = new Color(255, 255, 255, 0); + public static readonly Bgra Transparent = new Bgra(255, 255, 255, 0); /// - /// Represents a black that has B, G, R, and A values set to 0, 0, 0, 0. + /// Represents a black that has B, G, R, and A values set to 0, 0, 0, 0. /// - public static readonly Color Black = new Color(0, 0, 0, 255); + public static readonly Bgra Black = new Bgra(0, 0, 0, 255); /// - /// Represents a white that has B, G, R, and A values set to 255, 255, 255, 255. + /// Represents a white that has B, G, R, and A values set to 255, 255, 255, 255. /// - public static readonly Color White = new Color(255, 255, 255, 255); + public static readonly Bgra White = new Bgra(255, 255, 255, 255); /// /// Holds the blue component of the color @@ -65,24 +65,24 @@ namespace ImageProcessor public byte A; /// - /// Permits the to be treated as a 32 bit integer. + /// Permits the to be treated as a 32 bit integer. /// [FieldOffset(0)] - public int Bgra; + public int BGRA; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// - /// The blue component of this . + /// The blue component of this . /// /// - /// The green component of this . + /// The green component of this . /// /// - /// The red component of this . + /// The red component of this . /// - public Color(byte b, byte g, byte r) + public Bgra(byte b, byte g, byte r) : this() { this.B = b; @@ -92,21 +92,21 @@ namespace ImageProcessor } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// - /// The blue component of this . + /// The blue component of this . /// /// - /// The green component of this . + /// The green component of this . /// /// - /// The red component of this . + /// The red component of this . /// /// - /// The alpha component of this . + /// The alpha component of this . /// - public Color(byte b, byte g, byte r, byte a) + public Bgra(byte b, byte g, byte r, byte a) : this() { this.B = b; @@ -116,27 +116,28 @@ namespace ImageProcessor } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The combined color components. /// - public Color(int bgra) + public Bgra(int bgra) : this() { - this.Bgra = bgra; + this.BGRA = bgra; } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The hexadecimal representation of the combined color components arranged - /// in r,g,b or a,r,g,b format to match web syntax. + /// in rgb, rrggbb, or aarrggbb format to match web syntax. /// - public Color(string hex) + public Bgra(string hex) : this() { + // Hexadecimal representations are layed out AARRGGBB to we need to do some reordering. hex = hex.StartsWith("#") ? hex.Substring(1) : hex; if (hex.Length != 8 && hex.Length != 6 && hex.Length != 3) @@ -146,33 +147,33 @@ namespace ImageProcessor if (hex.Length == 8) { - this.R = Convert.ToByte(hex.Substring(2, 2), 16); - this.G = Convert.ToByte(hex.Substring(4, 2), 16); this.B = Convert.ToByte(hex.Substring(6, 2), 16); + this.G = Convert.ToByte(hex.Substring(4, 2), 16); + this.R = Convert.ToByte(hex.Substring(2, 2), 16); this.A = Convert.ToByte(hex.Substring(0, 2), 16); } else if (hex.Length == 6) { - this.R = Convert.ToByte(hex.Substring(0, 2), 16); - this.G = Convert.ToByte(hex.Substring(2, 2), 16); this.B = Convert.ToByte(hex.Substring(4, 2), 16); + this.G = Convert.ToByte(hex.Substring(2, 2), 16); + this.R = Convert.ToByte(hex.Substring(0, 2), 16); this.A = 255; } else { - string r = char.ToString(hex[0]); - string g = char.ToString(hex[1]); string b = char.ToString(hex[2]); + string g = char.ToString(hex[1]); + string r = char.ToString(hex[0]); - this.R = Convert.ToByte(r + r, 16); - this.G = Convert.ToByte(g + g, 16); this.B = Convert.ToByte(b + b, 16); + this.G = Convert.ToByte(g + g, 16); + this.R = Convert.ToByte(r + r, 16); this.A = 255; } } /// - /// Gets a value indicating whether this is empty. + /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty @@ -184,66 +185,43 @@ namespace ImageProcessor } /// - /// Compares two objects. The result specifies whether the values - /// of the , , , and - /// properties of the two objects are equal. + /// Compares two objects. The result specifies whether the values + /// of the , , , and + /// properties of the two objects are equal. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is equal to the parameter; otherwise, false. /// - public static bool operator ==(Color left, Color right) + public static bool operator ==(Bgra left, Bgra right) { return left.Equals(right); } /// - /// Compares two objects. The result specifies whether the values - /// of the , , , and - /// properties of the two objects are unequal. + /// Compares two objects. The result specifies whether the values + /// of the , , , and + /// properties of the two objects are unequal. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is unequal to the parameter; otherwise, false. /// - public static bool operator !=(Color left, Color right) + public static bool operator !=(Bgra left, Bgra right) { return !left.Equals(right); } - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// - /// The instance of to convert. - /// - /// - /// An instance of . - /// - public static implicit operator Color(YCbCrColor color) - { - float y = color.Y; - float cb = color.Cb - 128; - float cr = color.Cr - 128; - - byte r = Convert.ToByte((y + (1.402 * cr)).Clamp(0, 255)); - byte g = Convert.ToByte((y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255)); - byte b = Convert.ToByte((y + (1.772 * cb)).Clamp(0, 255)); - - return new Color(b, g, r, 255); - } - /// /// Indicates whether this instance and a specified object are equal. /// @@ -253,11 +231,11 @@ namespace ImageProcessor /// Another object to compare to. public override bool Equals(object obj) { - if (obj is Color) + if (obj is Bgra) { - Color color = (Color)obj; + Bgra color = (Bgra)obj; - return this.Bgra == color.Bgra; + return this.BGRA == color.BGRA; } return false; @@ -284,7 +262,7 @@ namespace ImageProcessor { if (this.IsEmpty) { - return "Color [Empty]"; + return "Color [ Empty ]"; } return string.Format("Color [ B={0}, G={1}, R={2}, A={3} ]", this.B, this.G, this.R, this.A); @@ -297,7 +275,7 @@ namespace ImageProcessor /// True if the current object is equal to the parameter; otherwise, false. /// /// An object to compare with this object. - public bool Equals(Color other) + public bool Equals(Bgra other) { return this.B.Equals(other.B) && this.G.Equals(other.G) && this.R.Equals(other.R) && this.A.Equals(other.A); @@ -307,12 +285,12 @@ namespace ImageProcessor /// Returns the hash code for the given instance. /// /// - /// The instance of to return the hash code for. + /// The instance of to return the hash code for. /// /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(Color color) + private int GetHashCode(Bgra color) { unchecked { diff --git a/src/ImageProcessor/Colors/YCbCrColor.cs b/src/ImageProcessor/Colors/YCbCr.cs similarity index 69% rename from src/ImageProcessor/Colors/YCbCrColor.cs rename to src/ImageProcessor/Colors/YCbCr.cs index 5fa4eaa0c..389d76b25 100644 --- a/src/ImageProcessor/Colors/YCbCrColor.cs +++ b/src/ImageProcessor/Colors/YCbCr.cs @@ -1,5 +1,5 @@ // -------------------------------------------------------------------------------------------------------------------- -// +// // Copyright © James South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -20,12 +20,12 @@ namespace ImageProcessor /// ITU-R BT.601 standard used in digital imaging systems. /// /// - public struct YCbCrColor : IEquatable + public struct YCbCr : IEquatable { /// - /// Represents a that has Y, Cb, and Cr values set to zero. + /// Represents a that has Y, Cb, and Cr values set to zero. /// - public static readonly YCbCrColor Empty = new YCbCrColor(); + public static readonly YCbCr Empty = new YCbCr(); /// /// Holds the Y luminance component. @@ -51,12 +51,12 @@ namespace ImageProcessor private const float Epsilon = 0.0001f; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The y luminance component. /// The cb chroma component. /// The cr chroma component. - public YCbCrColor(float y, float cb, float cr) + public YCbCr(float y, float cb, float cr) { this.Y = y.Clamp(0, 255); this.Cb = cb.Clamp(0, 255); @@ -64,7 +64,7 @@ namespace ImageProcessor } /// - /// Gets a value indicating whether this is empty. + /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty @@ -78,64 +78,87 @@ namespace ImageProcessor } /// - /// Compares two objects. The result specifies whether the values - /// of the , , and - /// properties of the two objects are equal. + /// Compares two objects. The result specifies whether the values + /// of the , , and + /// properties of the two objects are equal. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is equal to the parameter; otherwise, false. /// - public static bool operator ==(YCbCrColor left, YCbCrColor right) + public static bool operator ==(YCbCr left, YCbCr right) { return left.Equals(right); } /// - /// Compares two objects. The result specifies whether the values - /// of the , , and - /// properties of the two objects are unequal. + /// Compares two objects. The result specifies whether the values + /// of the , , and + /// properties of the two objects are unequal. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is unequal to the parameter; otherwise, false. /// - public static bool operator !=(YCbCrColor left, YCbCrColor right) + public static bool operator !=(YCbCr left, YCbCr right) { return !left.Equals(right); } /// - /// Allows the implicit conversion of an instance of to a - /// . + /// Allows the implicit conversion of an instance of to a + /// . /// /// - /// The instance of to convert. + /// The instance of to convert. /// /// - /// An instance of . + /// An instance of . /// - public static implicit operator YCbCrColor(Color color) + public static implicit operator YCbCr(Bgra color) { - byte r = color.R; - byte g = color.G; byte b = color.B; + byte g = color.G; + byte r = color.R; float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * b)); float cb = 128 + (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b)); float cr = 128 + (float)((0.5 * r) - (0.418688 * g) - (0.081312 * b)); - return new YCbCrColor(y, cb, cr); + return new YCbCr(y, cb, cr); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator Bgra(YCbCr color) + { + float y = color.Y; + float cb = color.Cb - 128; + float cr = color.Cr - 128; + + byte b = Convert.ToByte((y + (1.772 * cb)).Clamp(0, 255)); + byte g = Convert.ToByte((y - (0.34414 * cb) - (0.71414 * cr)).Clamp(0, 255)); + byte r = Convert.ToByte((y + (1.402 * cr)).Clamp(0, 255)); + + return new Bgra(b, g, r, 255); } /// @@ -147,9 +170,9 @@ namespace ImageProcessor /// Another object to compare to. public override bool Equals(object obj) { - if (obj is YCbCrColor) + if (obj is YCbCr) { - YCbCrColor color = (YCbCrColor)obj; + YCbCr color = (YCbCr)obj; return Math.Abs(this.Y - color.Y) < Epsilon && Math.Abs(this.Cb - color.Cb) < Epsilon @@ -180,10 +203,10 @@ namespace ImageProcessor { if (this.IsEmpty) { - return "YCbCrColor [Empty]"; + return "YCbCrColor [ Empty ]"; } - return string.Format("YCbCrColor [ Y={0:#0.##}, Cb={1:#0.##}, Cr={2:#0.##}]", this.Y, this.Cb, this.Cr); + return string.Format("YCbCrColor [ Y={0:#0.##}, Cb={1:#0.##}, Cr={2:#0.##} ]", this.Y, this.Cb, this.Cr); } /// @@ -193,7 +216,7 @@ namespace ImageProcessor /// True if the current object is equal to the parameter; otherwise, false. /// /// An object to compare with this object. - public bool Equals(YCbCrColor other) + public bool Equals(YCbCr other) { return this.Y.Equals(other.Y) && this.Cb.Equals(other.Cb) @@ -204,12 +227,12 @@ namespace ImageProcessor /// Returns the hash code for the given instance. /// /// - /// The instance of to return the hash code for. + /// The instance of to return the hash code for. /// /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(YCbCrColor color) + private int GetHashCode(YCbCr color) { unchecked { diff --git a/src/ImageProcessor/Formats/Gif/GifDecoder.cs b/src/ImageProcessor/Formats/Gif/GifDecoder.cs index 15bca5e40..5779d7ff9 100644 --- a/src/ImageProcessor/Formats/Gif/GifDecoder.cs +++ b/src/ImageProcessor/Formats/Gif/GifDecoder.cs @@ -1,12 +1,27 @@ -namespace ImageProcessor.Formats +// -------------------------------------------------------------------------------------------------------------------- +// +// 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 { using System; using System.IO; + /// + /// Encoder for generating an image out of a gif encoded stream. + /// public class GifDecoder : IImageDecoder { - public static int MaxCommentLength = 1024 * 8; - + /// + /// Gets the size of the header for this image type. + /// + /// The size of the header. public int HeaderSize { get @@ -15,12 +30,30 @@ } } + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file extension. + /// + /// True if the decoder supports the file extension; otherwise, false. + /// public bool IsSupportedFileExtension(string extension) { - if (extension.StartsWith(".")) extension = extension.Substring(1); + Guard.NotNullOrEmpty(extension, "extension"); + + extension = extension.StartsWith(".") ? extension.Substring(1) : extension; return extension.Equals("GIF", StringComparison.OrdinalIgnoreCase); } + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file header. + /// + /// True if the decoder supports the file header; otherwise, false. + /// public bool IsSupportedFileFormat(byte[] header) { return header.Length >= 6 && @@ -32,352 +65,14 @@ header[5] == 0x61; // a } + /// + /// Decodes the image from the specified stream to the . + /// + /// The to decode to. + /// The containing image data. public void Decode(Image image, Stream stream) { new GifDecoderCore().Decode(image, stream); } - - class GifDecoderCore - { - 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 _stream; - private GifLogicalScreenDescriptor _descriptor; - private byte[] _globalColorTable; - private byte[] _currentFrame; - private GifGraphicsControlExtension _graphicsControl; - - public void Decode(Image image, Stream stream) - { - _image = image; - - _stream = stream; - _stream.Seek(6, SeekOrigin.Current); - - ReadLogicalScreenDescriptor(); - - if (_descriptor.GlobalColorTableFlag == true) - { - _globalColorTable = new byte[_descriptor.GlobalColorTableSize * 3]; - - // Read the global color table from the stream - stream.Read(_globalColorTable, 0, _globalColorTable.Length); - } - - int nextFlag = stream.ReadByte(); - while (nextFlag != 0) - { - if (nextFlag == ImageLabel) - { - ReadFrame(); - } - else if (nextFlag == ExtensionIntroducer) - { - int gcl = stream.ReadByte(); - switch (gcl) - { - case GraphicControlLabel: - ReadGraphicalControlExtension(); - break; - case CommentLabel: - ReadComments(); - break; - case ApplicationExtensionLabel: - Skip(12); - break; - case PlainTextLabel: - Skip(13); - break; - } - } - else if (nextFlag == EndIntroducer) - { - break; - } - nextFlag = stream.ReadByte(); - } - } - - private void ReadGraphicalControlExtension() - { - byte[] buffer = new byte[6]; - - _stream.Read(buffer, 0, buffer.Length); - - byte packed = buffer[1]; - - _graphicsControl = new GifGraphicsControlExtension(); - _graphicsControl.DelayTime = BitConverter.ToInt16(buffer, 2); - _graphicsControl.TransparencyIndex = buffer[4]; - _graphicsControl.TransparencyFlag = (packed & 0x01) == 1; - _graphicsControl.DisposalMethod = (DisposalMethod)((packed & 0x1C) >> 2); - } - - private GifImageDescriptor ReadImageDescriptor() - { - byte[] buffer = new byte[9]; - - _stream.Read(buffer, 0, buffer.Length); - - byte packed = buffer[8]; - - GifImageDescriptor imageDescriptor = new GifImageDescriptor(); - imageDescriptor.Left = BitConverter.ToInt16(buffer, 0); - imageDescriptor.Top = BitConverter.ToInt16(buffer, 2); - imageDescriptor.Width = BitConverter.ToInt16(buffer, 4); - imageDescriptor.Height = BitConverter.ToInt16(buffer, 6); - imageDescriptor.LocalColorTableFlag = ((packed & 0x80) >> 7) == 1; - imageDescriptor.LocalColorTableSize = 2 << (packed & 0x07); - imageDescriptor.InterlaceFlag = ((packed & 0x40) >> 6) == 1; - - return imageDescriptor; - } - - private void ReadLogicalScreenDescriptor() - { - byte[] buffer = new byte[7]; - - _stream.Read(buffer, 0, buffer.Length); - - byte packed = buffer[4]; - - _descriptor = new GifLogicalScreenDescriptor(); - _descriptor.Width = BitConverter.ToInt16(buffer, 0); - _descriptor.Height = BitConverter.ToInt16(buffer, 2); - _descriptor.Background = buffer[5]; - _descriptor.GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1; - _descriptor.GlobalColorTableSize = 2 << (packed & 0x07); - - if (_descriptor.GlobalColorTableSize > 255 * 4) - { - throw new ImageFormatException(string.Format("Invalid gif colormap size '{0}'", _descriptor.GlobalColorTableSize)); - } - - if (_descriptor.Width > ImageBase.MaxWidth || _descriptor.Height > ImageBase.MaxHeight) - { - throw new ArgumentOutOfRangeException( - string.Format( - "The input gif '{0}x{1}' is bigger then the max allowed size '{2}x{3}'", - _descriptor.Width, - _descriptor.Height, - ImageBase.MaxWidth, - ImageBase.MaxHeight)); - } - } - - private void Skip(int length) - { - _stream.Seek(length, SeekOrigin.Current); - - int flag = 0; - - while ((flag = _stream.ReadByte()) != 0) - { - _stream.Seek(flag, SeekOrigin.Current); - } - } - - private void ReadComments() - { - int flag = 0; - - while ((flag = _stream.ReadByte()) != 0) - { - if (flag > MaxCommentLength) - { - throw new ImageFormatException(string.Format("Gif comment length '{0}' exceeds max '{1}'", flag, MaxCommentLength)); - } - - byte[] buffer = new byte[flag]; - - _stream.Read(buffer, 0, flag); - - _image.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(buffer))); - } - } - - private void ReadFrame() - { - GifImageDescriptor imageDescriptor = ReadImageDescriptor(); - - byte[] localColorTable = ReadFrameLocalColorTable(imageDescriptor); - - byte[] indices = ReadFrameIndices(imageDescriptor); - - // 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; - - ReadFrameColors(indices, colorTable, imageDescriptor); - - Skip(0); // skip any remaining blocks - } - - private byte[] ReadFrameIndices(GifImageDescriptor imageDescriptor) - { - int dataSize = _stream.ReadByte(); - - LzwDecoder lzwDecoder = new LzwDecoder(_stream); - - byte[] indices = lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize); - return indices; - } - - private byte[] ReadFrameLocalColorTable(GifImageDescriptor imageDescriptor) - { - byte[] localColorTable = null; - - if (imageDescriptor.LocalColorTableFlag) - { - localColorTable = new byte[imageDescriptor.LocalColorTableSize * 3]; - - _stream.Read(localColorTable, 0, localColorTable.Length); - } - - return localColorTable; - } - - private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) - { - int imageWidth = _descriptor.Width; - int imageHeight = _descriptor.Height; - - if (_currentFrame == null) - { - _currentFrame = new byte[imageWidth * imageHeight * 4]; - } - - byte[] lastFrame = null; - - if (_graphicsControl != null && - _graphicsControl.DisposalMethod == DisposalMethod.RestoreToPrevious) - { - lastFrame = new byte[imageWidth * imageHeight * 4]; - - Array.Copy(_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 - - for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) - { - // Check if this image is interlaced. - 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) - { - iPass++; - switch (iPass) - { - case 1: - iY = 4; - break; - case 2: - iY = 2; - iInc = 4; - break; - case 3: - iY = 1; - iInc = 2; - break; - } - } - - writeY = iY + descriptor.Top; - - iY += iInc; - } - else - { - writeY = y; - } - - for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) - { - offset = writeY * imageWidth + x; - - index = indices[i]; - - if (_graphicsControl == null || - _graphicsControl.TransparencyFlag == false || - _graphicsControl.TransparencyIndex != index) - { - _currentFrame[offset * 4 + 0] = colorTable[index * 3 + 2]; - _currentFrame[offset * 4 + 1] = colorTable[index * 3 + 1]; - _currentFrame[offset * 4 + 2] = colorTable[index * 3 + 0]; - _currentFrame[offset * 4 + 3] = (byte)255; - } - - i++; - } - } - - byte[] pixels = new byte[imageWidth * imageHeight * 4]; - - Array.Copy(_currentFrame, pixels, pixels.Length); - - ImageBase currentImage = null; - - if (_image.Pixels == null) - { - currentImage = _image; - currentImage.SetPixels(imageWidth, imageHeight, pixels); - - if (_graphicsControl != null && _graphicsControl.DelayTime > 0) - { - _image.FrameDelay = _graphicsControl.DelayTime; - } - } - else - { - ImageFrame frame = new ImageFrame(); - - currentImage = frame; - currentImage.SetPixels(imageWidth, imageHeight, pixels); - - _image.Frames.Add(frame); - } - - if (_graphicsControl != null) - { - if (_graphicsControl.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; - - _currentFrame[offset * 4 + 0] = 0; - _currentFrame[offset * 4 + 1] = 0; - _currentFrame[offset * 4 + 2] = 0; - _currentFrame[offset * 4 + 3] = 0; - } - } - } - else if (_graphicsControl.DisposalMethod == DisposalMethod.RestoreToPrevious) - { - _currentFrame = lastFrame; - } - } - } - } } } diff --git a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs new file mode 100644 index 000000000..2c7b9ee73 --- /dev/null +++ b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs @@ -0,0 +1,359 @@ +namespace ImageProcessor.Formats +{ + using System; + using System.IO; + + internal class GifDecoderCore + { + /// + /// The maximum comment length. + /// + 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; + private byte[] globalColorTable; + private byte[] _currentFrame; + private GifLogicalScreenDescriptor logicalScreenDescriptor; + private GifGraphicsControlExtension _graphicsControl; + + public void Decode(Image image, Stream stream) + { + this.image = image; + + this.currentStream = stream; + this.currentStream.Seek(6, SeekOrigin.Current); + this.ReadLogicalScreenDescriptor(); + + if (this.logicalScreenDescriptor.GlobalColorTableFlag) + { + this.globalColorTable = new byte[this.logicalScreenDescriptor.GlobalColorTableSize * 3]; + + // Read the global color table from the stream + stream.Read(this.globalColorTable, 0, this.globalColorTable.Length); + } + + int nextFlag = stream.ReadByte(); + while (nextFlag != 0) + { + if (nextFlag == ImageLabel) + { + this.ReadFrame(); + } + else if (nextFlag == ExtensionIntroducer) + { + int label = stream.ReadByte(); + switch (label) + { + case GraphicControlLabel: + this.ReadGraphicalControlExtension(); + break; + case CommentLabel: + this.ReadComments(); + break; + case ApplicationExtensionLabel: + this.Skip(12); + break; + case PlainTextLabel: + this.Skip(13); + break; + } + } + else if (nextFlag == EndIntroducer) + { + break; + } + + nextFlag = stream.ReadByte(); + } + } + + private void ReadGraphicalControlExtension() + { + byte[] buffer = new byte[6]; + + this.currentStream.Read(buffer, 0, buffer.Length); + + byte packed = buffer[1]; + + _graphicsControl = new GifGraphicsControlExtension + { + DelayTime = BitConverter.ToInt16(buffer, 2), + TransparencyIndex = buffer[4], + TransparencyFlag = (packed & 0x01) == 1, + DisposalMethod = (DisposalMethod)((packed & 0x1C) >> 2) + }; + } + + private GifImageDescriptor ReadImageDescriptor() + { + byte[] buffer = new byte[9]; + + this.currentStream.Read(buffer, 0, buffer.Length); + + byte packed = buffer[8]; + + GifImageDescriptor imageDescriptor = new GifImageDescriptor(); + imageDescriptor.Left = BitConverter.ToInt16(buffer, 0); + imageDescriptor.Top = BitConverter.ToInt16(buffer, 2); + imageDescriptor.Width = BitConverter.ToInt16(buffer, 4); + imageDescriptor.Height = BitConverter.ToInt16(buffer, 6); + imageDescriptor.LocalColorTableFlag = ((packed & 0x80) >> 7) == 1; + imageDescriptor.LocalColorTableSize = 2 << (packed & 0x07); + imageDescriptor.InterlaceFlag = ((packed & 0x40) >> 6) == 1; + + return imageDescriptor; + } + + private void ReadLogicalScreenDescriptor() + { + byte[] buffer = new byte[7]; + + this.currentStream.Read(buffer, 0, buffer.Length); + + byte packed = buffer[4]; + + this.logicalScreenDescriptor = new GifLogicalScreenDescriptor + { + Width = BitConverter.ToInt16(buffer, 0), + Height = BitConverter.ToInt16(buffer, 2), + BackgroundColorIndex = buffer[5], + PixelAspectRatio = buffer[6], + GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1, + GlobalColorTableSize = 2 << (packed & 0x07) + }; + + if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4) + { + throw new ImageFormatException(string.Format("Invalid gif colormap size '{0}'", this.logicalScreenDescriptor.GlobalColorTableSize)); + } + + if (this.logicalScreenDescriptor.Width > ImageBase.MaxWidth || this.logicalScreenDescriptor.Height > ImageBase.MaxHeight) + { + throw new ArgumentOutOfRangeException( + string.Format( + "The input gif '{0}x{1}' is bigger then the max allowed size '{2}x{3}'", + this.logicalScreenDescriptor.Width, + this.logicalScreenDescriptor.Height, + ImageBase.MaxWidth, + ImageBase.MaxHeight)); + } + } + + private void Skip(int length) + { + this.currentStream.Seek(length, SeekOrigin.Current); + + int flag = 0; + + while ((flag = this.currentStream.ReadByte()) != 0) + { + this.currentStream.Seek(flag, SeekOrigin.Current); + } + } + + private void ReadComments() + { + int flag = 0; + + while ((flag = this.currentStream.ReadByte()) != 0) + { + if (flag > MaxCommentLength) + { + throw new ImageFormatException(string.Format("Gif comment length '{0}' exceeds max '{1}'", flag, MaxCommentLength)); + } + + byte[] buffer = new byte[flag]; + + this.currentStream.Read(buffer, 0, flag); + + this.image.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(buffer))); + } + } + + private void ReadFrame() + { + GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); + + byte[] localColorTable = this.ReadFrameLocalColorTable(imageDescriptor); + + byte[] indices = this.ReadFrameIndices(imageDescriptor); + + // 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; + + this.ReadFrameColors(indices, colorTable, imageDescriptor); + + // Skip any remaining blocks + this.Skip(0); + } + + private byte[] ReadFrameIndices(GifImageDescriptor imageDescriptor) + { + int dataSize = this.currentStream.ReadByte(); + + LzwDecoder lzwDecoder = new LzwDecoder(this.currentStream); + + byte[] indices = lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize); + return indices; + } + + 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; + } + + private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) + { + int imageWidth = this.logicalScreenDescriptor.Width; + int imageHeight = this.logicalScreenDescriptor.Height; + + if (_currentFrame == null) + { + _currentFrame = new byte[imageWidth * imageHeight * 4]; + } + + byte[] lastFrame = null; + + if (_graphicsControl != null && + _graphicsControl.DisposalMethod == DisposalMethod.RestoreToPrevious) + { + lastFrame = new byte[imageWidth * imageHeight * 4]; + + Array.Copy(_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 + + for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) + { + // Check if this image is interlaced. + 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) + { + iPass++; + switch (iPass) + { + case 1: + iY = 4; + break; + case 2: + iY = 2; + iInc = 4; + break; + case 3: + iY = 1; + iInc = 2; + break; + } + } + + writeY = iY + descriptor.Top; + + iY += iInc; + } + else + { + writeY = y; + } + + for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) + { + offset = writeY * imageWidth + x; + + index = indices[i]; + + if (_graphicsControl == null || + _graphicsControl.TransparencyFlag == false || + _graphicsControl.TransparencyIndex != index) + { + _currentFrame[offset * 4 + 0] = colorTable[index * 3 + 2]; + _currentFrame[offset * 4 + 1] = colorTable[index * 3 + 1]; + _currentFrame[offset * 4 + 2] = colorTable[index * 3 + 0]; + _currentFrame[offset * 4 + 3] = (byte)255; + } + + i++; + } + } + + byte[] pixels = new byte[imageWidth * imageHeight * 4]; + + Array.Copy(_currentFrame, pixels, pixels.Length); + + ImageBase currentImage; + + if (this.image.Pixels == null) + { + currentImage = this.image; + currentImage.SetPixels(imageWidth, imageHeight, pixels); + + if (_graphicsControl != null && _graphicsControl.DelayTime > 0) + { + this.image.FrameDelay = _graphicsControl.DelayTime; + } + } + else + { + ImageFrame frame = new ImageFrame(); + + currentImage = frame; + currentImage.SetPixels(imageWidth, imageHeight, pixels); + + this.image.Frames.Add(frame); + } + + if (_graphicsControl != null) + { + if (_graphicsControl.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; + + _currentFrame[offset * 4 + 0] = 0; + _currentFrame[offset * 4 + 1] = 0; + _currentFrame[offset * 4 + 2] = 0; + _currentFrame[offset * 4 + 3] = 0; + } + } + } + else if (_graphicsControl.DisposalMethod == DisposalMethod.RestoreToPrevious) + { + _currentFrame = lastFrame; + } + } + } + } +} diff --git a/src/ImageProcessor/Formats/Gif/GifEncoder.cs b/src/ImageProcessor/Formats/Gif/GifEncoder.cs new file mode 100644 index 000000000..f32853d34 --- /dev/null +++ b/src/ImageProcessor/Formats/Gif/GifEncoder.cs @@ -0,0 +1,139 @@ + +namespace ImageProcessor.Formats +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.IO; + + public class GifEncoder : IImageEncoder + { + /// + /// The quality. + /// + private int quality = 256; + + /// + /// Gets or sets the quality of output for images. + /// + /// For gifs the value ranges from 1 to 256. + public int Quality + { + get + { + return this.quality; + } + + set + { + this.quality = value.Clamp(1, 256); + } + } + + /// + /// Gets the default file extension for this encoder. + /// + public string Extension + { + get { return "GIF"; } + } + + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file extension. + /// + /// True if the decoder supports the file extension; otherwise, false. + /// + public bool IsSupportedFileExtension(string extension) + { + Guard.NotNullOrEmpty(extension, "extension"); + + extension = extension.StartsWith(".") ? extension.Substring(1) : extension; + return extension.Equals("GIF", StringComparison.OrdinalIgnoreCase); + } + + public void Encode(ImageBase image, Stream stream) + { + Guard.NotNull(image, "image"); + Guard.NotNull(stream, "stream"); + + // Write the header. + // File Header signature and version. + this.WriteString(stream, "GIF"); + this.WriteString(stream, "89a"); + + GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor + { + Width = (short)image.Width, + Height = (short)image.Height, + GlobalColorTableFlag = true, + GlobalColorTableSize = this.Quality + }; + + this.WriteGlobalLogicalScreenDescriptor(stream, descriptor); + + throw new System.NotImplementedException(); + } + + private void WriteGlobalLogicalScreenDescriptor(Stream stream, GifLogicalScreenDescriptor descriptor) + { + this.WriteShort(stream, descriptor.Width); + this.WriteShort(stream, descriptor.Width); + int bitdepth = this.GetBitsNeededForColorDepth(descriptor.GlobalColorTableSize) - 1; + int packed = 0x80 | // 1 : global color table flag = 1 (gct used) + 0x70 | // 2-4 : color resolution + 0x00 | // 5 : gct sort flag = 0 + bitdepth; // 6-8 : gct size` + + this.WriteByte(stream, packed); + this.WriteByte(stream, descriptor.BackgroundColorIndex); // Background Color Index + this.WriteByte(stream, descriptor.PixelAspectRatio); // Pixel aspect ratio + } + + /// + /// Writes a short to the given stream. + /// + /// The containing image data. + /// The value to write. + private void WriteShort(Stream stream, int value) + { + // Leave only one significant byte. + stream.WriteByte(Convert.ToByte(value & 0xff)); + stream.WriteByte(Convert.ToByte((value >> 8) & 0xff)); + } + + /// + /// Writes a short to the given stream. + /// + /// The containing image data. + /// The value to write. + private void WriteByte(Stream stream, int value) + { + stream.WriteByte(Convert.ToByte(value)); + } + + /// + /// Writes a string to the given stream. + /// + /// The containing image data. + /// The value to write. + private void WriteString(Stream stream, string value) + { + char[] chars = value.ToCharArray(); + foreach (char c in chars) + { + stream.WriteByte((byte)c); + } + } + + /// + /// Returns how many bits are required to store the specified number of colors. + /// Performs a Log2() on the value. + /// + private int GetBitsNeededForColorDepth(int colors) + { + return (int)Math.Ceiling(Math.Log(colors, 2)); + } + } +} diff --git a/src/ImageProcessor/Formats/Gif/GifLogicalScreenDescriptor.cs b/src/ImageProcessor/Formats/Gif/GifLogicalScreenDescriptor.cs index f1dbf7ba0..122288fd5 100644 --- a/src/ImageProcessor/Formats/Gif/GifLogicalScreenDescriptor.cs +++ b/src/ImageProcessor/Formats/Gif/GifLogicalScreenDescriptor.cs @@ -36,7 +36,12 @@ namespace ImageProcessor.Formats /// The Background Color is the color used for those /// pixels on the screen that are not covered by an image. /// - public byte Background { get; set; } + public byte BackgroundColorIndex { get; set; } + + /// + /// Gets or sets the pixel aspect ratio. Default to 0. + /// + public byte PixelAspectRatio { get; set; } /// /// Gets or sets a value indicating whether a flag denoting the presence of a Global Color Table diff --git a/src/ImageProcessor/Formats/Png/PngDecoder.cs b/src/ImageProcessor/Formats/Png/PngDecoder.cs index 8df7d0106..96f0dd109 100644 --- a/src/ImageProcessor/Formats/Png/PngDecoder.cs +++ b/src/ImageProcessor/Formats/Png/PngDecoder.cs @@ -4,7 +4,7 @@ // Licensed under the Apache License, Version 2.0. // // -// Encoder for generating a image out of a png stream. +// Encoder for generating an image out of a png encoded stream. // // -------------------------------------------------------------------------------------------------------------------- @@ -14,7 +14,7 @@ namespace ImageProcessor.Formats using System.IO; /// - /// Encoder for generating a image out of a png stream. + /// Encoder for generating an image out of a png encoded stream. /// /// /// At the moment the following features are supported: diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index 750ef0313..332b9e92e 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -120,7 +120,14 @@ namespace ImageProcessor.Formats stream.Write( new byte[] { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A + 0x89, // Set the high bit. + 0x50, // P + 0x4E, // N + 0x47, // G + 0x0D, // Line ending CRLF + 0x0A, // Line ending CRLF + 0x1A, // EOF + 0x0A // LF }, 0, 8); diff --git a/src/ImageProcessor/ImageBase.cs b/src/ImageProcessor/ImageBase.cs index e6c280e44..42605d535 100644 --- a/src/ImageProcessor/ImageBase.cs +++ b/src/ImageProcessor/ImageBase.cs @@ -162,8 +162,8 @@ namespace ImageProcessor /// The y-coordinate of the pixel. Must be greater /// than zero and smaller than the width of the pixel. /// - /// The at the specified position. - public Color this[int x, int y] + /// The at the specified position. + public Bgra this[int x, int y] { get { @@ -180,7 +180,7 @@ namespace ImageProcessor #endif int start = ((y * this.Width) + x) * 4; - return new Color(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]); + return new Bgra(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]); } set diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index be72662df..86b578617 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -40,7 +40,7 @@ - + @@ -50,6 +50,8 @@ + + @@ -63,7 +65,7 @@ - + diff --git a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs index 355cd3ea9..836be2e12 100644 --- a/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs +++ b/tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs @@ -21,46 +21,45 @@ namespace ImageProcessor.Tests public class ColorConversionTests { /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")] public void ColorToYCbCrColor() { // White - Color color = new Color(255, 255, 255, 255); - YCbCrColor yCbCrColor = color; + Bgra color = new Bgra(255, 255, 255, 255); + YCbCr yCbCrColor = color; Assert.Equal(255, yCbCrColor.Y); Assert.Equal(128, yCbCrColor.Cb); Assert.Equal(128, yCbCrColor.Cr); // Black - Color color2 = new Color(0, 0, 0, 255); - YCbCrColor yCbCrColor2 = color2; + Bgra color2 = new Bgra(0, 0, 0, 255); + YCbCr yCbCrColor2 = color2; Assert.Equal(0, yCbCrColor2.Y); Assert.Equal(128, yCbCrColor2.Cb); Assert.Equal(128, yCbCrColor2.Cr); - // Grey - Color color3 = new Color(128, 128, 128, 255); - YCbCrColor yCbCrColor3 = color3; + Bgra color3 = new Bgra(128, 128, 128, 255); + YCbCr yCbCrColor3 = color3; Assert.Equal(128, yCbCrColor3.Y); Assert.Equal(128, yCbCrColor3.Cb); Assert.Equal(128, yCbCrColor3.Cr); } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")] public void YCbCrColorToColor() { // White - YCbCrColor yCbCrColor = new YCbCrColor(255, 128, 128); - Color color = yCbCrColor; + YCbCr yCbCrColor = new YCbCr(255, 128, 128); + Bgra color = yCbCrColor; Assert.Equal(255, color.B); Assert.Equal(255, color.G); @@ -68,8 +67,8 @@ namespace ImageProcessor.Tests Assert.Equal(255, color.A); // Black - YCbCrColor yCbCrColor2 = new YCbCrColor(0, 128, 128); - Color color2 = yCbCrColor2; + YCbCr yCbCrColor2 = new YCbCr(0, 128, 128); + Bgra color2 = yCbCrColor2; Assert.Equal(0, color2.B); Assert.Equal(0, color2.G); @@ -77,8 +76,8 @@ namespace ImageProcessor.Tests Assert.Equal(255, color2.A); // Grey - YCbCrColor yCbCrColor3 = new YCbCrColor(128, 128, 128); - Color color3 = yCbCrColor3; + YCbCr yCbCrColor3 = new YCbCr(128, 128, 128); + Bgra color3 = yCbCrColor3; Assert.Equal(128, color3.B); Assert.Equal(128, color3.G); diff --git a/tests/ImageProcessor.Tests/Colors/ColorTests.cs b/tests/ImageProcessor.Tests/Colors/ColorTests.cs index c5a0d08e3..0f592e349 100644 --- a/tests/ImageProcessor.Tests/Colors/ColorTests.cs +++ b/tests/ImageProcessor.Tests/Colors/ColorTests.cs @@ -13,7 +13,7 @@ namespace ImageProcessor.Tests using Xunit; /// - /// Tests the struct. + /// Tests the struct. /// public class ColorTests { @@ -23,12 +23,12 @@ namespace ImageProcessor.Tests [Fact] public void AreEqual() { - Color color1 = new Color(0, 0, 0, 255); - Color color2 = new Color(0, 0, 0, 255); - Color color3 = new Color("#000"); - Color color4 = new Color("#000000"); - Color color5 = new Color("#FF000000"); - Color color6 = new Color(-16777216); + Bgra color1 = new Bgra(0, 0, 0, 255); + Bgra color2 = new Bgra(0, 0, 0, 255); + Bgra color3 = new Bgra("#000"); + Bgra color4 = new Bgra("#000000"); + Bgra color5 = new Bgra("#FF000000"); + Bgra color6 = new Bgra(-16777216); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -43,12 +43,12 @@ namespace ImageProcessor.Tests [Fact] public void AreNotEqual() { - Color color1 = new Color(255, 0, 0, 255); - Color color2 = new Color(0, 0, 0, 255); - Color color3 = new Color("#000"); - Color color4 = new Color("#000000"); - Color color5 = new Color("#FF000000"); - Color color6 = new Color(-16777216); + Bgra color1 = new Bgra(255, 0, 0, 255); + Bgra color2 = new Bgra(0, 0, 0, 255); + Bgra color3 = new Bgra("#000"); + Bgra color4 = new Bgra("#000000"); + Bgra color5 = new Bgra("#FF000000"); + Bgra color6 = new Bgra(-16777216); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -63,25 +63,25 @@ namespace ImageProcessor.Tests [Fact] public void ConstructorAssignsProperties() { - Color color1 = new Color(255, 10, 34, 220); + Bgra color1 = new Bgra(255, 10, 34, 220); Assert.Equal(255, color1.B); Assert.Equal(10, color1.G); Assert.Equal(34, color1.R); Assert.Equal(220, color1.A); - Color color2 = new Color(255, 10, 34); + Bgra color2 = new Bgra(255, 10, 34); Assert.Equal(255, color2.B); Assert.Equal(10, color2.G); Assert.Equal(34, color2.R); Assert.Equal(255, color2.A); - Color color3 = new Color(-1); + Bgra color3 = new Bgra(-1); Assert.Equal(255, color3.B); Assert.Equal(255, color3.G); Assert.Equal(255, color3.R); Assert.Equal(255, color3.A); - Color color4 = new Color("#FF0000"); + Bgra color4 = new Bgra("#FF0000"); Assert.Equal(0, color4.B); Assert.Equal(0, color4.G); Assert.Equal(255, color4.R); @@ -95,7 +95,7 @@ namespace ImageProcessor.Tests public void ConvertHex() { const string First = "FF000000"; - string second = new Color(0, 0, 0, 255).Bgra.ToString("X"); + string second = new Bgra(0, 0, 0, 255).BGRA.ToString("X"); Assert.Equal(First, second); } }