// // Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessor { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using Formats; /// /// 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 /// alpha values are simple bytes. /// [DebuggerDisplay("Image: {Width}x{Height}")] public class Image : ImageBase, IImage { /// /// 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. /// public const double DefaultVerticalResolution = 96; /// /// The default collection of . /// private static readonly Lazy> DefaultFormats = new Lazy>(() => new List { new BmpFormat(), new JpegFormat(), new PngFormat(), new GifFormat(), }); /// /// Initializes a new instance of the class. /// public Image() { this.HorizontalResolution = DefaultHorizontalResolution; this.VerticalResolution = DefaultVerticalResolution; this.CurrentImageFormat = DefaultFormats.Value.First(f => f.GetType() == typeof(PngFormat)); } /// /// Initializes a new instance of the class /// with the height and the width of the image. /// /// The width of the image in pixels. /// The height of the image in pixels. public Image(int width, int height) : base(width, height) { this.HorizontalResolution = DefaultHorizontalResolution; this.VerticalResolution = DefaultVerticalResolution; this.CurrentImageFormat = DefaultFormats.Value.First(f => f.GetType() == typeof(PngFormat)); } /// /// Initializes a new instance of the class /// by making a copy from another image. /// /// The other image, where the clone should be made from. /// is null. public Image(Image other) : base(other) { Guard.NotNull(other, nameof(other), "Other image cannot be null."); foreach (ImageFrame frame in other.Frames) { if (frame != null) { this.Frames.Add(new ImageFrame(frame)); } } this.HorizontalResolution = DefaultHorizontalResolution; this.VerticalResolution = DefaultVerticalResolution; this.CurrentImageFormat = other.CurrentImageFormat; } /// /// Initializes a new instance of the class. /// /// /// The stream containing image information. /// /// Thrown if the is null. public Image(Stream stream) { Guard.NotNull(stream, nameof(stream)); this.Load(stream, Formats); } /// /// Initializes a new instance of the class. /// /// /// The stream containing image information. /// /// /// The collection of . /// /// Thrown if the stream is null. public Image(Stream stream, params IImageFormat[] formats) { Guard.NotNull(stream, nameof(stream)); this.Load(stream, formats); } /// /// Gets a list of supported image formats. /// public static IList Formats => DefaultFormats.Value; /// public double HorizontalResolution { get; set; } /// public double VerticalResolution { get; set; } /// public double InchWidth { get { double resolution = this.HorizontalResolution; if (resolution <= 0) { resolution = DefaultHorizontalResolution; } return this.Width / resolution; } } /// public double InchHeight { get { double resolution = this.VerticalResolution; if (resolution <= 0) { resolution = DefaultVerticalResolution; } return this.Height / resolution; } } /// public bool IsAnimated => this.Frames.Count > 0; /// public ushort RepeatCount { get; set; } /// public IList Frames { get; } = new List(); /// public IList Properties { get; } = new List(); /// public IImageFormat CurrentImageFormat { get; internal 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 . /// /// /// Thrown if the stream is not readable nor seekable. /// private void Load(Stream stream, IList formats) { try { if (!stream.CanRead) { throw new NotSupportedException("Cannot read from the stream."); } if (!stream.CanSeek) { throw new NotSupportedException("The stream does not support seeking."); } if (formats.Count > 0) { int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize); if (maxHeaderSize > 0) { byte[] header = new byte[maxHeaderSize]; stream.Read(header, 0, maxHeaderSize); stream.Position = 0; IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header)); if (format != null) { format.Decoder.Decode(this, stream); this.CurrentImageFormat = format; return; } } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); foreach (IImageFormat format in formats) { stringBuilder.AppendLine("-" + format); } throw new NotSupportedException(stringBuilder.ToString()); } finally { stream.Dispose(); } } } }