Browse Source

Moar gif updates

Former-commit-id: e3548b3f5fb82d262a1054c6d2ff89c24f86206d
Former-commit-id: d9ba2a7a4c74231ba7b8f7c90a87567c41b0605a
Former-commit-id: a6b6bac2b10088c73838b5177636b5c9db6424c4
pull/17/head
James Jackson-South 11 years ago
parent
commit
82d65ea57f
  1. 68
      src/ImageProcessor/Formats/Gif/GifConstants.cs
  2. 8
      src/ImageProcessor/Formats/Gif/GifDecoder.cs
  3. 47
      src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
  4. 109
      src/ImageProcessor/Formats/Gif/GifEncoder.cs
  5. 1
      src/ImageProcessor/Formats/Gif/GifGraphicsControlExtension.cs
  6. 35
      src/ImageProcessor/Formats/Gif/LzwEncoder.cs
  7. 12
      src/ImageProcessor/Image.cs
  8. 4
      src/ImageProcessor/ImageBase.cs
  9. 5
      tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

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

@ -0,0 +1,68 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GifConstants.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Constants that define specific points within a gif.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
/// <summary>
/// Constants that define specific points within a gif.
/// </summary>
internal sealed class GifConstants
{
/// <summary>
/// The maximum comment length.
/// </summary>
public const int MaxCommentLength = 1024 * 8;
/// <summary>
/// The extension block introducer <value>!</value>.
/// </summary>
public const byte ExtensionIntroducer = 0x21;
/// <summary>
/// The terminator.
/// </summary>
public const byte Terminator = 0;
/// <summary>
/// The image label introducer <value>,</value>.
/// </summary>
public const byte ImageLabel = 0x2C;
/// <summary>
/// The end introducer trailer <value>;</value>.
/// </summary>
public const byte EndIntroducer = 0x3B;
/// <summary>
/// The application extension label.
/// </summary>
public const byte ApplicationExtensionLabel = 0xFF;
/// <summary>
/// The comment label.
/// </summary>
public const byte CommentLabel = 0xFE;
/// <summary>
/// The image descriptor label <value>,</value>.
/// </summary>
public const byte ImageDescriptorLabel = 0x2C;
/// <summary>
/// The plain text label.
/// </summary>
public const byte PlainTextLabel = 0x01;
/// <summary>
/// The graphic control label.
/// </summary>
public const byte GraphicControlLabel = 0xF9;
}
}

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

@ -24,6 +24,9 @@ namespace ImageProcessor.Formats
/// <value>The size of the header.</value> /// <value>The size of the header.</value>
public int HeaderSize => 6; public int HeaderSize => 6;
internal GifDecoderCore CoreDecoder { get; private set; }
/// <summary> /// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified /// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header. /// file header.
@ -34,7 +37,7 @@ namespace ImageProcessor.Formats
/// </returns> /// </returns>
public bool IsSupportedFileExtension(string extension) public bool IsSupportedFileExtension(string extension)
{ {
Guard.NotNullOrEmpty(extension, "extension"); Guard.NotNullOrEmpty(extension, nameof(extension));
extension = extension.StartsWith(".") ? extension.Substring(1) : extension; extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("GIF", StringComparison.OrdinalIgnoreCase); return extension.Equals("GIF", StringComparison.OrdinalIgnoreCase);
@ -66,7 +69,8 @@ namespace ImageProcessor.Formats
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream) public void Decode(Image image, Stream stream)
{ {
new GifDecoderCore().Decode(image, stream); this.CoreDecoder = new GifDecoderCore();
this.CoreDecoder.Decode(image, stream);
} }
} }
} }

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

@ -24,8 +24,9 @@
private Stream currentStream; private Stream currentStream;
private byte[] globalColorTable; private byte[] globalColorTable;
private byte[] currentFrame; private byte[] currentFrame;
private GifLogicalScreenDescriptor logicalScreenDescriptor;
private GifGraphicsControlExtension graphicsControlExtension; internal GifLogicalScreenDescriptor LogicalScreenDescriptor { get; set; }
internal GifGraphicsControlExtension GraphicsControlExtension { get; set; }
public void Decode(Image image, Stream stream) public void Decode(Image image, Stream stream)
{ {
@ -37,16 +38,16 @@
this.currentStream.Seek(6, SeekOrigin.Current); this.currentStream.Seek(6, SeekOrigin.Current);
this.ReadLogicalScreenDescriptor(); this.ReadLogicalScreenDescriptor();
if (this.logicalScreenDescriptor.GlobalColorTableFlag) if (this.LogicalScreenDescriptor.GlobalColorTableFlag)
{ {
this.globalColorTable = new byte[this.logicalScreenDescriptor.GlobalColorTableSize * 3]; this.globalColorTable = new byte[this.LogicalScreenDescriptor.GlobalColorTableSize * 3];
// Read the global color table from the stream // Read the global color table from the stream
stream.Read(this.globalColorTable, 0, this.globalColorTable.Length); stream.Read(this.globalColorTable, 0, this.globalColorTable.Length);
} }
int nextFlag = stream.ReadByte(); int nextFlag = stream.ReadByte();
while (nextFlag != 0) while (nextFlag != Terminator)
{ {
if (nextFlag == ImageLabel) if (nextFlag == ImageLabel)
{ {
@ -89,7 +90,7 @@
byte packed = buffer[1]; byte packed = buffer[1];
this.graphicsControlExtension = new GifGraphicsControlExtension this.GraphicsControlExtension = new GifGraphicsControlExtension
{ {
DelayTime = BitConverter.ToInt16(buffer, 2), DelayTime = BitConverter.ToInt16(buffer, 2),
TransparencyIndex = buffer[4], TransparencyIndex = buffer[4],
@ -134,7 +135,7 @@
byte packed = buffer[4]; byte packed = buffer[4];
this.logicalScreenDescriptor = new GifLogicalScreenDescriptor this.LogicalScreenDescriptor = new GifLogicalScreenDescriptor
{ {
Width = BitConverter.ToInt16(buffer, 0), Width = BitConverter.ToInt16(buffer, 0),
Height = BitConverter.ToInt16(buffer, 2), Height = BitConverter.ToInt16(buffer, 2),
@ -144,16 +145,16 @@
GlobalColorTableSize = 2 << (packed & 0x07) GlobalColorTableSize = 2 << (packed & 0x07)
}; };
if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4) if (this.LogicalScreenDescriptor.GlobalColorTableSize > 255 * 4)
{ {
throw new ImageFormatException( throw new ImageFormatException(
$"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); $"Invalid gif colormap size '{this.LogicalScreenDescriptor.GlobalColorTableSize}'");
} }
if (this.logicalScreenDescriptor.Width > ImageBase.MaxWidth || this.logicalScreenDescriptor.Height > ImageBase.MaxHeight) if (this.LogicalScreenDescriptor.Width > ImageBase.MaxWidth || this.LogicalScreenDescriptor.Height > ImageBase.MaxHeight)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); $"The input gif '{this.LogicalScreenDescriptor.Width}x{this.LogicalScreenDescriptor.Height}' is bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
} }
} }
@ -232,8 +233,8 @@
private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor)
{ {
int imageWidth = this.logicalScreenDescriptor.Width; int imageWidth = this.LogicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.LogicalScreenDescriptor.Height;
if (this.currentFrame == null) if (this.currentFrame == null)
{ {
@ -242,8 +243,8 @@
byte[] lastFrame = null; byte[] lastFrame = null;
if (this.graphicsControlExtension != null && if (this.GraphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{ {
lastFrame = new byte[imageWidth * imageHeight * 4]; lastFrame = new byte[imageWidth * imageHeight * 4];
@ -299,9 +300,9 @@
index = indices[i]; index = indices[i];
if (this.graphicsControlExtension == null || if (this.GraphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false || this.GraphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index) this.GraphicsControlExtension.TransparencyIndex != index)
{ {
this.currentFrame[offset * 4 + 0] = colorTable[index * 3 + 2]; this.currentFrame[offset * 4 + 0] = colorTable[index * 3 + 2];
this.currentFrame[offset * 4 + 1] = colorTable[index * 3 + 1]; this.currentFrame[offset * 4 + 1] = colorTable[index * 3 + 1];
@ -324,9 +325,9 @@
currentImage = this.image; currentImage = this.image;
currentImage.SetPixels(imageWidth, imageHeight, pixels); currentImage.SetPixels(imageWidth, imageHeight, pixels);
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) if (this.GraphicsControlExtension != null && this.GraphicsControlExtension.DelayTime > 0)
{ {
this.image.FrameDelay = this.graphicsControlExtension.DelayTime; this.image.FrameDelay = this.GraphicsControlExtension.DelayTime;
} }
} }
else else
@ -339,9 +340,9 @@
this.image.Frames.Add(frame); this.image.Frames.Add(frame);
} }
if (this.graphicsControlExtension != null) if (this.GraphicsControlExtension != null)
{ {
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) if (this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
{ {
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
{ {
@ -356,7 +357,7 @@
} }
} }
} }
else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) else if (this.GraphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{ {
this.currentFrame = lastFrame; this.currentFrame = lastFrame;
} }

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

@ -11,7 +11,7 @@ namespace ImageProcessor.Formats
/// </summary> /// </summary>
private int quality = 256; private int quality = 256;
private ImageBase image; private Image image;
/// <summary> /// <summary>
/// Gets or sets the quality of output for images. /// Gets or sets the quality of output for images.
@ -58,48 +58,65 @@ namespace ImageProcessor.Formats
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode(ImageBase image, Stream stream) public void Encode(ImageBase image, Stream stream)
{ {
Guard.NotNull(image, "image"); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, nameof(stream));
this.image = image; this.image = (Image)image;
// Write the header. // Write the header.
// File Header signature and version. // File Header signature and version.
this.WriteString(stream, "GIF"); this.WriteString(stream, "GIF");
this.WriteString(stream, "89a"); this.WriteString(stream, "89a");
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor int bitdepth = this.GetBitsNeededForColorDepth(this.Quality) - 1;
{
Width = (short)image.Width,
Height = (short)image.Height,
GlobalColorTableFlag = true,
GlobalColorTableSize = this.Quality
};
this.WriteGlobalLogicalScreenDescriptor(stream, descriptor);
this.WriteGlobalLogicalScreenDescriptor(stream, bitdepth);
foreach (ImageFrame frame in this.image.Frames)
{
this.WriteColorTable(stream, bitdepth);
this.WriteGraphicalControlExtension(stream);
}
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
private void WriteGlobalLogicalScreenDescriptor(Stream stream, GifLogicalScreenDescriptor descriptor) private void WriteGlobalLogicalScreenDescriptor(Stream stream, int bitDepth)
{ {
IImageDecoder decoder = ((Image)this.image).Decoder;
GifLogicalScreenDescriptor descriptor;
// Try and grab an existing descriptor.
if (decoder.GetType() == typeof(GifDecoder))
{
// Ensure the dimensions etc are up to date.
descriptor = ((GifDecoder)decoder).CoreDecoder.LogicalScreenDescriptor;
descriptor.Width = (short)this.image.Width;
descriptor.Height = (short)this.image.Height;
descriptor.GlobalColorTableSize = this.Quality;
}
else
{
descriptor = new GifLogicalScreenDescriptor
{
Width = (short)this.image.Width,
Height = (short)this.image.Height,
GlobalColorTableFlag = true,
GlobalColorTableSize = this.Quality
};
}
this.WriteShort(stream, descriptor.Width); this.WriteShort(stream, descriptor.Width);
this.WriteShort(stream, descriptor.Width); this.WriteShort(stream, descriptor.Width);
int size = descriptor.GlobalColorTableSize;
int bitdepth = this.GetBitsNeededForColorDepth(size) - 1;
int packed = 0x80 | // 1 : Global color table flag = 1 (GCT used) int packed = 0x80 | // 1 : Global color table flag = 1 (GCT used)
0x70 | // 2-4 : color resolution 0x70 | // 2-4 : color resolution
0x00 | // 5 : GCT sort flag = 0 0x00 | // 5 : GCT sort flag = 0
bitdepth; // 6-8 : GCT size assume 1:1 bitDepth; // 6-8 : GCT size assume 1:1
this.WriteByte(stream, packed); this.WriteByte(stream, packed);
this.WriteByte(stream, descriptor.BackgroundColorIndex); // Background Color Index this.WriteByte(stream, descriptor.BackgroundColorIndex); // Background Color Index
this.WriteByte(stream, descriptor.PixelAspectRatio); // Pixel aspect ratio this.WriteByte(stream, descriptor.PixelAspectRatio); // Pixel aspect ratio
// Write the global color table.
this.WriteColorTable(stream, bitdepth);
} }
private void WriteColorTable(Stream stream, int bitDepth) private void WriteColorTable(Stream stream, int bitDepth)
@ -127,6 +144,54 @@ namespace ImageProcessor.Formats
stream.Write(colorTable, 0, colorTableLength); stream.Write(colorTable, 0, colorTableLength);
} }
private void WriteGraphicalControlExtension(Stream stream)
{
Image i = ((Image)this.image);
IImageDecoder decoder = i.Decoder;
GifGraphicsControlExtension extension;
// Try and grab an existing descriptor.
// TODO: Check whether we need to.
if (decoder.GetType() == typeof(GifDecoder))
{
// Ensure the dimensions etc are up to date.
extension = ((GifDecoder)decoder).CoreDecoder.GraphicsControlExtension;
extension.TransparencyFlag = this.Quality > 1;
extension.TransparencyIndex = this.Quality - 1;
extension.DelayTime = i.FrameDelay;
}
else
{
bool hasTransparent = this.Quality > 1;
DisposalMethod disposalMethod = hasTransparent
? DisposalMethod.RestoreToBackground
: DisposalMethod.Unspecified;
extension = new GifGraphicsControlExtension()
{
DisposalMethod = disposalMethod,
TransparencyFlag = hasTransparent,
TransparencyIndex = this.Quality - 1, // Quantizer set last as transparent.
DelayTime = i.FrameDelay
};
}
this.WriteByte(stream, GifConstants.ExtensionIntroducer);
this.WriteByte(stream, GifConstants.GraphicControlLabel);
this.WriteByte(stream, 4); // Size
int packed = 0 | // 1-3 : Reserved
(int)extension.DisposalMethod | // 4-6 : Disposal
0 | // 7 : User input - 0 = none
extension.TransparencyIndex;
this.WriteByte(stream, packed);
this.WriteShort(stream, extension.DelayTime);
this.WriteByte(stream, GifConstants.Terminator);
}
private void WriteApplicationExtension(Stream stream) private void WriteApplicationExtension(Stream stream)
{ {
// TODO: Implement // TODO: Implement
@ -146,7 +211,7 @@ namespace ImageProcessor.Formats
} }
/// <summary> /// <summary>
/// Writes a short to the given stream. /// Writes a byte to the given stream.
/// </summary> /// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param> /// <param name="value">The value to write.</param>

1
src/ImageProcessor/Formats/Gif/GifGraphicsControlExtension.cs

@ -42,7 +42,6 @@ namespace ImageProcessor.Formats
/// If not 0, this field specifies the number of hundredths (1/100) of a second to /// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream. /// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered. /// The clock starts ticking immediately after the graphic is rendered.
/// This field may be used in conjunction with the User Input Flag field.
/// </summary> /// </summary>
public int DelayTime { get; set; } public int DelayTime { get; set; }
} }

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

@ -63,7 +63,7 @@
// General DEFINEs // General DEFINEs
private const int BITS = 12; private const int Bits = 12;
private const int HSIZE = 5003; // 80% occupancy private const int HSIZE = 5003; // 80% occupancy
@ -79,12 +79,12 @@
// Joe Orost (decvax!vax135!petsd!joe) // Joe Orost (decvax!vax135!petsd!joe)
private int numberOfBits; // number of bits/code private int numberOfBits; // number of bits/code
private int maxbits = BITS; // user settable max # bits/code private int maxbits = Bits; // user settable max # bits/code
private int maxcode; // maximum code, given n_bits private int maxcode; // maximum code, given n_bits
private int maxmaxcode = 1 << BITS; // should NEVER generate this code private int maxmaxcode = 1 << Bits; // should NEVER generate this code
private int[] htab = new int[HSIZE]; private readonly int[] hashTable = new int[HSIZE];
private int[] codetab = new int[HSIZE]; private readonly int[] codeTable = new int[HSIZE];
private int hsize = HSIZE; // for dynamic table sizing private int hsize = HSIZE; // for dynamic table sizing
@ -168,7 +168,7 @@
{ {
for (int i = 0; i < hsize; ++i) for (int i = 0; i < hsize; ++i)
{ {
this.htab[i] = -1; this.hashTable[i] = -1;
} }
} }
@ -210,21 +210,21 @@
this.Output(this.ClearCode, outs); this.Output(this.ClearCode, outs);
// TODO: Refactor this. Goto is baaaaaaad! // TODO: Refactor this. Goto is baaaaaaad!
outer_loop: // outer_loop:
while ((c = this.NextPixel()) != EOF) while ((c = this.NextPixel()) != EOF)
{ {
fcode = (c << this.maxbits) + ent; fcode = (c << this.maxbits) + ent;
int i = c << hshift ^ ent; int i = c << hshift ^ ent;
if (this.htab[i] == fcode) if (this.hashTable[i] == fcode)
{ {
ent = this.codetab[i]; ent = this.codeTable[i];
continue; continue;
} }
// non-empty slot // non-empty slot
if (this.htab[i] >= 0) if (this.hashTable[i] >= 0)
{ {
disp = hsize_reg - i; // secondary hash (after G. Knott) disp = hsize_reg - i; // secondary hash (after G. Knott)
if (i == 0) if (i == 0)
@ -239,13 +239,14 @@
i += hsize_reg; i += hsize_reg;
} }
if (this.htab[i] == fcode) if (this.hashTable[i] == fcode)
{ {
ent = this.codetab[i]; ent = this.codeTable[i];
goto outer_loop; // goto outer_loop;
break;
} }
} }
while (this.htab[i] >= 0); while (this.hashTable[i] >= 0);
} }
this.Output(ent, outs); this.Output(ent, outs);
@ -253,8 +254,8 @@
if (this.freeEntry < this.maxmaxcode) if (this.freeEntry < this.maxmaxcode)
{ {
this.codetab[i] = this.freeEntry++; // code -> hashtable this.codeTable[i] = this.freeEntry++; // code -> hashtable
this.htab[i] = fcode; this.hashTable[i] = fcode;
} }
else else
{ {

12
src/ImageProcessor/Image.cs

@ -98,7 +98,7 @@ namespace ImageProcessor
public Image(Image other) public Image(Image other)
: base(other) : base(other)
{ {
Guard.NotNull(other, "other", "Other image cannot be null."); Guard.NotNull(other, nameof(other), "Other image cannot be null.");
foreach (ImageFrame frame in other.Frames) foreach (ImageFrame frame in other.Frames)
{ {
@ -120,7 +120,7 @@ namespace ImageProcessor
/// </param> /// </param>
public Image(Stream stream) public Image(Stream stream)
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, nameof(stream));
this.Load(stream, Decoders); this.Load(stream, Decoders);
} }
@ -154,9 +154,8 @@ namespace ImageProcessor
/// If not 0, this field specifies the number of hundredths (1/100) of a second to /// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream. /// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered. /// The clock starts ticking immediately after the graphic is rendered.
/// This field may be used in conjunction with the User Input Flag field.
/// </summary> /// </summary>
public int? FrameDelay { get; set; } public int FrameDelay { get; set; }
/// <summary> /// <summary>
/// Gets or sets the resolution of the image in x- direction. It is defined as /// Gets or sets the resolution of the image in x- direction. It is defined as
@ -240,6 +239,8 @@ namespace ImageProcessor
/// <value>A list of image properties.</value> /// <value>A list of image properties.</value>
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>(); public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
internal IImageDecoder Decoder { get; set; }
/// <summary> /// <summary>
/// Loads the image from the given stream. /// Loads the image from the given stream.
/// </summary> /// </summary>
@ -279,7 +280,8 @@ namespace ImageProcessor
IImageDecoder decoder = decoders.FirstOrDefault(x => x.IsSupportedFileFormat(header)); IImageDecoder decoder = decoders.FirstOrDefault(x => x.IsSupportedFileFormat(header));
if (decoder != null) if (decoder != null)
{ {
decoder.Decode(this, stream); this.Decoder = decoder;
this.Decoder.Decode(this, stream);
return; return;
} }
} }

4
src/ImageProcessor/ImageBase.cs

@ -40,8 +40,8 @@ namespace ImageProcessor
/// </exception> /// </exception>
protected ImageBase(int width, int height) protected ImageBase(int width, int height)
{ {
Guard.MustBeGreaterThan(width, 0, "width"); Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, "height"); Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;

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

@ -88,7 +88,7 @@ namespace ImageProcessor.Tests
} }
/// <summary> /// <summary>
/// Tests the implicit conversion from <see cref="YCbCr"/> to <see cref="Bgra"/>. /// Tests the implicit conversion from <see cref="Bgra"/> to <see cref="Hsv"/>.
/// </summary> /// </summary>
[Fact] [Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
@ -128,6 +128,9 @@ namespace ImageProcessor.Tests
Assert.Equal(80, hsv3.V, 1); Assert.Equal(80, hsv3.V, 1);
} }
/// <summary>
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Bgra"/>.
/// </summary>
[Fact] [Fact]
public void HsvToBgr() public void HsvToBgr()
{ {

Loading…
Cancel
Save