using ChunkyImageLib.DataHolders;
using Drawie.Backend.Core;
using Drawie.Backend.Core.Numerics;
using Drawie.Backend.Core.Surfaces;
using Drawie.Backend.Core.Surfaces.PaintImpl;
using Drawie.Numerics;
namespace ChunkyImageLib;
public class Chunk : IDisposable
{
private static volatile int chunkCounter = 0;
///
/// The number of chunks that haven't yet been returned (includes garbage collected chunks).
/// Used in tests to make sure that all chunks are disposed.
///
public static int ChunkCounter => chunkCounter;
private bool returned = false;
///
/// The surface of the chunk
///
public Surface Surface
{
get
{
if (returned)
{
throw new ObjectDisposedException("Chunk has been disposed");
}
return internalSurface;
}
}
///
/// The size of the chunk
///
public VecI PixelSize { get; }
///
/// The resolution of the chunk
///
public ChunkResolution Resolution { get; }
public bool Disposed => returned;
private Surface internalSurface;
private Chunk(ChunkResolution resolution)
{
int size = resolution.PixelSize();
Resolution = resolution;
PixelSize = new(size, size);
internalSurface = new Surface(PixelSize);
}
///
/// Tries to take a chunk with the from the pool, or creates a new one
///
public static Chunk Create(ChunkResolution resolution = ChunkResolution.Full)
{
var chunk = ChunkPool.Instance.Get(resolution) ?? new Chunk(resolution);
chunk.returned = false;
Interlocked.Increment(ref chunkCounter);
return chunk;
}
///
/// Draw's on the of the chunk
///
/// The destination for the
/// The paint to use while drawing
public void DrawChunkOn(DrawingSurface surface, VecI pos, Paint? paint = null)
{
surface.Canvas.DrawSurface(Surface.DrawingSurface, pos.X, pos.Y, paint);
}
public unsafe RectI? FindPreciseBounds(RectI? passedSearchRegion = null)
{
RectI? bounds = null;
if (returned)
return bounds;
if (passedSearchRegion is not null && !new RectI(VecI.Zero, Surface.Size).ContainsInclusive(passedSearchRegion.Value))
throw new ArgumentException("Passed search region lies outside of the chunk's surface", nameof(passedSearchRegion));
RectI searchRegion = passedSearchRegion ?? new RectI(VecI.Zero, Surface.Size);
ulong* ptr = (ulong*)Surface.PixelBuffer;
for (int y = searchRegion.Top; y < searchRegion.Bottom; y++)
{
for (int x = searchRegion.Left; x < searchRegion.Right; x++)
{
int i = y * Surface.Size.X + x;
// ptr[i] actually contains 4 16-bit floats. We only care about the first one which is alpha.
// An empty pixel can have alpha of 0 or -0 (not sure if -0 actually ever comes up). 0 in hex is 0x0, -0 in hex is 0x8000
if ((ptr[i] & 0x1111_0000_0000_0000) != 0 && (ptr[i] & 0x1111_0000_0000_0000) != 0x8000_0000_0000_0000)
{
bounds ??= new RectI(x, y, 1, 1);
bounds = bounds.Value.Union(new RectI(x, y, 1, 1));
}
}
}
return bounds;
}
///
/// Returns the chunk back to the pool
///
public void Dispose()
{
if (returned)
return;
Interlocked.Decrement(ref chunkCounter);
Surface.DrawingSurface.Canvas.Clear();
ChunkPool.Instance.Push(this);
returned = true;
}
}