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);
}
}
}