Browse Source

Rename Colors, Begin Gif encoder

Former-commit-id: b08ac8597fa0f3ee4025b2a8814b34dd008c5291
Former-commit-id: 9bb7361434f7c87ed463a67c52b563c0d0b1b72b
Former-commit-id: c8fa576e7a251a9a4954c6b605ef4eea4104368e
pull/17/head
James South 11 years ago
parent
commit
0852c4c66c
  1. 138
      src/ImageProcessor/Colors/Bgra.cs
  2. 91
      src/ImageProcessor/Colors/YCbCr.cs
  3. 389
      src/ImageProcessor/Formats/Gif/GifDecoder.cs
  4. 359
      src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
  5. 139
      src/ImageProcessor/Formats/Gif/GifEncoder.cs
  6. 7
      src/ImageProcessor/Formats/Gif/GifLogicalScreenDescriptor.cs
  7. 4
      src/ImageProcessor/Formats/Png/PngDecoder.cs
  8. 9
      src/ImageProcessor/Formats/Png/PngEncoder.cs
  9. 6
      src/ImageProcessor/ImageBase.cs
  10. 6
      src/ImageProcessor/ImageProcessor.csproj
  11. 29
      tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
  12. 36
      tests/ImageProcessor.Tests/Colors/ColorTests.cs

138
src/ImageProcessor/Colors/Color.cs → src/ImageProcessor/Colors/Bgra.cs

@ -1,5 +1,5 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Color.cs" company="James South">
// <copyright file="Bgra.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -18,27 +18,27 @@ namespace ImageProcessor
/// Represents an BGRA (blue, green, red, alpha) color.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct Color : IEquatable<Color>
public struct Bgra : IEquatable<Bgra>
{
/// <summary>
/// Represents a <see cref="Color"/> that has B, G, R, and A values set to zero.
/// Represents a <see cref="Bgra"/> that has B, G, R, and A values set to zero.
/// </summary>
public static readonly Color Empty;
public static readonly Bgra Empty;
/// <summary>
/// Represents a transparent <see cref="Color"/> that has B, G, R, and A values set to 255, 255, 255, 0.
/// Represents a transparent <see cref="Bgra"/> that has B, G, R, and A values set to 255, 255, 255, 0.
/// </summary>
public static readonly Color Transparent = new Color(255, 255, 255, 0);
public static readonly Bgra Transparent = new Bgra(255, 255, 255, 0);
/// <summary>
/// Represents a black <see cref="Color"/> that has B, G, R, and A values set to 0, 0, 0, 0.
/// Represents a black <see cref="Bgra"/> that has B, G, R, and A values set to 0, 0, 0, 0.
/// </summary>
public static readonly Color Black = new Color(0, 0, 0, 255);
public static readonly Bgra Black = new Bgra(0, 0, 0, 255);
/// <summary>
/// Represents a white <see cref="Color"/> that has B, G, R, and A values set to 255, 255, 255, 255.
/// Represents a white <see cref="Bgra"/> that has B, G, R, and A values set to 255, 255, 255, 255.
/// </summary>
public static readonly Color White = new Color(255, 255, 255, 255);
public static readonly Bgra White = new Bgra(255, 255, 255, 255);
/// <summary>
/// Holds the blue component of the color
@ -65,24 +65,24 @@ namespace ImageProcessor
public byte A;
/// <summary>
/// Permits the <see cref="Color"/> to be treated as a 32 bit integer.
/// Permits the <see cref="Bgra"/> to be treated as a 32 bit integer.
/// </summary>
[FieldOffset(0)]
public int Bgra;
public int BGRA;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="Color"/>.
/// The blue component of this <see cref="Bgra"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="Color"/>.
/// The green component of this <see cref="Bgra"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="Color"/>.
/// The red component of this <see cref="Bgra"/>.
/// </param>
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
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="Color"/>.
/// The blue component of this <see cref="Bgra"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="Color"/>.
/// The green component of this <see cref="Bgra"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="Color"/>.
/// The red component of this <see cref="Bgra"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="Color"/>.
/// The alpha component of this <see cref="Bgra"/>.
/// </param>
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
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// </summary>
/// <param name="bgra">
/// The combined color components.
/// </param>
public Color(int bgra)
public Bgra(int bgra)
: this()
{
this.Bgra = bgra;
this.BGRA = bgra;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// </summary>
/// <param name="hex">
/// 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.
/// </param>
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;
}
}
/// <summary>
/// Gets a value indicating whether this <see cref="Color"/> is empty.
/// Gets a value indicating whether this <see cref="Bgra"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty
@ -184,66 +185,43 @@ namespace ImageProcessor
}
/// <summary>
/// Compares two <see cref="Color"/> objects. The result specifies whether the values
/// of the <see cref="Color.B"/>, <see cref="Color.G"/>, <see cref="Color.R"/>, and <see cref="Color.A"/>
/// properties of the two <see cref="Color"/> objects are equal.
/// Compares two <see cref="Bgra"/> objects. The result specifies whether the values
/// of the <see cref="Bgra.B"/>, <see cref="Bgra.G"/>, <see cref="Bgra.R"/>, and <see cref="Bgra.A"/>
/// properties of the two <see cref="Bgra"/> objects are equal.
/// </summary>
/// <param name="left">
/// The <see cref="Color"/> on the left side of the operand.
/// The <see cref="Bgra"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Color"/> on the right side of the operand.
/// The <see cref="Bgra"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Color left, Color right)
public static bool operator ==(Bgra left, Bgra right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Color"/> objects. The result specifies whether the values
/// of the <see cref="Color.B"/>, <see cref="Color.G"/>, <see cref="Color.R"/>, and <see cref="Color.A"/>
/// properties of the two <see cref="Color"/> objects are unequal.
/// Compares two <see cref="Bgra"/> objects. The result specifies whether the values
/// of the <see cref="Bgra.B"/>, <see cref="Bgra.G"/>, <see cref="Bgra.R"/>, and <see cref="Bgra.A"/>
/// properties of the two <see cref="Bgra"/> objects are unequal.
/// </summary>
/// <param name="left">
/// The <see cref="Color"/> on the left side of the operand.
/// The <see cref="Bgra"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Color"/> on the right side of the operand.
/// The <see cref="Bgra"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Color left, Color right)
public static bool operator !=(Bgra left, Bgra right)
{
return !left.Equals(right);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCrColor"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="YCbCrColor"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
@ -253,11 +231,11 @@ namespace ImageProcessor
/// <param name="obj">Another object to compare to. </param>
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 <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
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.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to return the hash code for.
/// The instance of <see cref="Bgra"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Color color)
private int GetHashCode(Bgra color)
{
unchecked
{

91
src/ImageProcessor/Colors/YCbCrColor.cs → src/ImageProcessor/Colors/YCbCr.cs

@ -1,5 +1,5 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="YCbCrColor.cs" company="James South">
// <copyright file="YCbCr.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -20,12 +20,12 @@ namespace ImageProcessor
/// ITU-R BT.601 standard used in digital imaging systems.
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// </summary>
public struct YCbCrColor : IEquatable<YCbCrColor>
public struct YCbCr : IEquatable<YCbCr>
{
/// <summary>
/// Represents a <see cref="YCbCrColor"/> that has Y, Cb, and Cr values set to zero.
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.
/// </summary>
public static readonly YCbCrColor Empty = new YCbCrColor();
public static readonly YCbCr Empty = new YCbCr();
/// <summary>
/// Holds the Y luminance component.
@ -51,12 +51,12 @@ namespace ImageProcessor
private const float Epsilon = 0.0001f;
/// <summary>
/// Initializes a new instance of the <see cref="YCbCrColor"/> struct.
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary>
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
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
}
/// <summary>
/// Gets a value indicating whether this <see cref="YCbCrColor"/> is empty.
/// Gets a value indicating whether this <see cref="YCbCr"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty
@ -78,64 +78,87 @@ namespace ImageProcessor
}
/// <summary>
/// Compares two <see cref="YCbCrColor"/> objects. The result specifies whether the values
/// of the <see cref="YCbCrColor.Y"/>, <see cref="YCbCrColor.Cb"/>, and <see cref="YCbCrColor.Cr"/>
/// properties of the two <see cref="YCbCrColor"/> objects are equal.
/// Compares two <see cref="YCbCr"/> objects. The result specifies whether the values
/// of the <see cref="YCbCr.Y"/>, <see cref="YCbCr.Cb"/>, and <see cref="YCbCr.Cr"/>
/// properties of the two <see cref="YCbCr"/> objects are equal.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCrColor"/> on the left side of the operand.
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCrColor"/> on the right side of the operand.
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(YCbCrColor left, YCbCrColor right)
public static bool operator ==(YCbCr left, YCbCr right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="YCbCrColor"/> objects. The result specifies whether the values
/// of the <see cref="YCbCrColor.Y"/>, <see cref="YCbCrColor.Cb"/>, and <see cref="YCbCrColor.Cr"/>
/// properties of the two <see cref="YCbCrColor"/> objects are unequal.
/// Compares two <see cref="YCbCr"/> objects. The result specifies whether the values
/// of the <see cref="YCbCr.Y"/>, <see cref="YCbCr.Cb"/>, and <see cref="YCbCr.Cr"/>
/// properties of the two <see cref="YCbCr"/> objects are unequal.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCrColor"/> on the left side of the operand.
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCrColor"/> on the right side of the operand.
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(YCbCrColor left, YCbCrColor right)
public static bool operator !=(YCbCr left, YCbCr right)
{
return !left.Equals(right);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="YCbCrColor"/>.
/// Allows the implicit conversion of an instance of <see cref="Bgra"/> to a
/// <see cref="YCbCr"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// The instance of <see cref="Bgra"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCrColor"/>.
/// An instance of <see cref="YCbCr"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
/// <see cref="Bgra"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="YCbCr"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// </returns>
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);
}
/// <summary>
@ -147,9 +170,9 @@ namespace ImageProcessor
/// <param name="obj">Another object to compare to. </param>
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);
}
/// <summary>
@ -193,7 +216,7 @@ namespace ImageProcessor
/// True if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
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.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to return the hash code for.
/// The instance of <see cref="Bgra"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(YCbCrColor color)
private int GetHashCode(YCbCr color)
{
unchecked
{

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

@ -1,12 +1,27 @@
namespace ImageProcessor.Formats
// --------------------------------------------------------------------------------------------------------------------
// <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
{
using System;
using System.IO;
/// <summary>
/// Encoder for generating an image out of a gif encoded stream.
/// </summary>
public class GifDecoder : IImageDecoder
{
public static int MaxCommentLength = 1024 * 8;
/// <summary>
/// Gets the size of the header for this image type.
/// </summary>
/// <value>The size of the header.</value>
public int HeaderSize
{
get
@ -15,12 +30,30 @@
}
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="extension">The <see cref="string"/> containing the file extension.</param>
/// <returns>
/// True if the decoder supports the file extension; otherwise, false.
/// </returns>
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);
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>
/// True if the decoder supports the file header; otherwise, false.
/// </returns>
public bool IsSupportedFileFormat(byte[] header)
{
return header.Length >= 6 &&
@ -32,352 +65,14 @@
header[5] == 0x61; // a
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
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;
}
}
}
}
}
}

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

@ -0,0 +1,359 @@
namespace ImageProcessor.Formats
{
using System;
using System.IO;
internal class GifDecoderCore
{
/// <summary>
/// The maximum comment length.
/// </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;
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;
}
}
}
}
}

139
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
{
/// <summary>
/// The quality.
/// </summary>
private int quality = 256;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
public int Quality
{
get
{
return this.quality;
}
set
{
this.quality = value.Clamp(1, 256);
}
}
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
public string Extension
{
get { return "GIF"; }
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="extension">The <see cref="string"/> containing the file extension.</param>
/// <returns>
/// True if the decoder supports the file extension; otherwise, false.
/// </returns>
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
}
/// <summary>
/// Writes a short to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
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));
}
/// <summary>
/// Writes a short to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteByte(Stream stream, int value)
{
stream.WriteByte(Convert.ToByte(value));
}
/// <summary>
/// Writes a string to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteString(Stream stream, string value)
{
char[] chars = value.ToCharArray();
foreach (char c in chars)
{
stream.WriteByte((byte)c);
}
}
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
private int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
}
}
}

7
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.
/// </summary>
public byte Background { get; set; }
public byte BackgroundColorIndex { get; set; }
/// <summary>
/// Gets or sets the pixel aspect ratio. Default to 0.
/// </summary>
public byte PixelAspectRatio { get; set; }
/// <summary>
/// Gets or sets a value indicating whether a flag denoting the presence of a Global Color Table

4
src/ImageProcessor/Formats/Png/PngDecoder.cs

@ -4,7 +4,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encoder for generating a image out of a png stream.
// Encoder for generating an image out of a png encoded stream.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
@ -14,7 +14,7 @@ namespace ImageProcessor.Formats
using System.IO;
/// <summary>
/// Encoder for generating a image out of a png stream.
/// Encoder for generating an image out of a png encoded stream.
/// </summary>
/// <remarks>
/// At the moment the following features are supported:

9
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);

6
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.
/// </param>
/// <returns>The <see cref="Color"/> at the specified position.</returns>
public Color this[int x, int y]
/// <returns>The <see cref="Bgra"/> at the specified position.</returns>
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

6
src/ImageProcessor/ImageProcessor.csproj

@ -40,7 +40,7 @@
<Folder Include="Filters\" />
</ItemGroup>
<ItemGroup>
<Compile Include="Colors\YCbCrColor.cs" />
<Compile Include="Colors\YCbCr.cs" />
<Compile Include="Common\Extensions\ByteExtensions.cs" />
<Compile Include="Common\Extensions\ComparableExtensions.cs" />
<Compile Include="Common\Helpers\Utils.cs" />
@ -50,6 +50,8 @@
<Compile Include="Formats\Bmp\BmpDecoder.cs" />
<Compile Include="Formats\Bmp\BmpDecoderCore.cs" />
<Compile Include="Formats\Bmp\BmpEncoder.cs" />
<Compile Include="Formats\Gif\GifDecoderCore.cs" />
<Compile Include="Formats\Gif\GifEncoder.cs" />
<Compile Include="Formats\Png\PngDecoder.cs" />
<Compile Include="Formats\Png\PngDecoderCore.cs" />
<Compile Include="Formats\Png\PngEncoder.cs" />
@ -63,7 +65,7 @@
<Compile Include="Formats\Png\PngHeader.cs" />
<Compile Include="ImageProperty.cs" />
<Compile Include="ImageFrame.cs" />
<Compile Include="Colors\Color.cs" />
<Compile Include="Colors\Bgra.cs" />
<Compile Include="Common\Exceptions\ImageFormatException.cs" />
<Compile Include="Common\Helpers\Guard.cs" />
<Compile Include="Formats\Gif\GifDecoder.cs" />

29
tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

@ -21,46 +21,45 @@ namespace ImageProcessor.Tests
public class ColorConversionTests
{
/// <summary>
/// Tests the implicit conversion from <see cref="Color"/> to <see cref="YCbCrColor"/>.
/// Tests the implicit conversion from <see cref="Bgra"/> to <see cref="YCbCr"/>.
/// </summary>
[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);
}
/// <summary>
/// Tests the implicit conversion from <see cref="YCbCrColor"/> to <see cref="Color"/>.
/// Tests the implicit conversion from <see cref="YCbCr"/> to <see cref="Bgra"/>.
/// </summary>
[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);

36
tests/ImageProcessor.Tests/Colors/ColorTests.cs

@ -13,7 +13,7 @@ namespace ImageProcessor.Tests
using Xunit;
/// <summary>
/// Tests the <see cref="Color"/> struct.
/// Tests the <see cref="Bgra"/> struct.
/// </summary>
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);
}
}

Loading…
Cancel
Save