|
@@ -49,7 +49,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
private object lockObject = new();
|
|
private object lockObject = new();
|
|
private int commitCounter = 0;
|
|
private int commitCounter = 0;
|
|
|
|
|
|
- public static int ChunkSize => ChunkPool.FullChunkSize;
|
|
|
|
|
|
+ public const int FullChunkSize = ChunkPool.FullChunkSize;
|
|
private static SKPaint ClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstIn };
|
|
private static SKPaint ClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstIn };
|
|
private static SKPaint InverseClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstOut };
|
|
private static SKPaint InverseClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstOut };
|
|
private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
|
|
private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
|
|
@@ -101,6 +101,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ChunkyImage output = new(LatestSize);
|
|
ChunkyImage output = new(LatestSize);
|
|
var chunks = FindCommittedChunks();
|
|
var chunks = FindCommittedChunks();
|
|
foreach (var chunk in chunks)
|
|
foreach (var chunk in chunks)
|
|
@@ -108,7 +109,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
var image = GetCommittedChunk(chunk, ChunkResolution.Full);
|
|
var image = GetCommittedChunk(chunk, ChunkResolution.Full);
|
|
if (image is null)
|
|
if (image is null)
|
|
continue;
|
|
continue;
|
|
- output.EnqueueDrawImage(chunk * ChunkSize, image.Surface);
|
|
|
|
|
|
+ output.EnqueueDrawImage(chunk * FullChunkSize, image.Surface);
|
|
}
|
|
}
|
|
output.CommitChanges();
|
|
output.CommitChanges();
|
|
return output;
|
|
return output;
|
|
@@ -122,6 +123,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
var latestChunk = GetLatestChunk(chunkPos, resolution);
|
|
var latestChunk = GetLatestChunk(chunkPos, resolution);
|
|
var committedChunk = GetCommittedChunk(chunkPos, resolution);
|
|
var committedChunk = GetCommittedChunk(chunkPos, resolution);
|
|
|
|
|
|
@@ -159,6 +161,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
if (MaybeGetLatestChunk(chunkPos, ChunkResolution.Full) is not null ||
|
|
if (MaybeGetLatestChunk(chunkPos, ChunkResolution.Full) is not null ||
|
|
MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null)
|
|
MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null)
|
|
return true;
|
|
return true;
|
|
@@ -175,6 +178,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
var chunk = GetCommittedChunk(chunkPos, resolution);
|
|
var chunk = GetCommittedChunk(chunkPos, resolution);
|
|
if (chunk is null)
|
|
if (chunk is null)
|
|
return false;
|
|
return false;
|
|
@@ -187,6 +191,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
return MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null;
|
|
return MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) is not null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -229,6 +234,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
if (queuedOperations.Count > 0)
|
|
if (queuedOperations.Count > 0)
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
activeClips.Add(clippingMask);
|
|
activeClips.Add(clippingMask);
|
|
@@ -242,6 +248,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
if (queuedOperations.Count > 0)
|
|
if (queuedOperations.Count > 0)
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
blendMode = mode;
|
|
blendMode = mode;
|
|
@@ -252,6 +259,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
if (queuedOperations.Count > 0)
|
|
if (queuedOperations.Count > 0)
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
horizontalSymmetryAxis = position;
|
|
horizontalSymmetryAxis = position;
|
|
@@ -262,6 +270,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
if (queuedOperations.Count > 0)
|
|
if (queuedOperations.Count > 0)
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
throw new InvalidOperationException("This function can only be executed when there are no queued operations");
|
|
verticalSymmetryAxis = position;
|
|
verticalSymmetryAxis = position;
|
|
@@ -272,6 +281,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
lockTransparency = true;
|
|
lockTransparency = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -280,6 +290,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
RectangleOperation operation = new(rect);
|
|
RectangleOperation operation = new(rect);
|
|
EnqueueOperation(operation);
|
|
EnqueueOperation(operation);
|
|
}
|
|
}
|
|
@@ -295,6 +306,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ImageOperation operation = new(corners, image, copyImage);
|
|
ImageOperation operation = new(corners, image, copyImage);
|
|
EnqueueOperation(operation);
|
|
EnqueueOperation(operation);
|
|
}
|
|
}
|
|
@@ -310,15 +322,27 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ImageOperation operation = new(pos, image, copyImage);
|
|
ImageOperation operation = new(pos, image, copyImage);
|
|
EnqueueOperation(operation);
|
|
EnqueueOperation(operation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public void EnqueueDrawChunkyImage(VecI pos, ChunkyImage image, bool flipHor = false, bool flipVer = false)
|
|
|
|
+ {
|
|
|
|
+ lock (lockObject)
|
|
|
|
+ {
|
|
|
|
+ ThrowIfDisposed();
|
|
|
|
+ ChunkyImageOperation operation = new(image, pos, flipHor, flipVer);
|
|
|
|
+ EnqueueOperation(operation);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
public void EnqueueClearRegion(VecI pos, VecI size)
|
|
public void EnqueueClearRegion(VecI pos, VecI size)
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ClearRegionOperation operation = new(pos, size);
|
|
ClearRegionOperation operation = new(pos, size);
|
|
EnqueueOperation(operation);
|
|
EnqueueOperation(operation);
|
|
}
|
|
}
|
|
@@ -328,6 +352,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ClearOperation operation = new();
|
|
ClearOperation operation = new();
|
|
EnqueueOperation(operation, FindAllChunks());
|
|
EnqueueOperation(operation, FindAllChunks());
|
|
}
|
|
}
|
|
@@ -337,6 +362,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
ResizeOperation operation = new(newSize);
|
|
ResizeOperation operation = new(newSize);
|
|
LatestSize = newSize;
|
|
LatestSize = newSize;
|
|
EnqueueOperation(operation, FindAllChunksOutsideBounds(newSize));
|
|
EnqueueOperation(operation, FindAllChunksOutsideBounds(newSize));
|
|
@@ -374,9 +400,10 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
//clear queued operations
|
|
//clear queued operations
|
|
foreach (var operation in queuedOperations)
|
|
foreach (var operation in queuedOperations)
|
|
- operation.Item1.Dispose();
|
|
|
|
|
|
+ operation.operation.Dispose();
|
|
queuedOperations.Clear();
|
|
queuedOperations.Clear();
|
|
|
|
|
|
//clear additional state
|
|
//clear additional state
|
|
@@ -407,6 +434,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
var affectedChunks = FindAffectedChunks();
|
|
var affectedChunks = FindAffectedChunks();
|
|
foreach (var chunk in affectedChunks)
|
|
foreach (var chunk in affectedChunks)
|
|
{
|
|
{
|
|
@@ -543,6 +571,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
var allChunks = committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
|
|
var allChunks = committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
|
|
foreach (var (_, opChunks) in queuedOperations)
|
|
foreach (var (_, opChunks) in queuedOperations)
|
|
{
|
|
{
|
|
@@ -556,6 +585,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
return committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
|
|
return committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -567,6 +597,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
var chunks = new HashSet<VecI>();
|
|
var chunks = new HashSet<VecI>();
|
|
foreach (var (_, opChunks) in queuedOperations)
|
|
foreach (var (_, opChunks) in queuedOperations)
|
|
{
|
|
{
|
|
@@ -642,7 +673,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
if (mask.CommittedChunkExists(chunkPos))
|
|
if (mask.CommittedChunkExists(chunkPos))
|
|
{
|
|
{
|
|
- mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.SkiaSurface, new(0, 0), ClippingPaint);
|
|
|
|
|
|
+ mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.SkiaSurface, VecI.Zero, ClippingPaint);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
@@ -686,14 +717,14 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
var clip = combinedClips.AsT2;
|
|
var clip = combinedClips.AsT2;
|
|
|
|
|
|
using var tempChunk = Chunk.Create(targetChunk.Resolution);
|
|
using var tempChunk = Chunk.Create(targetChunk.Resolution);
|
|
- targetChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ReplacingPaint);
|
|
|
|
|
|
+ targetChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, VecI.Zero, ReplacingPaint);
|
|
|
|
|
|
chunkOperation.DrawOnChunk(tempChunk, chunkPos);
|
|
chunkOperation.DrawOnChunk(tempChunk, chunkPos);
|
|
|
|
|
|
- clip.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
|
|
|
|
- clip.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), InverseClippingPaint);
|
|
|
|
|
|
+ clip.DrawOnSurface(tempChunk.Surface.SkiaSurface, VecI.Zero, ClippingPaint);
|
|
|
|
+ clip.DrawOnSurface(targetChunk.Surface.SkiaSurface, VecI.Zero, InverseClippingPaint);
|
|
|
|
|
|
- tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), AddingPaint);
|
|
|
|
|
|
+ tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, VecI.Zero, AddingPaint);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -712,6 +743,9 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|
|
{
|
|
{
|
|
|
|
+ ThrowIfDisposed();
|
|
|
|
+ if (queuedOperations.Count > 0)
|
|
|
|
+ throw new InvalidOperationException("This function can only be used when there are no queued operations");
|
|
FindAndDeleteEmptyCommittedChunks();
|
|
FindAndDeleteEmptyCommittedChunks();
|
|
return committedChunks[ChunkResolution.Full].Count == 0;
|
|
return committedChunks[ChunkResolution.Full].Count == 0;
|
|
}
|
|
}
|
|
@@ -726,7 +760,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
|
|
|
|
private static bool IsOutsideBounds(VecI chunkPos, VecI imageSize)
|
|
private static bool IsOutsideBounds(VecI chunkPos, VecI imageSize)
|
|
{
|
|
{
|
|
- return chunkPos.X < 0 || chunkPos.Y < 0 || chunkPos.X * ChunkSize >= imageSize.X || chunkPos.Y * ChunkSize >= imageSize.Y;
|
|
|
|
|
|
+ return chunkPos.X < 0 || chunkPos.Y < 0 || chunkPos.X * FullChunkSize >= imageSize.X || chunkPos.Y * FullChunkSize >= imageSize.Y;
|
|
}
|
|
}
|
|
|
|
|
|
private void FindAndDeleteEmptyCommittedChunks()
|
|
private void FindAndDeleteEmptyCommittedChunks()
|
|
@@ -836,6 +870,12 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
|
|
return newLatestChunk;
|
|
return newLatestChunk;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private void ThrowIfDisposed()
|
|
|
|
+ {
|
|
|
|
+ if (disposed)
|
|
|
|
+ throw new ObjectDisposedException(nameof(ChunkyImage));
|
|
|
|
+ }
|
|
|
|
+
|
|
public void Dispose()
|
|
public void Dispose()
|
|
{
|
|
{
|
|
lock (lockObject)
|
|
lock (lockObject)
|