让本地代码对.NET托管对象进行操作
假设我想在我的自定义图形引擎中为TTF字体添加支持。我把目光投向了FreeType,这是在C 领域广泛使用、专为处理TTF字体而设计的库。
要在FreeType中加载字体,你需要调用 FT_Open_Face:
[LibraryImport("freetype")]
internal static partial int FT_Open_Face(nint library, FT_Open_Args* args,
CLong face_index, nint* aface);
FT_Open_Args 是一个C 结构体,其布局如下:
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FT_Open_Args
{
public uint flags;
public byte* memory_base;
public CLong memory_size;
public byte* pathname;
public FT_Stream* stream;
public nint driver;
public int num_params;
public FT_Parameter* params;
}
FT_Stream 进一步来看,它的样子如下:
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FT_Stream
{
public byte* base;
public CULong size;
public CULong pos;
public nint descriptor;
public nint pathname;
public delegate* unmanaged[Cdecl]<FT_Stream*, CULong,
byte*, CULong, CULong> read;
public delegate* unmanaged[Cdecl]<FT_Stream*, void> close;
public nint memory;
public nint cursor;
public nint limit;
}
我的图形引擎提供一种方法来访问已编译的游戏资源,该方法返回 System.IO.Stream。为了让FreeType在 C语言中使用自定义流,你需要把 descriptor 指向你的自定义流对象,然后把 read 和 close 指向你自己的读取回调和关闭回调。
那么,我该如何让FreeType使用.NET的 Stream 对象来加载我的字体呢?
解决方案
你可以将 GCHandle 传递给未托管的库,并在回调中检索它。
在从.NET流创建 FT_Stream 时,你需要对该流进行包装:
internal static unsafe FT_Stream* ToFTStream(this Stream stream)
{
// FreeType requires ftStream to be in unmanaged memory
FT_Stream* ftStream = NativeMemory.AllocZeroed((nuint)sizeof(FT_Stream));
GCHandle h = GCHandle.Alloc(stream);
ftStream->size = new CULong((nuint)stream.Length);
ftStream->descriptor = (nint)h;
ftStream->read = &_FTRead;
ftStream->close = &_FTClose;
return ftStream;
}
在FreeType使用的回调中,对它进行解封装:
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private unsafe static CULong _FTRead(FT_Stream* ftStream, CULong offset,
byte* buffer, CULong count)
{
GCHandle h = (GCHandle)(ftStream->descriptor);
Stream stream = (Stream)(h.Target)!;
// rest of your code...
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。