using System.Buffers; using System.IO; using System.Runtime.InteropServices; namespace NetVips; /// /// An source you can connect delegates to implement behaviour. /// public class SourceCustom : Source { /// /// A read delegate. /// /// /// The interface is exactly as . /// The handler is given a number of bytes to fetch, and should return a /// bytes-like object containing up to that number of bytes. If there is /// no more data available, it should return . /// /// An array of bytes. /// The maximum number of bytes to be read. /// The total number of bytes read into the buffer. public delegate int ReadDelegate(byte[] buffer, int length); /// /// A seek delegate. /// /// /// The interface is exactly as . The handler is given /// parameters for offset and whence with the same meanings. It also returns the /// new position within the current source. /// /// Seek handlers are optional. If you do not set one, your source will be /// treated as unseekable and libvips will do extra caching. /// /// A byte offset relative to the /// parameter. /// A value of type indicating the /// reference point used to obtain the new position. /// The new position within the current source. public delegate long SeekDelegate(long offset, SeekOrigin origin); /// /// Attach a read delegate. /// public event ReadDelegate OnRead; /// /// Attach a seek delegate. /// public event SeekDelegate OnSeek; /// public SourceCustom() : base(Internal.VipsSourceCustom.New()) { SignalConnect("read", (Internal.VipsSourceCustom.ReadSignal)ReadHandler); SignalConnect("seek", (Internal.VipsSourceCustom.SeekSignal)SeekHandler); } /// /// The internal read handler. /// /// The underlying pointer to the source. /// A pointer to an array of bytes. /// The maximum number of bytes to be read. /// User data associated with the source. /// The total number of bytes read into the buffer. internal long ReadHandler(nint sourcePtr, nint buffer, long length, nint userDataPtr) { if (length <= 0) { return 0; } var tempArray = ArrayPool.Shared.Rent((int)length); try { var readLength = OnRead?.Invoke(tempArray, (int)length); if (!readLength.HasValue) { return -1; } if (readLength.Value > 0) { Marshal.Copy(tempArray, 0, buffer, readLength.Value); } return readLength.Value; } catch { return -1; } finally { ArrayPool.Shared.Return(tempArray); } } /// /// The internal seek handler. /// /// The underlying pointer to the source. /// A byte offset relative to the /// parameter. /// A value of type indicating the /// reference point used to obtain the new position. /// User data associated with the source. /// The new position within the current source. internal long SeekHandler(nint sourcePtr, long offset, int whence, nint userDataPtr) { var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence); return newPosition ?? -1; } }