using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using Spectre.Console.Rendering; namespace Spectre.Console { /// /// Represents a renderable image. /// public sealed class CanvasImage : Renderable { private static readonly IResampler _defaultResampler = KnownResamplers.Bicubic; /// /// Gets the image width. /// public int Width => Image.Width; /// /// Gets the image height. /// public int Height => Image.Height; /// /// Gets or sets the render width of the canvas. /// public int? MaxWidth { get; set; } /// /// Gets or sets the render width of the canvas. /// public int PixelWidth { get; set; } = 2; /// /// Gets or sets the that should /// be used when scaling the image. Defaults to bicubic sampling. /// public IResampler? Resampler { get; set; } internal SixLabors.ImageSharp.Image Image { get; } /// /// Initializes a new instance of the class. /// /// The image filename. public CanvasImage(string filename) { Image = SixLabors.ImageSharp.Image.Load(filename); } /// /// Initializes a new instance of the class. /// /// Buffer containing an image. public CanvasImage(ReadOnlySpan data) { Image = SixLabors.ImageSharp.Image.Load(data); } /// /// Initializes a new instance of the class. /// /// Stream containing an image. public CanvasImage(Stream data) { Image = SixLabors.ImageSharp.Image.Load(data); } /// protected override Measurement Measure(RenderContext context, int maxWidth) { if (PixelWidth < 0) { throw new InvalidOperationException("Pixel width must be greater than zero."); } var width = MaxWidth ?? Width; if (maxWidth < width * PixelWidth) { return new Measurement(maxWidth, maxWidth); } return new Measurement(width * PixelWidth, width * PixelWidth); } /// protected override IEnumerable Render(RenderContext context, int maxWidth) { var image = Image; var width = Width; var height = Height; // Got a max width? if (MaxWidth != null) { height = (int)(height * ((float)MaxWidth.Value) / Width); width = MaxWidth.Value; } // Exceed the max width when we take pixel width into account? if (width * PixelWidth > maxWidth) { height = (int)(height * (maxWidth / (float)(width * PixelWidth))); width = maxWidth / PixelWidth; } // Need to rescale the pixel buffer? if (width != Width || height != Height) { var resampler = Resampler ?? _defaultResampler; image = image.Clone(); // Clone the original image image.Mutate(i => i.Resize(width, height, resampler)); } var canvas = new Canvas(width, height) { MaxWidth = MaxWidth, PixelWidth = PixelWidth, Scale = false, }; for (var y = 0; y < image.Height; y++) { for (var x = 0; x < image.Width; x++) { if (image[x, y].A == 0) { continue; } canvas.SetPixel(x, y, new Color( image[x, y].R, image[x, y].G, image[x, y].B)); } } return ((IRenderable)canvas).Render(context, maxWidth); } } }