From f15f1323c0780b5ed87f3c876a078fb31a77869e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 4 Oct 2015 00:42:49 +1000 Subject: [PATCH] Make things more testable. Former-commit-id: 53bdb09258dc39b852dda3679b991887e8204f98 Former-commit-id: 64f1fe6f18af8d8bb053c59e9fa999cf7c675524 Former-commit-id: b889f3dc5a64b8968a52d0a5cf7af2d9cf37579c --- src/ImageProcessor/Formats/Bmp/BmpEncoder.cs | 49 ++---- src/ImageProcessor/Formats/Bmp/BmpFormat.cs | 19 +++ src/ImageProcessor/Formats/Gif/GifEncoder.cs | 31 ++-- src/ImageProcessor/Formats/Gif/GifFormat.cs | 19 +++ src/ImageProcessor/Formats/IImageEncoder.cs | 7 +- src/ImageProcessor/Formats/IImageFormat.cs | 16 +- src/ImageProcessor/Formats/Jpg/JpegEncoder.cs | 42 +++-- src/ImageProcessor/Formats/Jpg/JpegFormat.cs | 19 +++ src/ImageProcessor/Formats/Png/PngEncoder.cs | 60 ++----- src/ImageProcessor/Formats/Png/PngFormat.cs | 19 +++ src/ImageProcessor/IImage.cs | 95 +++++++++++ src/ImageProcessor/IImageBase.cs | 90 ++++++++++ src/ImageProcessor/Image.cs | 155 +++++++----------- src/ImageProcessor/ImageBase.cs | 4 +- src/ImageProcessor/ImageProcessor.csproj | 7 + .../Formats/EncoderDecoderTests.cs | 14 +- 16 files changed, 428 insertions(+), 218 deletions(-) create mode 100644 src/ImageProcessor/Formats/Bmp/BmpFormat.cs create mode 100644 src/ImageProcessor/Formats/Gif/GifFormat.cs create mode 100644 src/ImageProcessor/Formats/Jpg/JpegFormat.cs create mode 100644 src/ImageProcessor/Formats/Png/PngFormat.cs create mode 100644 src/ImageProcessor/IImage.cs create mode 100644 src/ImageProcessor/IImageBase.cs diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs index c94037fee..6a6f3f4f2 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Image encoder for writing an image to a stream as a Windows bitmap. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -22,41 +17,31 @@ namespace ImageProcessor.Formats /// /// Gets or sets the quality of output for images. /// - /// Png is a lossless format so this is not used in this encoder. + /// Bitmap is a lossless format so this is not used in this encoder. public int Quality { get; set; } - /// - /// Gets the default file extension for this encoder. - /// - public string Extension => "BMP"; + /// + public string MimeType => "image/bmp"; - /// - /// Returns a value indicating whether the supports the specified - /// file header. - /// - /// The containing the file extension. - /// - /// True if the decoder supports the file extension; otherwise, false. - /// + /// + public string Extension => "bmp"; + + /// 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); } - /// - /// Encodes the image to the specified stream from the . - /// - /// The to encode from. - /// The to encode the image data to. + /// 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 /// 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) { diff --git a/src/ImageProcessor/Formats/Bmp/BmpFormat.cs b/src/ImageProcessor/Formats/Bmp/BmpFormat.cs new file mode 100644 index 000000000..cd882469a --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpFormat.cs @@ -0,0 +1,19 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats +{ + /// + /// Encapsulates the means to encode and decode bitmap images. + /// + public class BmpFormat : IImageFormat + { + /// + public IImageDecoder Decoder => new BmpDecoder(); + + /// + public IImageEncoder Encoder => new BmpEncoder(); + } +} diff --git a/src/ImageProcessor/Formats/Gif/GifEncoder.cs b/src/ImageProcessor/Formats/Gif/GifEncoder.cs index 1870b1151..0a2318f45 100644 --- a/src/ImageProcessor/Formats/Gif/GifEncoder.cs +++ b/src/ImageProcessor/Formats/Gif/GifEncoder.cs @@ -20,32 +20,22 @@ namespace ImageProcessor.Formats /// For gifs the value ranges from 1 to 256. public int Quality { get; set; } - /// - /// Gets the default file extension for this encoder. - /// - public string Extension => "GIF"; + /// + public string Extension => "gif"; - /// - /// Returns a value indicating whether the supports the specified - /// file header. - /// - /// The containing the file extension. - /// - /// True if the decoder supports the file extension; otherwise, false. - /// + /// + public string MimeType => "image/gif"; + + /// 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); } - /// - /// Encodes the image to the specified stream from the . - /// - /// The to encode from. - /// The to encode the image data to. + /// public void Encode(ImageBase imageBase, Stream stream) { Guard.NotNull(imageBase, nameof(imageBase)); @@ -96,6 +86,9 @@ namespace ImageProcessor.Formats /// The 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, diff --git a/src/ImageProcessor/Formats/Gif/GifFormat.cs b/src/ImageProcessor/Formats/Gif/GifFormat.cs new file mode 100644 index 000000000..ccc3d5c99 --- /dev/null +++ b/src/ImageProcessor/Formats/Gif/GifFormat.cs @@ -0,0 +1,19 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats +{ + /// + /// Encapsulates the means to encode and decode gif images. + /// + public class GifFormat : IImageFormat + { + /// + public IImageDecoder Decoder => new GifDecoder(); + + /// + public IImageEncoder Encoder => new GifEncoder(); + } +} diff --git a/src/ImageProcessor/Formats/IImageEncoder.cs b/src/ImageProcessor/Formats/IImageEncoder.cs index ab62f3566..5414af88d 100644 --- a/src/ImageProcessor/Formats/IImageEncoder.cs +++ b/src/ImageProcessor/Formats/IImageEncoder.cs @@ -22,6 +22,11 @@ namespace ImageProcessor.Formats /// int Quality { get; set; } + /// + /// Gets the standard identifier used on the Internet to indicate the type of data that a file contains. + /// + string MimeType { get; } + /// /// Gets the default file extension for this encoder. /// @@ -33,7 +38,7 @@ namespace ImageProcessor.Formats /// /// The containing the file extension. /// - /// True if the decoder supports the file extension; otherwise, false. + /// True if the decoder supports the file extension; otherwise, false. /// bool IsSupportedFileExtension(string extension); diff --git a/src/ImageProcessor/Formats/IImageFormat.cs b/src/ImageProcessor/Formats/IImageFormat.cs index 596805877..8e0efc711 100644 --- a/src/ImageProcessor/Formats/IImageFormat.cs +++ b/src/ImageProcessor/Formats/IImageFormat.cs @@ -1,9 +1,23 @@ -namespace ImageProcessor.Encoders +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats { + /// + /// Encapsulates a supported image format, providing means to encode and decode an image. + /// public interface IImageFormat { + /// + /// Gets the image encoder for encoding an image from a stream. + /// IImageEncoder Encoder { get; } + /// + /// Gets the image decoder for decoding an image from a stream. + /// IImageDecoder Decoder { get; } } } diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs index c816e279e..575cfb6c9 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encoder for writing the data image to a stream in jpg format. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -16,14 +11,18 @@ namespace ImageProcessor.Formats using BitMiracle.LibJpeg; /// - /// Encoder for writing the data image to a stream in jpg format. + /// Encoder for writing the data image to a stream in jpeg format. /// public class JpegEncoder : IImageEncoder { + /// + /// The jpeg quality. + /// private int quality = 100; + /// - /// 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). /// /// The quality of the jpg image from 0 to 100. public int Quality @@ -32,11 +31,14 @@ namespace ImageProcessor.Formats set { this.quality = value.Clamp(1, 100); } } + /// + public string MimeType => "image/jpeg"; + /// /// Gets the default file extension for this encoder. /// /// The default file extension for this encoder. - public string Extension => "JPG"; + public string Extension => "jpg"; /// /// 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); } /// @@ -76,8 +82,8 @@ namespace ImageProcessor.Formats /// 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; diff --git a/src/ImageProcessor/Formats/Jpg/JpegFormat.cs b/src/ImageProcessor/Formats/Jpg/JpegFormat.cs new file mode 100644 index 000000000..390916221 --- /dev/null +++ b/src/ImageProcessor/Formats/Jpg/JpegFormat.cs @@ -0,0 +1,19 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats +{ + /// + /// Encapsulates the means to encode and decode jpeg images. + /// + public class JpegFormat : IImageFormat + { + /// + public IImageDecoder Decoder => new JpegDecoder(); + + /// + public IImageEncoder Encoder => new JpegEncoder(); + } +} diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index bc32c95c6..26f3fe89d 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright © James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Image encoder for writing image data to a stream in png format. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -40,6 +35,12 @@ namespace ImageProcessor.Formats /// Png is a lossless format so this is not used in this encoder. public int Quality { get; set; } + /// + public string MimeType => "image/jpepngg"; + + /// + public string Extension => "png"; + /// /// 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. /// /// - /// true if this instance is writing gamma + /// True if this instance is writing gamma /// information to the stream.; otherwise, false. /// public bool IsWritingGamma { get; set; } @@ -68,50 +69,21 @@ namespace ImageProcessor.Formats /// The gamma value of the image. public double Gamma { get; set; } - /// - /// Gets the default file extension for this encoder. - /// - /// The default file extension for this encoder. - public string Extension => "PNG"; - - /// - /// Indicates if the image encoder supports the specified - /// file extension. - /// - /// The file extension. - /// true, if the encoder supports the specified - /// extensions; otherwise false. - /// - /// - /// is null (Nothing in Visual Basic). - /// is a string - /// of length zero or contains only blanks. + /// 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); } - /// - /// Encodes the data of the specified image and writes the result to - /// the specified stream. - /// - /// The image, where the data should be get from. - /// Cannot be null (Nothing in Visual Basic). - /// The stream, where the image data should be written to. - /// Cannot be null (Nothing in Visual Basic). - /// - /// is null (Nothing in Visual Basic). - /// - or - - /// is null (Nothing in Visual Basic). - /// + /// 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( diff --git a/src/ImageProcessor/Formats/Png/PngFormat.cs b/src/ImageProcessor/Formats/Png/PngFormat.cs new file mode 100644 index 000000000..9e2bcfee2 --- /dev/null +++ b/src/ImageProcessor/Formats/Png/PngFormat.cs @@ -0,0 +1,19 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Formats +{ + /// + /// Encapsulates the means to encode and decode png images. + /// + public class PngFormat : IImageFormat + { + /// + public IImageDecoder Decoder => new PngDecoder(); + + /// + public IImageEncoder Encoder => new PngEncoder(); + } +} diff --git a/src/ImageProcessor/IImage.cs b/src/ImageProcessor/IImage.cs new file mode 100644 index 000000000..82c9710ec --- /dev/null +++ b/src/ImageProcessor/IImage.cs @@ -0,0 +1,95 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System; + using System.Collections.Generic; + using System.IO; + + using ImageProcessor.Formats; + + /// + /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. + /// + public interface IImage : IImageBase + { + /// + /// 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. + /// + /// The density of the image in x- direction. + double HorizontalResolution { get; set; } + + /// + /// 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. + /// + /// The density of the image in y- direction. + double VerticalResolution { get; set; } + + /// + /// 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. + /// + /// The width of the image in inches. + double InchWidth { get; } + + /// + /// 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. + /// + /// The height of the image in inches. + double InchHeight { get; } + + /// + /// Gets a value indicating whether this image is animated. + /// + /// + /// True if this image is animated; otherwise, false. + /// + bool IsAnimated { get; } + + /// + /// Gets or sets the number of times any animation is repeated. + /// 0 means to repeat indefinitely. + /// + ushort RepeatCount { get; set; } + + /// + /// Gets the currently loaded image format. + /// + IImageFormat CurrentImageFormat { get; } + + /// + /// Gets the other frames for the animation. + /// + /// The list of frame images. + IList Frames { get; } + + /// + /// Gets the list of properties for storing meta information about this image. + /// + /// A list of image properties. + IList Properties { get; } + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The stream to save the image to. + /// Thrown if the stream is null. + void Save(Stream stream); + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The stream to save the image to. + /// The format to save the image as. + /// Thrown if the stream is null. + void Save(Stream stream, IImageFormat format); + } +} \ No newline at end of file diff --git a/src/ImageProcessor/IImageBase.cs b/src/ImageProcessor/IImageBase.cs new file mode 100644 index 000000000..94912c887 --- /dev/null +++ b/src/ImageProcessor/IImageBase.cs @@ -0,0 +1,90 @@ +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System; + + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + public interface IImageBase + { + /// + /// Gets the image pixels as byte array. + /// + /// + /// 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. + /// + byte[] Pixels { get; } + + /// + /// Gets the width in pixels. + /// + int Width { get; } + + /// + /// Gets the height in pixels. + /// + int Height { get; } + + /// + /// Gets the pixel ratio made up of the width and height. + /// + double PixelRatio { get; } + + /// + /// Gets the representing the bounds of the image. + /// + Rectangle Bounds { get; } + + /// + /// Gets or sets th quality of the image. This affects the output quality of lossy image formats. + /// + int Quality { get; set; } + + /// + /// 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. + /// + int FrameDelay { get; set; } + + /// + /// Gets or sets the color of a pixel at the specified position. + /// + /// + /// The x-coordinate of the pixel. Must be greater + /// than zero and smaller than the width of the pixel. + /// + /// + /// The y-coordinate of the pixel. Must be greater + /// than zero and smaller than the width of the pixel. + /// + /// The at the specified position. + Bgra this[int x, int y] { get; set; } + + /// + /// Sets the pixel array of the image. + /// + /// + /// The new width of the image. Must be greater than zero. + /// The new height of the image. Must be greater than zero. + /// + /// The array with colors. Must be a multiple + /// of four, width and height. + /// + /// + /// Thrown if either or are less than or equal to 0. + /// + /// + /// Thrown if the length is not equal to Width * Height * 4. + /// + void SetPixels(int width, int height, byte[] pixels); + } +} diff --git a/src/ImageProcessor/Image.cs b/src/ImageProcessor/Image.cs index 8a4adb1f2..c2a5c8ebf 100644 --- a/src/ImageProcessor/Image.cs +++ b/src/ImageProcessor/Image.cs @@ -21,50 +21,37 @@ namespace ImageProcessor using Formats; /// - /// 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. /// /// - /// 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. /// [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase + public class Image : ImageBase, IImage { /// - /// 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. + /// The default value is 96 dots per inch. /// public const double DefaultHorizontalResolution = 96; /// - /// 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. + /// The default value is 96 dots per inch. /// public const double DefaultVerticalResolution = 96; /// - /// The default collection of . + /// The default collection of . /// - private static readonly Lazy> DefaultDecoders = - new Lazy>(() => new List + private static readonly Lazy> DefaultFormats = + new Lazy>(() => new List { - new BmpDecoder(), - new JpegDecoder(), - new PngDecoder(), - new GifDecoder(), - }); - - /// - /// The default collection of . - /// - private static readonly Lazy> DefaultEncoders = - new Lazy>(() => new List - { - new BmpEncoder(), - new JpegEncoder(), - new PngEncoder(), - new GifEncoder(), + new BmpFormat(), + new JpegFormat(), + new PngFormat(), + new GifFormat(), }); /// @@ -94,8 +81,7 @@ namespace ImageProcessor /// by making a copy from another image. /// /// The other image, where the clone should be made from. - /// is null - /// (Nothing in Visual Basic). + /// is null. public Image(Image other) : base(other) { @@ -119,10 +105,11 @@ namespace ImageProcessor /// /// The stream containing image information. /// + /// Thrown if the is null. public Image(Stream stream) { Guard.NotNull(stream, nameof(stream)); - this.Load(stream, Decoders); + this.Load(stream, Formats); } /// @@ -131,45 +118,28 @@ namespace ImageProcessor /// /// The stream containing image information. /// - /// - /// The collection of . + /// + /// The collection of . /// - public Image(Stream stream, params IImageDecoder[] decoders) + /// Thrown if the stream is null. + public Image(Stream stream, params IImageFormat[] formats) { - Guard.NotNull(stream, "stream"); - this.Load(stream, decoders); + Guard.NotNull(stream, nameof(stream)); + this.Load(stream, formats); } /// - /// Gets a list of default decoders. - /// - public static IList Decoders => DefaultDecoders.Value; - - /// - /// Gets a list of default encoders. + /// Gets a list of supported image formats. /// - public static IList Encoders => DefaultEncoders.Value; + public static IList Formats => DefaultFormats.Value; - /// - /// 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. - /// - /// The density of the image in x- direction. + /// public double HorizontalResolution { get; set; } - /// - /// 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. - /// - /// The density of the image in y- direction. + /// public double VerticalResolution { get; set; } - /// - /// 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. - /// - /// The width of the image in inches. + /// public double InchWidth { get @@ -185,12 +155,7 @@ namespace ImageProcessor } } - /// - /// 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. - /// - /// The height of the image in inches. + /// public double InchHeight { get @@ -206,45 +171,48 @@ namespace ImageProcessor } } - /// - /// Gets a value indicating whether this image is animated. - /// - /// - /// true if this image is animated; otherwise, false. - /// + /// public bool IsAnimated => this.Frames.Count > 0; - /// - /// Gets or sets the number of times any animation is repeated. - /// 0 means to repeat indefinitely. - /// + /// public ushort RepeatCount { get; set; } - /// - /// Gets the other frames for the animation. - /// - /// The list of frame images. + /// public IList Frames { get; } = new List(); - /// - /// Gets the list of properties for storing meta information about this image. - /// - /// A list of image properties. + /// public IList Properties { get; } = new List(); + /// + public IImageFormat CurrentImageFormat { get; private set; } + + /// + public void Save(Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + this.CurrentImageFormat.Encoder.Encode(this, stream); + } + + /// + public void Save(Stream stream, IImageFormat format) + { + Guard.NotNull(stream, nameof(stream)); + format.Encoder.Encode(this, stream); + } + /// /// Loads the image from the given stream. /// /// /// The stream containing image information. /// - /// - /// The collection of . + /// + /// The collection of . /// /// /// Thrown if the stream is not readable nor seekable. /// - private void Load(Stream stream, IList decoders) + private void Load(Stream stream, IList 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()); diff --git a/src/ImageProcessor/ImageBase.cs b/src/ImageProcessor/ImageBase.cs index c90516861..890f6c861 100644 --- a/src/ImageProcessor/ImageBase.cs +++ b/src/ImageProcessor/ImageBase.cs @@ -14,10 +14,10 @@ namespace ImageProcessor using System; /// - /// 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. /// - public abstract class ImageBase + public abstract class ImageBase : IImageBase { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 4fc9b385e..db3003fcf 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -47,11 +47,13 @@ + + @@ -60,6 +62,8 @@ + + @@ -137,6 +141,7 @@ + @@ -148,6 +153,8 @@ + + diff --git a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs index dae8f8763..aac27677d 100644 --- a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs +++ b/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); } } }