// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessorCore { 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) { foreach (ImageFrame frame in other.Frames) { if (frame != null) { this.Frames.Add(new ImageFrame(frame)); } } this.RepeatCount = other.RepeatCount; this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; this.CurrentImageFormat = other.CurrentImageFormat; } /// /// Initializes a new instance of the class. /// /// /// The other to create this instance from. /// /// /// Thrown if the given is null. /// public Image(ImageFrame other) : base(other) { this.HorizontalResolution = DefaultHorizontalResolution; this.VerticalResolution = DefaultVerticalResolution; // Most likely a gif // TODO: Should this be aproperty on ImageFrame? this.CurrentImageFormat = DefaultFormats.Value.First(f => f.GetType() == typeof(GifFormat)); } /// /// 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); } /// public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); 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) { 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.Position = 0; 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()); } } }