using System; using System.Buffers; using System.Runtime.InteropServices; using System.Text; using NetVips.Internal; namespace NetVips; /// /// Useful extension methods that we use in our codebase. /// internal static class ExtensionMethods { /// /// Removes the element with the specified key from the /// and retrieves the value to . /// /// The to remove from. /// >The key of the element to remove. /// The target to retrieve the value to. /// if the element is successfully removed; otherwise, . internal static bool Remove(this VOption self, string key, out object target) { self.TryGetValue(key, out target); return self.Remove(key); } /// /// Merges 2 s. /// /// The to merge into. /// The to merge from. internal static void Merge(this VOption self, VOption merge) { foreach (var item in merge) { self[item.Key] = item.Value; } } /// /// Call a libvips operation. /// /// A used as guide. /// Operation name. /// A new object. internal static object Call(this Image image, string operationName) => Operation.Call(operationName, null, image); /// /// Call a libvips operation. /// /// A used as guide. /// Operation name. /// An arbitrary number and variety of arguments. /// A new object. internal static object Call(this Image image, string operationName, params object[] args) => Operation.Call(operationName, null, image, args); /// /// Call a libvips operation. /// /// A used as guide. /// Operation name. /// Optional arguments. /// A new object. internal static object Call(this Image image, string operationName, VOption kwargs) => Operation.Call(operationName, kwargs, image); /// /// Call a libvips operation. /// /// A used as guide. /// Operation name. /// Optional arguments. /// An arbitrary number and variety of arguments. /// A new object. internal static object Call(this Image image, string operationName, VOption kwargs, params object[] args) => Operation.Call(operationName, kwargs, image, args); /// /// Prepends to . /// /// The array. /// The to prepend to . /// A new object array. internal static object[] PrependImage(this T[] args, Image image) { if (args == null) { return new object[] { image }; } var newValues = new object[args.Length + 1]; newValues[0] = image; Array.Copy(args, 0, newValues, 1, args.Length); return newValues; } /// /// Marshals a GLib UTF8 char* to a managed string. /// /// Pointer to the GLib string. /// If set to , free the GLib string. /// Size of the GLib string, use 0 to read until the null character. /// The managed string. internal static string ToUtf8String(this nint utf8Str, bool freePtr = false, int size = 0) { if (utf8Str == IntPtr.Zero) { return null; } if (size == 0) { while (Marshal.ReadByte(utf8Str, size) != 0) { ++size; } } if (size == 0) { if (freePtr) { GLib.GFree(utf8Str); } return string.Empty; } var bytes = ArrayPool.Shared.Rent(size); try { Marshal.Copy(utf8Str, bytes, 0, size); return Encoding.UTF8.GetString(bytes, 0, size); } finally { ArrayPool.Shared.Return(bytes); if (freePtr) { GLib.GFree(utf8Str); } } } /// /// Convert bytes to human readable format. /// /// The number of bytes. /// The readable format of the bytes. internal static string ToReadableBytes(this ulong value) { string[] sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; var i = 0; decimal dValue = value; while (Math.Round(dValue, 2) >= 1000) { dValue /= 1024; i++; } return $"{dValue:n2} {sizeSuffixes[i]}"; } /// /// Negate all elements in an array. /// /// An array of doubles. /// The negated array. internal static double[] Negate(this double[] array) { for (var i = 0; i < array.Length; i++) { array[i] *= -1; } return array; } /// /// Negate all elements in an array. /// /// /// It will output an array of doubles instead of integers. /// /// An array of integers. /// The negated array. internal static double[] Negate(this int[] array) { var doubles = new double[array.Length]; for (var i = 0; i < array.Length; i++) { ref var value = ref doubles[i]; value = array[i] * -1; } return doubles; } /// /// Invert all elements in an array. /// /// An array of doubles. /// The inverted array. internal static double[] Invert(this double[] array) { for (var i = 0; i < array.Length; i++) { array[i] = 1.0 / array[i]; } return array; } /// /// Invert all elements in an array. /// /// /// It will output an array of doubles instead of integers. /// /// An array of integers. /// The inverted array. internal static double[] Invert(this int[] array) { var doubles = new double[array.Length]; for (var i = 0; i < array.Length; i++) { ref var value = ref doubles[i]; value = 1.0 / array[i]; } return doubles; } /// /// Compatibility method to call loaders with the enum. /// /// The optional arguments for the loader. /// The optional parameter. internal static void AddFailOn(this VOption options, Enums.FailOn? failOn = null) { if (!failOn.HasValue) { return; } if (NetVips.AtLeastLibvips(8, 12)) { options.Add("fail_on", failOn); } else { // The deprecated "fail" param was at the highest sensitivity (>= warning), // but for compat it would be more correct to set this to true only when // a non-permissive enum is given (> none). options.Add("fail", failOn > Enums.FailOn.None); } } /// /// Compatibility method to call savers with the enum. /// /// The optional arguments for the saver. /// The optional parameter. /// Whether this operation is -like. internal static void AddForeignKeep(this VOption options, Enums.ForeignKeep? keep = null, bool isDzsave = false) { if (!keep.HasValue) { return; } if (NetVips.AtLeastLibvips(8, 15)) { options.Add(nameof(keep), keep); } else if (isDzsave) { options.Add("no_strip", keep != Enums.ForeignKeep.None); } else { options.Add("strip", keep == Enums.ForeignKeep.None); } } }