// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
/// Represents a pixel-specific image frame containing all pixel data and .
/// In case of animated formats like gif, it contains the single frame in a animation.
/// In all other cases it is the only frame of the image.
///
/// The pixel format.
public sealed class ImageFrame : ImageFrame, IPixelSource
where TPixel : struct, IPixel
{
private bool isDisposed;
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
internal ImageFrame(Configuration configuration, int width, int height)
: this(configuration, width, height, new ImageFrameMetadata())
{
}
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The of the frame.
/// The metadata.
internal ImageFrame(Configuration configuration, Size size, ImageFrameMetadata metadata)
: this(configuration, size.Width, size.Height, metadata)
{
}
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata)
: base(configuration, width, height, metadata)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean);
}
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The color to clear the image with.
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor)
: this(configuration, width, height, backgroundColor, new ImageFrameMetadata())
{
}
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The color to clear the image with.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata)
: base(configuration, width, height, metadata)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(width, height);
this.Clear(backgroundColor);
}
///
/// Initializes a new instance of the class wrapping an existing buffer.
///
/// The configuration providing initialization code which allows extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The memory source.
internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource)
: this(configuration, width, height, memorySource, new ImageFrameMetadata())
{
}
///
/// Initializes a new instance of the class wrapping an existing buffer.
///
/// The configuration providing initialization code which allows extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The memory source.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetadata metadata)
: base(configuration, width, height, metadata)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = new Buffer2D(memorySource, width, height);
}
///
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
/// The source.
internal ImageFrame(Configuration configuration, ImageFrame source)
: base(configuration, source.Width, source.Height, source.Metadata.DeepClone())
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source));
this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan());
}
///
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
///
internal Buffer2D PixelBuffer { get; private set; }
///
Buffer2D IPixelSource.PixelBuffer => this.PixelBuffer;
///
/// Gets or sets the pixel at the specified position.
///
/// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.
/// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.
/// The at the specified position.
public TPixel this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.PixelBuffer[x, y];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => this.PixelBuffer[x, y] = value;
}
///
/// Gets a reference to the pixel at the specified position.
///
/// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.
/// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.
/// The at the specified position.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref TPixel GetPixelReference(int x, int y) => ref this.PixelBuffer[x, y];
///
/// Copies the pixels to a of the same size.
///
/// The target pixel buffer accessor.
internal void CopyTo(Buffer2D target)
{
if (this.Size() != target.Size())
{
throw new ArgumentException("ImageFrame.CopyTo(): target must be of the same size!", nameof(target));
}
this.GetPixelSpan().CopyTo(target.GetSpan());
}
///
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
///
/// The pixel source.
internal void SwapOrCopyPixelsBufferFrom(ImageFrame pixelSource)
{
Guard.NotNull(pixelSource, nameof(pixelSource));
Buffer2D.SwapOrCopyContent(this.PixelBuffer, pixelSource.PixelBuffer);
this.UpdateSize(this.PixelBuffer.Size());
}
///
protected override void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
this.PixelBuffer?.Dispose();
this.PixelBuffer = null;
}
this.isDisposed = true;
}
internal override void CopyPixelsTo(Span destination)
{
if (typeof(TPixel) == typeof(TDestinationPixel))
{
Span dest1 = MemoryMarshal.Cast(destination);
this.PixelBuffer.GetSpan().CopyTo(dest1);
}
PixelOperations.Instance.To(this.GetConfiguration(), this.PixelBuffer.GetSpan(), destination);
}
///
public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})";
///
/// Clones the current instance.
///
/// The
internal ImageFrame Clone() => this.Clone(this.GetConfiguration());
///
/// Clones the current instance.
///
/// The configuration providing initialization code which allows extending the library.
/// The
internal ImageFrame Clone(Configuration configuration) => new ImageFrame(configuration, this);
///
/// Returns a copy of the image frame in the given pixel format.
///
/// The pixel format.
/// The
internal ImageFrame CloneAs()
where TPixel2 : struct, IPixel => this.CloneAs(this.GetConfiguration());
///
/// Returns a copy of the image frame in the given pixel format.
///
/// The pixel format.
/// The configuration providing initialization code which allows extending the library.
/// The
internal ImageFrame CloneAs(Configuration configuration)
where TPixel2 : struct, IPixel
{
if (typeof(TPixel2) == typeof(TPixel))
{
return this.Clone(configuration) as ImageFrame;
}
var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone());
ParallelRowIterator.IterateRows2(
this.Bounds(),
configuration,
new RowAction(this, target, configuration));
return target;
}
///
/// Clears the bitmap.
///
/// The value to initialize the bitmap with.
internal void Clear(TPixel value)
{
Span span = this.GetPixelSpan();
if (value.Equals(default))
{
span.Clear();
}
else
{
span.Fill(value);
}
}
///
/// A implementing the clone logic for .
///
private readonly struct RowAction : IRowAction
where TPixel2 : struct, IPixel
{
private readonly ImageFrame source;
private readonly ImageFrame target;
private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)]
public RowAction(
ImageFrame source,
ImageFrame target,
Configuration configuration)
{
this.source = source;
this.target = target;
this.configuration = configuration;
}
///
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
Span sourceRow = this.source.GetPixelRowSpan(y);
Span targetRow = this.target.GetPixelRowSpan(y);
PixelOperations.Instance.To(this.configuration, sourceRow, targetRow);
}
}
}
}