// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessorCore { using System.IO; using System.Text; using System; using System.Collections.Generic; using System.Linq; using Formats; /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// /// The packed vector containing pixel information. /// public class Image : ImageBase where TPackedVector : IPackedVector { /// /// 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; /// /// Initializes a new instance of the class. /// public Image() { this.CurrentImageFormat = Bootstrapper.Instance.ImageFormats.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.CurrentImageFormat = Bootstrapper.Instance.ImageFormats.First(f => f.GetType() == typeof(PngFormat)); } /// /// 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); } /// /// 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) { 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; } /// /// Gets a list of supported image formats. /// public IReadOnlyCollection Formats { get; } = Bootstrapper.Instance.ImageFormats; /// /// 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; } = DefaultHorizontalResolution; /// /// 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; } = DefaultVerticalResolution; /// /// 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 { double resolution = this.HorizontalResolution; if (resolution <= 0) { resolution = DefaultHorizontalResolution; } return this.Width / resolution; } } /// /// 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 { double resolution = this.VerticalResolution; if (resolution <= 0) { resolution = DefaultVerticalResolution; } return this.Height / resolution; } } /// /// 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(); /// /// Gets the currently loaded image format. /// public IImageFormat CurrentImageFormat { get; internal set; } /// public override IPixelAccessor Lock() { return Bootstrapper.Instance.GetPixelAccessor(this); } /// /// 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. public void Save(Stream stream) { Guard.NotNull(stream, nameof(stream)); this.CurrentImageFormat.Encoder.Encode(this, stream); } /// /// Saves the image to the given stream using the given image format. /// /// The stream to save the image to. /// The format to save the image as. /// Thrown if the stream is null. public void Save(Stream stream, IImageFormat format) { Guard.NotNull(stream, nameof(stream)); format.Encoder.Encode(this, stream); } /// /// Saves the image to the given stream using the given image encoder. /// /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); encoder.Encode(this, stream); } /// /// Returns a Base64 encoded string from the given image. /// /// data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA== /// The public override string ToString() { using (MemoryStream stream = new MemoryStream()) { this.Save(stream); stream.Flush(); return $"data:{this.CurrentImageFormat.Encoder.MimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; } } /// /// Loads the image from the given stream. /// /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// private void Load(Stream stream) { if (!this.Formats.Any()) { return; } if (!stream.CanRead) { throw new NotSupportedException("Cannot read from the stream."); } if (!stream.CanSeek) { throw new NotSupportedException("The stream does not support seeking."); } int maxHeaderSize = this.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 = this.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 this.Formats) { stringBuilder.AppendLine("-" + format); } throw new NotSupportedException(stringBuilder.ToString()); } } }