// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
///
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
///
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct GenericBlock8x8
where T : unmanaged
{
public const int Size = 64;
///
/// FOR TESTING ONLY!
/// Gets or sets a value at the given index
///
/// The index
/// The value
public T this[int idx]
{
get
{
ref T selfRef = ref Unsafe.As, T>(ref this);
return Unsafe.Add(ref selfRef, idx);
}
set
{
ref T selfRef = ref Unsafe.As, T>(ref this);
Unsafe.Add(ref selfRef, idx) = value;
}
}
///
/// FOR TESTING ONLY!
/// Gets or sets a value in a row+column of the 8x8 block
///
/// The x position index in the row
/// The column index
/// The value
public T this[int x, int y]
{
get => this[(y * 8) + x];
set => this[(y * 8) + x] = value;
}
///
/// Load a 8x8 region of an image into the block.
/// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image.
///
public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY, in RowOctet currentRows)
{
int width = Math.Min(8, source.Width - sourceX);
int height = Math.Min(8, source.Height - sourceY);
if (width <= 0 || height <= 0)
{
return;
}
uint byteWidth = (uint)width * (uint)Unsafe.SizeOf();
int remainderXCount = 8 - width;
ref byte blockStart = ref Unsafe.As, byte>(ref this);
int blockRowSizeInBytes = 8 * Unsafe.SizeOf();
for (int y = 0; y < height; y++)
{
Span row = currentRows[y];
ref byte s = ref Unsafe.As(ref row[sourceX]);
ref byte d = ref Unsafe.Add(ref blockStart, y * blockRowSizeInBytes);
Unsafe.CopyBlock(ref d, ref s, byteWidth);
ref T last = ref Unsafe.Add(ref Unsafe.As(ref d), width - 1);
for (int x = 1; x <= remainderXCount; x++)
{
Unsafe.Add(ref last, x) = last;
}
}
int remainderYCount = 8 - height;
if (remainderYCount == 0)
{
return;
}
ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * blockRowSizeInBytes);
for (int y = 1; y <= remainderYCount; y++)
{
ref byte remStart = ref Unsafe.Add(ref lastRowStart, blockRowSizeInBytes * y);
Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)blockRowSizeInBytes);
}
}
///
/// Only for on-stack instances!
///
public Span AsSpanUnsafe()
{
#if SUPPORTS_CREATESPAN
Span> s = MemoryMarshal.CreateSpan(ref this, 1);
return MemoryMarshal.Cast, T>(s);
#else
return new Span(Unsafe.AsPointer(ref this), Size);
#endif
}
}
}