Get rid of DisposableGCHandle.

Use Span functionality instead. This introduces some copies for .NET Standard 2.0 builds.
This commit is contained in:
Ray Koopa 2020-06-26 18:57:01 +02:00
parent b4cf309156
commit 57815e9072
5 changed files with 239 additions and 271 deletions

View File

@ -1,44 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Core
{
/// <summary>
/// Represents a disposable <see cref="GCHandle"/>.
/// </summary>
internal class DisposableGCHandle : IDisposable
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private readonly GCHandle _handle;
private bool _disposed;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
public DisposableGCHandle(object value, GCHandleType type) => _handle = GCHandle.Alloc(value, type);
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
public IntPtr AddrOfPinnedObject => _handle.AddrOfPinnedObject();
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void Dispose() => Dispose(true);
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_handle.IsAllocated)
_handle.Free();
}
_disposed = true;
}
}
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Core.Graphics
{
@ -20,16 +20,15 @@ namespace Syroot.Worms.Core.Graphics
/// <param name="palette">The palette as a <see cref="Color"/> array.</param>
/// <param name="data">The data array storing bytes indexing the palette color array.</param>
/// <returns>The <see cref="Bitmap"/> instance.</returns>
public static Bitmap CreateIndexed(Size size, IList<Color> palette, byte[] data)
public static unsafe Bitmap CreateIndexed(Size size, IList<Color> palette, byte[] data)
{
using DisposableGCHandle dataPin = new DisposableGCHandle(data, GCHandleType.Pinned);
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Span<byte> bitmapSpan = new Span<byte>(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
for (int y = 0; y < size.Height; y++)
Marshal.Copy(data, y * size.Width, bitmapData.Scan0 + y * bitmapData.Stride, size.Width);
data.AsSpan(y * size.Width, size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
bitmap.UnlockBits(bitmapData);
// Transfer the palette.

View File

@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Syroot.Worms.Core;
namespace Syroot.Worms
{
@ -39,16 +38,15 @@ namespace Syroot.Worms
/// Creates a <see cref="Bitmap"/> from the raw data.
/// </summary>
/// <returns>The <see cref="Bitmap"/> created from the raw data.</returns>
public Bitmap ToBitmap()
public unsafe Bitmap ToBitmap()
{
using DisposableGCHandle dataPin = new DisposableGCHandle(Data, GCHandleType.Pinned);
// Transfer the pixel data, respecting power-of-2 strides.
Bitmap bitmap = new Bitmap(Size.Width, Size.Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Span<byte> bitmapSpan = new Span<byte>(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
for (int y = 0; y < Size.Height; y++)
Marshal.Copy(Data, y * Size.Width, bitmapData.Scan0 + y * bitmapData.Stride, Size.Width);
Data.AsSpan(y * Size.Width, Size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
bitmap.UnlockBits(bitmapData);
// Transfer the palette.

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Syroot.BinaryData;
@ -84,12 +86,17 @@ namespace Syroot.Worms.Core.IO
public static T ReadStruct<T>(this BinaryStream self) where T : struct
{
// Read the raw bytes of the structure.
byte[] bytes = self.ReadBytes(Marshal.SizeOf<T>());
#if NETSTANDARD2_0
byte[] bytes = new byte[Unsafe.SizeOf<T>()];
self.Read(bytes, 0, bytes.Length);
#else
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<T>()];
self.Read(bytes);
#endif
// Convert them to a structure instance and return it.
using DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned);
T instance = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject);
return instance;
ReadOnlySpan<T> span = MemoryMarshal.Cast<byte, T>(bytes);
return span[0];
}
/// <summary>
@ -101,10 +108,18 @@ namespace Syroot.Worms.Core.IO
/// <returns>The structures of type <typeparamref name="T"/>.</returns>
public static T[] ReadStructs<T>(this BinaryStream self, int count) where T : struct
{
T[] values = new T[count];
for (int i = 0; i < count; i++)
values[i] = ReadStruct<T>(self);
return values;
// Read the raw bytes of the structures.
#if NETSTANDARD2_0
byte[] bytes = new byte[Unsafe.SizeOf<T>()];
self.Read(bytes, 0, bytes.Length);
#else
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<T>() * count];
self.Read(bytes);
#endif
// Convert them to a structure array and return it.
ReadOnlySpan<T> span = MemoryMarshal.Cast<byte, T>(bytes);
return span.ToArray();
}
/// <summary>
@ -186,14 +201,13 @@ namespace Syroot.Worms.Core.IO
/// <param name="value">The structure to write.</param>
public static void WriteStruct<T>(this BinaryStream self, T value) where T : struct
{
// Get the raw bytes of the structure instance.
byte[] bytes = new byte[Marshal.SizeOf<T>()];
using (DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned))
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject, false);
// Write the bytes to the stream.
self.WriteStructs(bytes);
ReadOnlySpan<byte> bytes;
unsafe { bytes = new ReadOnlySpan<byte>(Unsafe.AsPointer(ref value), Unsafe.SizeOf<T>()); }
#if NETSTANDARD2_0
self.Write(bytes.ToArray()); // cannot prevent copy in .NET Standard 2.0
#else
self.Write(bytes);
#endif
}
/// <summary>
@ -202,10 +216,10 @@ namespace Syroot.Worms.Core.IO
/// <typeparam name="T">The type of the structure to read.</typeparam>
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
/// <param name="values">The structures to write.</param>
public static void WriteStructs<T>(this BinaryStream self, IList<T> values) where T : struct
public static void WriteStructs<T>(this BinaryStream self, IEnumerable<T> values) where T : struct
{
foreach (T value in values)
WriteStruct(self, value);
self.WriteStruct(value);
}
}
}

View File

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)build.xml" />
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>Syroot.Worms</AssemblyName>
<Description>.NET library for loading and modifying files of Team17 Worms games.</Description>
<PackageReleaseNotes>Split into game-related packages.</PackageReleaseNotes>