Browse Source

Make things more testable.

Former-commit-id: 53bdb09258dc39b852dda3679b991887e8204f98
Former-commit-id: 64f1fe6f18af8d8bb053c59e9fa999cf7c675524
Former-commit-id: b889f3dc5a64b8968a52d0a5cf7af2d9cf37579c
af/merge-core
James Jackson-South 11 years ago
parent
commit
f15f1323c0
  1. 49
      src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
  2. 19
      src/ImageProcessor/Formats/Bmp/BmpFormat.cs
  3. 31
      src/ImageProcessor/Formats/Gif/GifEncoder.cs
  4. 19
      src/ImageProcessor/Formats/Gif/GifFormat.cs
  5. 7
      src/ImageProcessor/Formats/IImageEncoder.cs
  6. 16
      src/ImageProcessor/Formats/IImageFormat.cs
  7. 42
      src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
  8. 19
      src/ImageProcessor/Formats/Jpg/JpegFormat.cs
  9. 60
      src/ImageProcessor/Formats/Png/PngEncoder.cs
  10. 19
      src/ImageProcessor/Formats/Png/PngFormat.cs
  11. 95
      src/ImageProcessor/IImage.cs
  12. 90
      src/ImageProcessor/IImageBase.cs
  13. 155
      src/ImageProcessor/Image.cs
  14. 4
      src/ImageProcessor/ImageBase.cs
  15. 7
      src/ImageProcessor/ImageProcessor.csproj
  16. 14
      tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

49
src/ImageProcessor/Formats/Bmp/BmpEncoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="BmpEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Image encoder for writing an image to a stream as a Windows bitmap.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -22,41 +17,31 @@ namespace ImageProcessor.Formats
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
/// <remarks>Png is a lossless format so this is not used in this encoder.</remarks>
/// <remarks>Bitmap is a lossless format so this is not used in this encoder.</remarks>
public int Quality { get; set; }
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
public string Extension => "BMP";
/// <inheritdoc/>
public string MimeType => "image/bmp";
/// <summary>
/// Returns a value indicating whether the <see cref="IImageEncoder"/> 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>
/// <inheritdoc/>
public string Extension => "bmp";
/// <inheritdoc/>
public bool IsSupportedFileExtension(string extension)
{
Guard.NotNullOrEmpty(extension, "extension");
Guard.NotNullOrEmpty(extension, nameof(extension));
extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("BMP", StringComparison.OrdinalIgnoreCase)
|| extension.Equals("DIP", StringComparison.OrdinalIgnoreCase);
return extension.Equals(this.Extension, StringComparison.OrdinalIgnoreCase)
|| extension.Equals("dip", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <inheritdoc/>
public void Encode(ImageBase image, Stream stream)
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
int rowWidth = image.Width;
@ -108,7 +93,7 @@ namespace ImageProcessor.Formats
/// </param>
private static void WriteImage(BinaryWriter writer, ImageBase image)
{
// TODO: Check this as Bitmaps can have an alpha channel.
// TODO: Add more compression formats.
int amount = (image.Width * 3) % 4;
if (amount != 0)
{

19
src/ImageProcessor/Formats/Bmp/BmpFormat.cs

@ -0,0 +1,19 @@
// <copyright file="BmpFormat.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
/// <summary>
/// Encapsulates the means to encode and decode bitmap images.
/// </summary>
public class BmpFormat : IImageFormat
{
/// <inheritdoc/>
public IImageDecoder Decoder => new BmpDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new BmpEncoder();
}
}

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

@ -20,32 +20,22 @@ namespace ImageProcessor.Formats
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
public int Quality { get; set; }
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
public string Extension => "GIF";
/// <inheritdoc/>
public string Extension => "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>
/// <inheritdoc/>
public string MimeType => "image/gif";
/// <inheritdoc/>
public bool IsSupportedFileExtension(string extension)
{
Guard.NotNullOrEmpty(extension, "extension");
Guard.NotNullOrEmpty(extension, nameof(extension));
extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("GIF", StringComparison.OrdinalIgnoreCase);
return extension.Equals(this.Extension, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>.
/// </summary>
/// <param name="imageBase">The <see cref="ImageBase"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <inheritdoc/>
public void Encode(ImageBase imageBase, Stream stream)
{
Guard.NotNull(imageBase, nameof(imageBase));
@ -96,6 +86,9 @@ namespace ImageProcessor.Formats
/// <returns>The <see cref="GifLogicalScreenDescriptor"/></returns>
private bool WriteGlobalLogicalScreenDescriptor(Image image, Stream stream, int bitDepth)
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor
{
Width = (short)image.Width,

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

@ -0,0 +1,19 @@
// <copyright file="GifFormat.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
/// <summary>
/// Encapsulates the means to encode and decode gif images.
/// </summary>
public class GifFormat : IImageFormat
{
/// <inheritdoc/>
public IImageDecoder Decoder => new GifDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new GifEncoder();
}
}

7
src/ImageProcessor/Formats/IImageEncoder.cs

@ -22,6 +22,11 @@ namespace ImageProcessor.Formats
/// </summary>
int Quality { get; set; }
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// </summary>
string MimeType { get; }
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
@ -33,7 +38,7 @@ namespace ImageProcessor.Formats
/// </summary>
/// <param name="extension">The <see cref="string"/> containing the file extension.</param>
/// <returns>
/// True if the decoder supports the file extension; otherwise, false.
/// <c>True</c> if the decoder supports the file extension; otherwise, <c>false</c>.
/// </returns>
bool IsSupportedFileExtension(string extension);

16
src/ImageProcessor/Formats/IImageFormat.cs

@ -1,9 +1,23 @@
namespace ImageProcessor.Encoders
// <copyright file="IImageFormat.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
/// <summary>
/// Encapsulates a supported image format, providing means to encode and decode an image.
/// </summary>
public interface IImageFormat
{
/// <summary>
/// Gets the image encoder for encoding an image from a stream.
/// </summary>
IImageEncoder Encoder { get; }
/// <summary>
/// Gets the image decoder for decoding an image from a stream.
/// </summary>
IImageDecoder Decoder { get; }
}
}

42
src/ImageProcessor/Formats/Jpg/JpegEncoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="JpegEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="JpegEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encoder for writing the data image to a stream in jpg format.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -16,14 +11,18 @@ namespace ImageProcessor.Formats
using BitMiracle.LibJpeg;
/// <summary>
/// Encoder for writing the data image to a stream in jpg format.
/// Encoder for writing the data image to a stream in jpeg format.
/// </summary>
public class JpegEncoder : IImageEncoder
{
/// <summary>
/// The jpeg quality.
/// </summary>
private int quality = 100;
/// <summary>
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value>
public int Quality
@ -32,11 +31,14 @@ namespace ImageProcessor.Formats
set { this.quality = value.Clamp(1, 100); }
}
/// <inheritdoc/>
public string MimeType => "image/jpeg";
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
/// <value>The default file extension for this encoder.</value>
public string Extension => "JPG";
public string Extension => "jpg";
/// <summary>
/// Indicates if the image encoder supports the specified
@ -55,10 +57,14 @@ namespace ImageProcessor.Formats
{
Guard.NotNullOrEmpty(extension, "extension");
if (extension.StartsWith(".")) extension = extension.Substring(1);
return extension.Equals("JPG", StringComparison.OrdinalIgnoreCase) ||
extension.Equals("JPEG", StringComparison.OrdinalIgnoreCase) ||
extension.Equals("JFIF", StringComparison.OrdinalIgnoreCase);
if (extension.StartsWith("."))
{
extension = extension.Substring(1);
}
return extension.Equals(this.Extension, StringComparison.OrdinalIgnoreCase) ||
extension.Equals("jpeg", StringComparison.OrdinalIgnoreCase) ||
extension.Equals("jfif", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@ -76,8 +82,8 @@ namespace ImageProcessor.Formats
/// </exception>
public void Encode(ImageBase image, Stream stream)
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
int pixelWidth = image.Width;
int pixelHeight = image.Height;

19
src/ImageProcessor/Formats/Jpg/JpegFormat.cs

@ -0,0 +1,19 @@
// <copyright file="JpegFormat.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
/// <summary>
/// Encapsulates the means to encode and decode jpeg images.
/// </summary>
public class JpegFormat : IImageFormat
{
/// <inheritdoc/>
public IImageDecoder Decoder => new JpegDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new JpegEncoder();
}
}

60
src/ImageProcessor/Formats/Png/PngEncoder.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PngEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="PngEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Image encoder for writing image data to a stream in png format.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@ -40,6 +35,12 @@ namespace ImageProcessor.Formats
/// <remarks>Png is a lossless format so this is not used in this encoder.</remarks>
public int Quality { get; set; }
/// <inheritdoc/>
public string MimeType => "image/jpepngg";
/// <inheritdoc/>
public string Extension => "png";
/// <summary>
/// Gets or sets a value indicating whether this encoder
/// will write the image uncompressed the stream.
@ -55,7 +56,7 @@ namespace ImageProcessor.Formats
/// gamma information to the stream. The default value is false.
/// </summary>
/// <value>
/// <c>true</c> if this instance is writing gamma
/// <c>True</c> if this instance is writing gamma
/// information to the stream.; otherwise, <c>false</c>.
/// </value>
public bool IsWritingGamma { get; set; }
@ -68,50 +69,21 @@ namespace ImageProcessor.Formats
/// <value>The gamma value of the image.</value>
public double Gamma { get; set; }
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
/// <value>The default file extension for this encoder.</value>
public string Extension => "PNG";
/// <summary>
/// Indicates if the image encoder supports the specified
/// file extension.
/// </summary>
/// <param name="extension">The file extension.</param>
/// <returns><c>true</c>, if the encoder supports the specified
/// extensions; otherwise <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="extension"/>
/// is null (Nothing in Visual Basic).</exception>
/// <exception cref="ArgumentException"><paramref name="extension"/> is a string
/// of length zero or contains only blanks.</exception>
/// <inheritdoc/>
public bool IsSupportedFileExtension(string extension)
{
Guard.NotNullOrEmpty(extension, "extension");
Guard.NotNullOrEmpty(extension, nameof(extension));
extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("PNG", StringComparison.OrdinalIgnoreCase);
return extension.Equals(this.Extension, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Encodes the data of the specified image and writes the result to
/// the specified stream.
/// </summary>
/// <param name="image">The image, where the data should be get from.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The stream, where the image data should be written to.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para>
/// </exception>
/// <inheritdoc/>
public void Encode(ImageBase image, Stream stream)
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
// Write the png header.
stream.Write(

19
src/ImageProcessor/Formats/Png/PngFormat.cs

@ -0,0 +1,19 @@
// <copyright file="PngFormat.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Formats
{
/// <summary>
/// Encapsulates the means to encode and decode png images.
/// </summary>
public class PngFormat : IImageFormat
{
/// <inheritdoc/>
public IImageDecoder Decoder => new PngDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new PngEncoder();
}
}

95
src/ImageProcessor/IImage.cs

@ -0,0 +1,95 @@
// <copyright file="IImage.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageProcessor.Formats;
/// <summary>
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary>
public interface IImage : IImageBase
{
/// <summary>
/// Gets or sets the resolution of the image in x- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in x- direction.</value>
double HorizontalResolution { get; set; }
/// <summary>
/// Gets or sets the resolution of the image in y- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in y- direction.</value>
double VerticalResolution { get; set; }
/// <summary>
/// Gets the width of the image in inches. It is calculated as the width of the image
/// in pixels multiplied with the density. When the density is equals or less than zero
/// the default value is used.
/// </summary>
/// <value>The width of the image in inches.</value>
double InchWidth { get; }
/// <summary>
/// Gets the height of the image in inches. It is calculated as the height of the image
/// in pixels multiplied with the density. When the density is equals or less than zero
/// the default value is used.
/// </summary>
/// <value>The height of the image in inches.</value>
double InchHeight { get; }
/// <summary>
/// Gets a value indicating whether this image is animated.
/// </summary>
/// <value>
/// <c>True</c> if this image is animated; otherwise, <c>false</c>.
/// </value>
bool IsAnimated { get; }
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>0 means to repeat indefinitely.</remarks>
/// </summary>
ushort RepeatCount { get; set; }
/// <summary>
/// Gets the currently loaded image format.
/// </summary>
IImageFormat CurrentImageFormat { get; }
/// <summary>
/// Gets the other frames for the animation.
/// </summary>
/// <value>The list of frame images.</value>
IList<ImageFrame> Frames { get; }
/// <summary>
/// Gets the list of properties for storing meta information about this image.
/// </summary>
/// <value>A list of image properties.</value>
IList<ImageProperty> Properties { get; }
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
void Save(Stream stream);
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image as.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
void Save(Stream stream, IImageFormat format);
}
}

90
src/ImageProcessor/IImageBase.cs

@ -0,0 +1,90 @@
// <copyright file="IImageBase.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
public interface IImageBase
{
/// <summary>
/// Gets the image pixels as byte array.
/// </summary>
/// <remarks>
/// The returned array has a length of Width * Height * 4 bytes
/// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order.
/// </remarks>
byte[] Pixels { get; }
/// <summary>
/// Gets the width in pixels.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height in pixels.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
double PixelRatio { get; }
/// <summary>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
Rectangle Bounds { get; }
/// <summary>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
int Quality { get; set; }
/// <summary>
/// Gets or sets the frame delay for animated images.
/// 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.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
int FrameDelay { get; set; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Bgra"/> at the specified position.</returns>
Bgra this[int x, int y] { get; set; }
/// <summary>
/// Sets the pixel array of the image.
/// </summary>
/// <param name="width">
/// The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple
/// of four, width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height * 4.
/// </exception>
void SetPixels(int width, int height, byte[] pixels);
}
}

155
src/ImageProcessor/Image.cs

@ -21,50 +21,37 @@ namespace ImageProcessor
using Formats;
/// <summary>
/// Image class which stores the pixels and provides common functionality
/// such as loading images from files and streams or operation like resizing or cropping.
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary>
/// <remarks>
/// The image data is always stored in BGRA format, where the blue, green, red, and
/// The image data is always stored in BGRA format, where the blue, green, red, and
/// alpha values are simple bytes.
/// </remarks>
[DebuggerDisplay("Image: {Width}x{Height}")]
public class Image : ImageBase
public class Image : ImageBase, IImage
{
/// <summary>
/// The default horizontal resolution value (dots per inch) in x direction.
/// The default value is 96 dots per inch.
/// The default horizontal resolution value (dots per inch) in x direction.
/// <remarks>The default value is 96 dots per inch.</remarks>
/// </summary>
public const double DefaultHorizontalResolution = 96;
/// <summary>
/// The default vertical resolution value (dots per inch) in y direction.
/// The default value is 96 dots per inch.
/// The default vertical resolution value (dots per inch) in y direction.
/// <remarks>The default value is 96 dots per inch.</remarks>
/// </summary>
public const double DefaultVerticalResolution = 96;
/// <summary>
/// The default collection of <see cref="IImageDecoder"/>.
/// The default collection of <see cref="IImageFormat"/>.
/// </summary>
private static readonly Lazy<List<IImageDecoder>> DefaultDecoders =
new Lazy<List<IImageDecoder>>(() => new List<IImageDecoder>
private static readonly Lazy<List<IImageFormat>> DefaultFormats =
new Lazy<List<IImageFormat>>(() => new List<IImageFormat>
{
new BmpDecoder(),
new JpegDecoder(),
new PngDecoder(),
new GifDecoder(),
});
/// <summary>
/// The default collection of <see cref="IImageEncoder"/>.
/// </summary>
private static readonly Lazy<List<IImageEncoder>> DefaultEncoders =
new Lazy<List<IImageEncoder>>(() => new List<IImageEncoder>
{
new BmpEncoder(),
new JpegEncoder(),
new PngEncoder(),
new GifEncoder(),
new BmpFormat(),
new JpegFormat(),
new PngFormat(),
new GifFormat(),
});
/// <summary>
@ -94,8 +81,7 @@ namespace ImageProcessor
/// by making a copy from another image.
/// </summary>
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="ArgumentNullException"><paramref name="other"/> is null
/// (Nothing in Visual Basic).</exception>
/// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image other)
: base(other)
{
@ -119,10 +105,11 @@ namespace ImageProcessor
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <exception cref="ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.Load(stream, Decoders);
this.Load(stream, Formats);
}
/// <summary>
@ -131,45 +118,28 @@ namespace ImageProcessor
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="decoders">
/// The collection of <see cref="IImageDecoder"/>.
/// <param name="formats">
/// The collection of <see cref="IImageFormat"/>.
/// </param>
public Image(Stream stream, params IImageDecoder[] decoders)
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public Image(Stream stream, params IImageFormat[] formats)
{
Guard.NotNull(stream, "stream");
this.Load(stream, decoders);
Guard.NotNull(stream, nameof(stream));
this.Load(stream, formats);
}
/// <summary>
/// Gets a list of default decoders.
/// </summary>
public static IList<IImageDecoder> Decoders => DefaultDecoders.Value;
/// <summary>
/// Gets a list of default encoders.
/// Gets a list of supported image formats.
/// </summary>
public static IList<IImageEncoder> Encoders => DefaultEncoders.Value;
public static IList<IImageFormat> Formats => DefaultFormats.Value;
/// <summary>
/// Gets or sets the resolution of the image in x- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in x- direction.</value>
/// <inheritdoc/>
public double HorizontalResolution { get; set; }
/// <summary>
/// Gets or sets the resolution of the image in y- direction. It is defined as
/// number of dots per inch and should be an positive value.
/// </summary>
/// <value>The density of the image in y- direction.</value>
/// <inheritdoc/>
public double VerticalResolution { get; set; }
/// <summary>
/// Gets the width of the image in inches. It is calculated as the width of the image
/// in pixels multiplied with the density. When the density is equals or less than zero
/// the default value is used.
/// </summary>
/// <value>The width of the image in inches.</value>
/// <inheritdoc/>
public double InchWidth
{
get
@ -185,12 +155,7 @@ namespace ImageProcessor
}
}
/// <summary>
/// Gets the height of the image in inches. It is calculated as the height of the image
/// in pixels multiplied with the density. When the density is equals or less than zero
/// the default value is used.
/// </summary>
/// <value>The height of the image in inches.</value>
/// <inheritdoc/>
public double InchHeight
{
get
@ -206,45 +171,48 @@ namespace ImageProcessor
}
}
/// <summary>
/// Gets a value indicating whether this image is animated.
/// </summary>
/// <value>
/// <c>true</c> if this image is animated; otherwise, <c>false</c>.
/// </value>
/// <inheritdoc/>
public bool IsAnimated => this.Frames.Count > 0;
/// <summary>
/// Gets or sets the number of times any animation is repeated.
/// <remarks>0 means to repeat indefinitely.</remarks>
/// </summary>
/// <inheritdoc/>
public ushort RepeatCount { get; set; }
/// <summary>
/// Gets the other frames for the animation.
/// </summary>
/// <value>The list of frame images.</value>
/// <inheritdoc/>
public IList<ImageFrame> Frames { get; } = new List<ImageFrame>();
/// <summary>
/// Gets the list of properties for storing meta information about this image.
/// </summary>
/// <value>A list of image properties.</value>
/// <inheritdoc/>
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
/// <inheritdoc/>
public IImageFormat CurrentImageFormat { get; private set; }
/// <inheritdoc/>
public void Save(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.CurrentImageFormat.Encoder.Encode(this, stream);
}
/// <inheritdoc/>
public void Save(Stream stream, IImageFormat format)
{
Guard.NotNull(stream, nameof(stream));
format.Encoder.Encode(this, stream);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="decoders">
/// The collection of <see cref="IImageDecoder"/>.
/// <param name="formats">
/// The collection of <see cref="IImageFormat"/>.
/// </param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
private void Load(Stream stream, IList<IImageDecoder> decoders)
private void Load(Stream stream, IList<IImageFormat> formats)
{
try
{
@ -258,9 +226,9 @@ namespace ImageProcessor
throw new NotSupportedException("The stream does not support seeking.");
}
if (decoders.Count > 0)
if (formats.Count > 0)
{
int maxHeaderSize = decoders.Max(x => x.HeaderSize);
int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize);
if (maxHeaderSize > 0)
{
byte[] header = new byte[maxHeaderSize];
@ -268,21 +236,22 @@ namespace ImageProcessor
stream.Read(header, 0, maxHeaderSize);
stream.Position = 0;
IImageDecoder decoder = decoders.FirstOrDefault(x => x.IsSupportedFileFormat(header));
if (decoder != null)
IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header));
if (format != null)
{
decoder.Decode(this, stream);
format.Decoder.Decode(this, stream);
this.CurrentImageFormat = format;
return;
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageDecoder decoder in decoders)
foreach (IImageFormat format in formats)
{
stringBuilder.AppendLine("-" + decoder);
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());

4
src/ImageProcessor/ImageBase.cs

@ -14,10 +14,10 @@ namespace ImageProcessor
using System;
/// <summary>
/// The base class of all images. Encapsulates the basic properties and methods
/// The base class of all images. Encapsulates the basic properties and methods
/// required to manipulate images.
/// </summary>
public abstract class ImageBase
public abstract class ImageBase : IImageBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class.

7
src/ImageProcessor/ImageProcessor.csproj

@ -47,11 +47,13 @@
<Compile Include="Common\Extensions\ComparableExtensions.cs" />
<Compile Include="Formats\Bmp\BmpCompression.cs" />
<Compile Include="Formats\Bmp\BmpFileHeader.cs" />
<Compile Include="Formats\Bmp\BmpFormat.cs" />
<Compile Include="Formats\Bmp\BmpInfoHeader.cs" />
<Compile Include="Formats\Bmp\BmpDecoder.cs" />
<Compile Include="Formats\Bmp\BmpDecoderCore.cs" />
<Compile Include="Formats\Bmp\BmpEncoder.cs" />
<Compile Include="Formats\Gif\BitEncoder.cs" />
<Compile Include="Formats\Gif\GifFormat.cs" />
<Compile Include="Formats\Gif\LzwEncoder.cs" />
<Compile Include="Formats\Gif\GifConstants.cs" />
<Compile Include="Formats\Gif\GifDecoderCore.cs" />
@ -60,6 +62,8 @@
<Compile Include="Formats\Gif\Quantizer\IQuantizer.cs" />
<Compile Include="Formats\Gif\Quantizer\OctreeQuantizer.cs" />
<Compile Include="Formats\Gif\Quantizer\Quantizer.cs" />
<Compile Include="Formats\IImageFormat.cs" />
<Compile Include="Formats\Jpg\JpegFormat.cs" />
<Compile Include="Formats\Jpg\JpegDecoder.cs" />
<Compile Include="Formats\Jpg\JpegEncoder.cs" />
<Compile Include="Formats\Jpg\LibJpeg\BitmapDestination.cs" />
@ -137,6 +141,7 @@
<Compile Include="Formats\Jpg\LibJpeg\Sample.cs" />
<Compile Include="Formats\Jpg\LibJpeg\SampleRow.cs" />
<Compile Include="Formats\Jpg\LibJpeg\Utils.cs" />
<Compile Include="Formats\Png\PngFormat.cs" />
<Compile Include="Formats\Png\PngDecoder.cs" />
<Compile Include="Formats\Png\PngDecoderCore.cs" />
<Compile Include="Formats\Png\PngEncoder.cs" />
@ -148,6 +153,8 @@
<Compile Include="Formats\Png\GrayscaleReader.cs" />
<Compile Include="Formats\Png\IColorReader.cs" />
<Compile Include="Formats\Png\PngHeader.cs" />
<Compile Include="IImage.cs" />
<Compile Include="IImageBase.cs" />
<Compile Include="ImageProperty.cs" />
<Compile Include="ImageFrame.cs" />
<Compile Include="Colors\Bgra.cs" />

14
tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

@ -29,15 +29,14 @@
FileStream stream = File.OpenRead(filename);
Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream);
string encodedFilename = "Encoded/" + Path.GetFileName(filename);
//if (!image.IsAnimated)
//{
using (FileStream output = File.OpenWrite(encodedFilename))
{
IImageEncoder encoder = Image.Encoders.First(e => e.IsSupportedFileExtension(Path.GetExtension(filename)));
encoder.Encode(image, output);
image.Save(output);
}
//}
//else
@ -62,8 +61,8 @@
[Theory]
[InlineData("../../TestImages/Formats/Jpg/Backdrop.jpg")]
//[InlineData("../../TestImages/Formats/Bmp/Car.bmp")]
//[InlineData("../../TestImages/Formats/Png/cmyk.png")]
[InlineData("../../TestImages/Formats/Bmp/Car.bmp")]
[InlineData("../../TestImages/Formats/Png/cmyk.png")]
public void QuantizedImageShouldPreserveMaximumColorPrecision(string filename)
{
if (!Directory.Exists("Quantized"))
@ -74,11 +73,10 @@
Image image = new Image(File.OpenRead(filename));
IQuantizer quantizer = new OctreeQuantizer();
QuantizedImage quantizedImage = quantizer.Quantize(image);
var pixel = quantizedImage.Pixels;
using (FileStream output = File.OpenWrite($"Quantized/{ Path.GetFileName(filename) }"))
{
IImageEncoder encoder = Image.Encoders.First(e => e.IsSupportedFileExtension(Path.GetExtension(filename)));
encoder.Encode(quantizedImage.ToImage(), output);
quantizedImage.ToImage().Save(output, image.CurrentImageFormat);
}
}
}

Loading…
Cancel
Save