using System.Buffers; using System.IO; using System.Runtime.InteropServices; namespace NetVips; /// /// An target you can connect delegates to implement behaviour. /// public class TargetCustom : Target { /// /// A write delegate. /// /// /// The interface is the same as . /// The handler is given a bytes-like object to write. However, the handler MUST /// return the number of bytes written. /// /// An array of bytes. /// The number of bytes to be written to the current target. /// The total number of bytes written to the target. public delegate long WriteDelegate(byte[] buffer, int length); /// /// A read delegate. /// /// /// libtiff needs to be able to read on targets, unfortunately. /// /// 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. /// /// /// libtiff needs to be able to seek on targets, unfortunately. /// /// 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 target. public delegate long SeekDelegate(long offset, SeekOrigin origin); /// /// A end delegate. /// /// /// This optional handler is called at the end of write. It should do any /// cleaning up, if necessary. /// /// 0 on success, -1 on error. public delegate int EndDelegate(); /// /// Attach a write delegate. /// public event WriteDelegate OnWrite; /// /// Attach a read delegate. /// /// /// This is not called prior libvips 8.13. /// public event ReadDelegate OnRead; /// /// Attach a seek delegate. /// /// /// This is not called prior libvips 8.13. /// public event SeekDelegate OnSeek; /// /// Attach a end delegate. /// public event EndDelegate OnEnd; /// public TargetCustom() : base(Internal.VipsTargetCustom.New()) { var vips813 = NetVips.AtLeastLibvips(8, 13); SignalConnect("write", (Internal.VipsTargetCustom.WriteSignal)WriteHandler); if (vips813) { SignalConnect("read", (Internal.VipsTargetCustom.ReadSignal)ReadHandler); SignalConnect("seek", (Internal.VipsTargetCustom.SeekSignal)SeekHandler); } SignalConnect(vips813 ? "end" : "finish", (Internal.VipsTargetCustom.EndSignal)EndHandler); } /// /// The internal write handler. /// /// The underlying pointer to the target. /// An array of bytes. /// The number of bytes to be written to the current target. /// User data associated with the target. /// The total number of bytes written to the target. internal long WriteHandler(nint targetPtr, byte[] buffer, int length, nint userDataPtr) { var bytesWritten = OnWrite?.Invoke(buffer, length); return bytesWritten ?? -1; } /// /// The internal read handler. /// /// The underlying pointer to the target. /// A pointer to an array of bytes. /// The maximum number of bytes to be read. /// User data associated with the target. /// The total number of bytes read into the buffer. internal long ReadHandler(nint targetPtr, 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 target. /// 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 target. /// The new position within the current target. internal long SeekHandler(nint targetPtr, long offset, int whence, nint userDataPtr) { var newPosition = OnSeek?.Invoke(offset, (SeekOrigin)whence); return newPosition ?? -1; } /// /// The internal end handler. /// /// The underlying pointer to the target. /// User data associated with the target. /// 0 on success, -1 on error. internal int EndHandler(nint targetPtr, nint userDataPtr) { return OnEnd?.Invoke() ?? 0; } }